diff --git a/.github/workflows/hocuspocus-docker.yml b/.github/workflows/hocuspocus-docker.yml index a4b21af5de6..ee34ed9ac98 100644 --- a/.github/workflows/hocuspocus-docker.yml +++ b/.github/workflows/hocuspocus-docker.yml @@ -112,7 +112,7 @@ jobs: - name: Generate GHA token id: generate-gha-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.DEPLOY_APP_ID }} private-key: ${{ secrets.DEPLOY_APP_PRIVATE_KEY }} @@ -136,7 +136,7 @@ jobs: - name: Deploy EDGE if: github.ref == 'refs/heads/dev' && github.repository == 'opf/openproject' - uses: benc-uk/workflow-dispatch@e2e5e9a103e331dad343f381a29e654aea3cf8fc + uses: benc-uk/workflow-dispatch@7a027648b88c2413826b6ddd6c76114894dc5ec4 with: workflow: edge-deploy-shards.yml repo: opf/saas-deploy @@ -147,7 +147,7 @@ jobs: - name: Deploy STAGE # make sure to always use the latest release branch here if: github.ref == 'refs/heads/release/17.1' && github.repository == 'opf/openproject' - uses: benc-uk/workflow-dispatch@e2e5e9a103e331dad343f381a29e654aea3cf8fc + uses: benc-uk/workflow-dispatch@7a027648b88c2413826b6ddd6c76114894dc5ec4 with: workflow: stage-deploy-shards.yml repo: opf/saas-deploy diff --git a/Gemfile b/Gemfile index 4de4e3ed1a2..cc6cdf9759e 100644 --- a/Gemfile +++ b/Gemfile @@ -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.4.0" +gem "mcp", "~> 0.7.0" gem "meta-tags", "~> 2.22.3" @@ -197,7 +197,7 @@ gem "puma", "~> 7.1" gem "puma-plugin-statsd", "~> 2.7" gem "rack-timeout", "~> 0.7.0", require: "rack/timeout/base" -gem "nokogiri", "~> 1.19.0" +gem "nokogiri", "~> 1.19.1" gem "carrierwave", "~> 1.3.4" gem "carrierwave_direct", "~> 2.1.0" @@ -207,7 +207,7 @@ gem "aws-sdk-core", "~> 3.241" # File upload via fog + screenshots on travis gem "aws-sdk-s3", "~> 1.213" -gem "openproject-token", "~> 8.6.0" +gem "openproject-token", "~> 8.7.0" gem "plaintext", "~> 0.3.7" @@ -273,7 +273,7 @@ group :test do gem "rack_session_access" gem "rspec", "~> 3.13.2" # also add to development group, so 'spec' rake task gets loaded - gem "rspec-rails", "~> 8.0.0", group: :development + gem "rspec-rails", "~> 8.0.3", group: :development # Retry failures within the same environment gem "retriable", "~> 3.1.1" diff --git a/Gemfile.lock b/Gemfile.lock index 84aaf76f4bb..1bde12d5c17 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -540,7 +540,7 @@ GEM activemodel equivalent-xml (0.6.0) nokogiri (>= 1.4.3) - erb (6.0.1) + erb (6.0.2) erb_lint (0.9.0) activesupport better_html (>= 2.0.1) @@ -743,7 +743,6 @@ GEM faraday-follow_redirects json-schema (4.3.1) addressable (>= 2.8) - json_rpc_handler (0.1.1) json_schemer (2.5.0) bigdecimal hana (~> 1.3) @@ -808,9 +807,8 @@ GEM marcel (1.1.0) markly (0.15.2) matrix (0.4.3) - mcp (0.4.0) + mcp (0.7.1) json-schema (>= 4.1) - json_rpc_handler (~> 0.1) messagebird-rest (5.0.0) jwt (< 4) meta-tags (2.22.3) @@ -823,7 +821,8 @@ GEM mini_magick (5.3.1) logger mini_mime (1.1.5) - minitest (6.0.1) + minitest (6.0.2) + drb (~> 2.0) prism (~> 1.5) msgpack (1.8.0) multi_json (1.19.1) @@ -846,21 +845,21 @@ GEM net-smtp (0.5.1) net-protocol nio4r (2.7.5) - nokogiri (1.19.0-aarch64-linux-gnu) + nokogiri (1.19.1-aarch64-linux-gnu) racc (~> 1.4) - nokogiri (1.19.0-aarch64-linux-musl) + nokogiri (1.19.1-aarch64-linux-musl) racc (~> 1.4) - nokogiri (1.19.0-arm-linux-gnu) + nokogiri (1.19.1-arm-linux-gnu) racc (~> 1.4) - nokogiri (1.19.0-arm-linux-musl) + nokogiri (1.19.1-arm-linux-musl) racc (~> 1.4) - nokogiri (1.19.0-arm64-darwin) + nokogiri (1.19.1-arm64-darwin) racc (~> 1.4) - nokogiri (1.19.0-x86_64-darwin) + nokogiri (1.19.1-x86_64-darwin) racc (~> 1.4) - nokogiri (1.19.0-x86_64-linux-gnu) + nokogiri (1.19.1-x86_64-linux-gnu) racc (~> 1.4) - nokogiri (1.19.0-x86_64-linux-musl) + nokogiri (1.19.1-x86_64-linux-musl) racc (~> 1.4) oj (3.16.15) bigdecimal (>= 3.0) @@ -894,7 +893,7 @@ GEM activesupport (>= 7.2.0) openproject-octicons (>= 19.30.1) view_component (>= 3.1, < 5.0) - openproject-token (8.6.0) + openproject-token (8.7.0) activemodel openssl (4.0.0) openssl-signature_algorithm (1.3.0) @@ -1184,7 +1183,7 @@ GEM puma (>= 5.0, < 8) raabro (1.4.0) racc (1.8.1) - rack (2.2.21) + rack (2.2.22) rack-attack (6.8.0) rack (>= 1.0, < 4) rack-cors (2.0.2) @@ -1234,8 +1233,8 @@ GEM activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.2) - loofah (~> 2.21) + rails-html-sanitizer (1.7.0) + loofah (~> 2.25) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) rails-i18n (8.1.0) i18n (>= 0.7, < 2) @@ -1303,7 +1302,7 @@ GEM rspec-mocks (3.13.7) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-rails (8.0.2) + rspec-rails (8.0.3) actionpack (>= 7.2) activesupport (>= 7.2) railties (>= 7.2) @@ -1533,7 +1532,7 @@ GEM yabeda (~> 0.8) yaml (0.4.0) yard (0.9.38) - zeitwerk (2.7.4) + zeitwerk (2.7.5) PLATFORMS aarch64-linux @@ -1636,14 +1635,14 @@ DEPENDENCIES mail (= 2.9.0) markly (~> 0.15) matrix (~> 0.4.3) - mcp (~> 0.4.0) + mcp (~> 0.7.0) md_to_pdf! meta-tags (~> 2.22.3) mini_magick (~> 5.3.0) multi_json (~> 1.19.0) my_page! net-ldap (~> 0.20.0) - nokogiri (~> 1.19.0) + nokogiri (~> 1.19.1) oj (~> 3.16.12) okcomputer (~> 1.19.1) omniauth! @@ -1672,7 +1671,7 @@ DEPENDENCIES openproject-reporting! openproject-storages! openproject-team_planner! - openproject-token (~> 8.6.0) + openproject-token (~> 8.7.0) openproject-two_factor_authentication! openproject-webhooks! openproject-xls_export! @@ -1714,7 +1713,7 @@ DEPENDENCIES roar (~> 1.2.0) rouge (~> 4.7.0) rspec (~> 3.13.2) - rspec-rails (~> 8.0.0) + rspec-rails (~> 8.0.3) rspec-retry (~> 0.6.1) rspec-wait rubocop @@ -1888,7 +1887,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.1) sha256=28ecdd99c5472aebd5674d6061e3c6b0a45c049578b071e5a52c2a7f13c197e5 + erb (6.0.2) sha256=9fe6264d44f79422c87490a1558479bd0e7dad4dd0e317656e67ea3077b5242b erb_lint (0.9.0) sha256=dfb5e40ad839e8d1f0d56ca85ec9a7ac4c9cd966ec281138282f35b323ca7c31 erblint-github (1.0.1) sha256=9f28f7dc381a0dc68a0093ef7af3424ed9d2bb2b3e39bdc8e8cba86a0d31f2d0 erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9 @@ -1970,7 +1969,6 @@ CHECKSUMS json (2.18.1) sha256=fe112755501b8d0466b5ada6cf50c8c3f41e897fa128ac5d263ec09eedc9f986 json-jwt (1.17.0) sha256=6ff99026b4c54281a9431179f76ceb81faa14772d710ef6169785199caadc4cc json-schema (4.3.1) sha256=d5e68dc32b94408d0b06ad04f9382ccbb6fe5a44910e066f8547f56c471a7825 - json_rpc_handler (0.1.1) sha256=ea248c8cb4d5490dde320db316ac5e3caf8137a20b5ff9035a4bfc1d19438d90 json_schemer (2.5.0) sha256=2f01fb4cce721a4e08dd068fc2030cffd0702a7f333f1ea2be6e8991f00ae396 json_spec (1.1.5) sha256=7a77b97a92c787e2aa3fbc4a1239afc3342c781151dc98cfb81461b3b7cad10f jwt (3.1.2) sha256=af6991f19a6bb4060d618d9add7a66f0eeb005ac0bc017cd01f63b42e122d535 @@ -1991,7 +1989,7 @@ CHECKSUMS marcel (1.1.0) sha256=fdcfcfa33cc52e93c4308d40e4090a5d4ea279e160a7f6af988260fa970e0bee markly (0.15.2) sha256=65dae965d4dd4ecd997fba43b93acc0fe7dadfec6f07a748640c7a9299a8551e matrix (0.4.3) sha256=a0d5ab7ddcc1973ff690ab361b67f359acbb16958d1dc072b8b956a286564c5b - mcp (0.4.0) sha256=4d1dd2b99fbd81a5fdc808d258c38a4f57dd69751ee1e5f62b3ab40e31625a36 + mcp (0.7.1) sha256=fa967895d6952bad0d981ea907731d8528d2c246d2079d56a9c8bae83d14f1c7 md_to_pdf (0.2.5) messagebird-rest (5.0.0) sha256=da4cc1efba3d5e4aa021fad07426c2cb6b326ce5670da5104bb8f6056a39d59c meta-tags (2.22.3) sha256=41ead5437140869717cbdd659cc6f1caa3e498b3e74b03ed63503b5b38ed504f @@ -2000,7 +1998,7 @@ CHECKSUMS mime-types-data (3.2026.0203) sha256=54353d693af028847391c28361c07d4b8b689cad78c3e1cc272fb1205c6d2a2f mini_magick (5.3.1) sha256=29395dfd76badcabb6403ee5aff6f681e867074f8f28ce08d78661e9e4a351c4 mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef - minitest (6.0.1) sha256=7854c74f48e2e975969062833adc4013f249a4b212f5e7b9d5c040bf838d54bb + minitest (6.0.2) sha256=db6e57956f6ecc6134683b4c87467d6dd792323c7f0eea7b93f66bd284adbc3d msgpack (1.8.0) sha256=e64ce0212000d016809f5048b48eb3a65ffb169db22238fb4b72472fecb2d732 multi_json (1.19.1) sha256=7aefeff8f2c854bf739931a238e4aea64592845e0c0395c8a7d2eea7fdd631b7 mustermann (3.0.4) sha256=85fadcb6b3c6493a8b511b42426f904b7f27b282835502233dd154daab13aa22 @@ -2013,14 +2011,14 @@ CHECKSUMS net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8 net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736 nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1 - nokogiri (1.19.0-aarch64-linux-gnu) sha256=11a97ecc3c0e7e5edcf395720b10860ef493b768f6aa80c539573530bc933767 - nokogiri (1.19.0-aarch64-linux-musl) sha256=eb70507f5e01bc23dad9b8dbec2b36ad0e61d227b42d292835020ff754fb7ba9 - nokogiri (1.19.0-arm-linux-gnu) sha256=572a259026b2c8b7c161fdb6469fa2d0edd2b61cd599db4bbda93289abefbfe5 - nokogiri (1.19.0-arm-linux-musl) sha256=23ed90922f1a38aed555d3de4d058e90850c731c5b756d191b3dc8055948e73c - nokogiri (1.19.0-arm64-darwin) sha256=0811dfd936d5f6dd3f6d32ef790568bf29b2b7bead9ba68866847b33c9cf5810 - nokogiri (1.19.0-x86_64-darwin) sha256=1dad56220b603a8edb9750cd95798bffa2b8dd9dd9aa47f664009ee5b43e3067 - nokogiri (1.19.0-x86_64-linux-gnu) sha256=f482b95c713d60031d48c44ce14562f8d2ce31e3a9e8dd0ccb131e9e5a68b58c - nokogiri (1.19.0-x86_64-linux-musl) sha256=1c4ca6b381622420073ce6043443af1d321e8ed93cc18b08e2666e5bd02ffae4 + nokogiri (1.19.1-aarch64-linux-gnu) sha256=cfdb0eafd9a554a88f12ebcc688d2b9005f9fce42b00b970e3dc199587b27f32 + nokogiri (1.19.1-aarch64-linux-musl) sha256=1e2150ab43c3b373aba76cd1190af7b9e92103564063e48c474f7600923620b5 + nokogiri (1.19.1-arm-linux-gnu) sha256=0a39ed59abe3bf279fab9dd4c6db6fe8af01af0608f6e1f08b8ffa4e5d407fa3 + nokogiri (1.19.1-arm-linux-musl) sha256=3a18e559ee499b064aac6562d98daab3d39ba6cbb4074a1542781b2f556db47d + nokogiri (1.19.1-arm64-darwin) sha256=dfe2d337e6700eac47290407c289d56bcf85805d128c1b5a6434ddb79731cb9e + nokogiri (1.19.1-x86_64-darwin) sha256=7093896778cc03efb74b85f915a775862730e887f2e58d6921e3fa3d981e68bf + nokogiri (1.19.1-x86_64-linux-gnu) sha256=1a4902842a186b4f901078e692d12257678e6133858d0566152fe29cdb98456a + nokogiri (1.19.1-x86_64-linux-musl) sha256=4267f38ad4fc7e52a2e7ee28ed494e8f9d8eb4f4b3320901d55981c7b995fc23 oj (3.16.15) sha256=4d3324cac3e8fef54c0fa250b2af26a16dadd9f9788a1d6b1b2098b793a1b2cd okcomputer (1.19.1) sha256=7df770e768434816d228407f0786563827cbf34cb379933578829720cb4f1e77 omniauth (1.9.2) @@ -2052,7 +2050,7 @@ CHECKSUMS openproject-reporting (1.0.0) openproject-storages (1.0.0) openproject-team_planner (1.0.0) - openproject-token (8.6.0) sha256=f5865070bd0586f618f476d22544cfa680916e3ff23aea8199a46e31b8a2970e + openproject-token (8.7.0) sha256=780c6fec77c4bc725049d78865cdc116459995c5ed0db08a4572044c2ee620a1 openproject-two_factor_authentication (1.0.0) openproject-webhooks (1.0.0) openproject-xls_export (1.0.0) @@ -2157,7 +2155,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.21) sha256=14e2f72f0765455fe424ff601588ac5ce84e95784f59e99251ffe1527152f739 + rack (2.2.22) sha256=c5cf0b7f872559966d974abe3101a57d51caf12504ee76290b98720004f64542 rack-attack (6.8.0) sha256=f2499fdebf85bcc05573a22dff57d24305ac14ec2e4156cd3c28d47cafeeecf2 rack-cors (2.0.2) sha256=415d4e1599891760c5dc9ef0349c7fecdf94f7c6a03e75b2e7c2b54b82adda1b rack-mini-profiler (4.0.1) sha256=485810c23211f908196c896ea10cad72ed68780ee2998bec1f1dfd7558263d78 @@ -2171,7 +2169,7 @@ CHECKSUMS rails (8.1.2) sha256=5069061b23dfa8706b9f0159ae8b9d35727359103178a26962b868a680ba7d95 rails-controller-testing (1.0.5) sha256=741448db59366073e86fc965ba403f881c636b79a2c39a48d0486f2607182e94 rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d - rails-html-sanitizer (1.6.2) sha256=35fce2ca8242da8775c83b6ba9c1bcaad6751d9eb73c1abaa8403475ab89a560 + rails-html-sanitizer (1.7.0) sha256=28b145cceaf9cc214a9874feaa183c3acba036c9592b19886e0e45efc62b1e89 rails-i18n (8.1.0) sha256=52d5fd6c0abef28d84223cc05647f6ae0fd552637a1ede92deee9545755b6cf3 railties (8.1.2) sha256=1289ece76b4f7668fc46d07e55cc992b5b8751f2ad85548b7da351b8c59f8055 rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a @@ -2202,7 +2200,7 @@ CHECKSUMS rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836 rspec-mocks (3.13.7) sha256=0979034e64b1d7a838aaaddf12bf065ea4dc40ef3d4c39f01f93ae2c66c62b1c - rspec-rails (8.0.2) sha256=113139a53f5d068d4f48d1c29ad5f982013ed9b0daa69d7f7b266eda5d433ace + rspec-rails (8.0.3) sha256=b0a440e7a10700317d898a014852e26660867298c4076dbc3baa99c768b79dc1 rspec-retry (0.6.2) sha256=6101ba23a38809811ae3484acde4ab481c54d846ac66d5037ccb40131a60d858 rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c rspec-wait (1.0.2) sha256=865f921239325d3d26fc10ded4bdd485d8b58bcaaad1a28dd85ed15266b5a912 @@ -2300,7 +2298,7 @@ CHECKSUMS yabeda-rails (0.11.0) sha256=afa2581bd44c8f419cb3f2bbf9f6fb40f817c30476f7caf5d1c55c48d69a5b29 yaml (0.4.0) sha256=240e69d1e6ce3584d6085978719a0faa6218ae426e034d8f9b02fb54d3471942 yard (0.9.38) sha256=721fb82afb10532aa49860655f6cc2eaa7130889df291b052e1e6b268283010f - zeitwerk (2.7.4) sha256=2bef90f356bdafe9a6c2bd32bcd804f83a4f9b8bc27f3600fff051eb3edcec8b + zeitwerk (2.7.5) sha256=d8da92128c09ea6ec62c949011b00ed4a20242b255293dd66bf41545398f73dd RUBY VERSION ruby 3.4.7p58 diff --git a/app/components/admin/custom_fields/edit_form_header_component.rb b/app/components/admin/custom_fields/edit_form_header_component.rb index 00987bf65c2..46c9ac4fd79 100644 --- a/app/components/admin/custom_fields/edit_form_header_component.rb +++ b/app/components/admin/custom_fields/edit_form_header_component.rb @@ -52,6 +52,12 @@ module Admin path: custom_field_items_path(@custom_field), label: t(:label_item_plural) } + elsif @custom_field.list? + tabs << { + name: "items", + path: list_items_custom_field_path(@custom_field), + label: t(:label_item_plural) + } end if @custom_field.is_a?(WorkPackageCustomField) || diff --git a/app/components/custom_fields/details_component.rb b/app/components/custom_fields/details_component.rb index a81ad052f37..e2d4f0272ee 100644 --- a/app/components/custom_fields/details_component.rb +++ b/app/components/custom_fields/details_component.rb @@ -41,16 +41,14 @@ module CustomFields "weighted_item_list" => { key: :weighted_item_lists, image: "enterprise/weighted_item_lists.png" } }.freeze - class << self - def supported?(custom_field) - custom_field.field_format.in?(%w[bool calculated_value hierarchy weighted_item_list]) - end - end - alias_method :custom_field, :model def form_url - model.new_record? ? custom_fields_path : custom_field_path(model) + if model.new_record? + model.type == "ProjectCustomField" ? admin_settings_project_custom_fields_path : custom_fields_path + else + model.type == "ProjectCustomField" ? admin_settings_project_custom_field_path(model) : custom_field_path(model) + end end def form_method @@ -71,7 +69,7 @@ module CustomFields def show_top_banner? case custom_field.field_format - when "hierarchy", "weighted_item_list" + when "hierarchy", "weighted_item_list", "list" persisted_cf_has_no_items_or_projects? else false @@ -80,12 +78,20 @@ module CustomFields def top_banner_text case custom_field.field_format - when "hierarchy", "weighted_item_list" + when "hierarchy", "weighted_item_list", "list" I18n.t("custom_fields.admin.notice.remember_items_and_projects") end end def persisted_cf_has_no_items_or_projects? + if custom_field.list? && custom_field.custom_options.empty? + if custom_field.respond_to?(:projects) + custom_field.projects.empty? + end + + true + end + custom_field.persisted? && custom_field.hierarchical_list? && custom_field.hierarchy_root.children.empty? && diff --git a/app/components/settings/project_custom_field_sections/show_component.html.erb b/app/components/settings/project_custom_field_sections/show_component.html.erb index feda29d90c6..6ce3bf4f969 100644 --- a/app/components/settings/project_custom_field_sections/show_component.html.erb +++ b/app/components/settings/project_custom_field_sections/show_component.html.erb @@ -15,36 +15,34 @@ end section_header_container.with_column(flex_layout: true, justify_content: :flex_end) do |actions_container| - if OpenProject::FeatureDecisions.new_project_overview_active? - actions_container.with_column do - render(Primer::Alpha::ActionMenu.new(select_variant: :single, size: :small, test_selector: "section-position-selector")) do |menu| - menu.with_show_button( - mr: 2, - aria: { label: t("settings.project_attributes.sections.display_representation.overview.label") } - ) do |button| - button.with_trailing_visual_icon(icon: :"triangle-down") - button.with_leading_visual_icon(icon: display_representation_icon_for_section(@project_custom_field_section)) - display_representation_label_for_section(@project_custom_field_section) - end + actions_container.with_column do + render(Primer::Alpha::ActionMenu.new(select_variant: :single, size: :small, test_selector: "section-position-selector")) do |menu| + menu.with_show_button( + mr: 2, + aria: { label: t("settings.project_attributes.sections.display_representation.overview.label") } + ) do |button| + button.with_trailing_visual_icon(icon: :"triangle-down") + button.with_leading_visual_icon(icon: display_representation_icon_for_section(@project_custom_field_section)) + display_representation_label_for_section(@project_custom_field_section) + end - menu.with_item( - label: t("settings.project_attributes.sections.display_representation.overview.side_panel.label"), - active: @project_custom_field_section.shown_in_overview_sidebar?, - test_selector: "section-position-selector--side-panel-option", - **menu_item_options_for(@project_custom_field_section, ProjectCustomFieldSection::OVERVIEW__SIDEBAR_KEY) - ) do |item| - item.with_leading_visual_icon(icon: :"op-view-split") - item.with_description.with_content(t("settings.project_attributes.sections.display_representation.overview.side_panel.description")) - end - menu.with_item( - label: t("settings.project_attributes.sections.display_representation.overview.main_area.label"), - active: @project_custom_field_section.shown_in_overview_main_area?, - test_selector: "section-position-selector--main-section-option", - **menu_item_options_for(@project_custom_field_section, ProjectCustomFieldSection::OVERVIEW__MAIN_AREA_KEY) - ) do |item| - item.with_leading_visual_icon(icon: :"op-view-cards") - item.with_description.with_content(t("settings.project_attributes.sections.display_representation.overview.main_area.description")) - end + menu.with_item( + label: t("settings.project_attributes.sections.display_representation.overview.side_panel.label"), + active: @project_custom_field_section.shown_in_overview_sidebar?, + test_selector: "section-position-selector--side-panel-option", + **menu_item_options_for(@project_custom_field_section, ProjectCustomFieldSection::OVERVIEW__SIDEBAR_KEY) + ) do |item| + item.with_leading_visual_icon(icon: :"op-view-split") + item.with_description.with_content(t("settings.project_attributes.sections.display_representation.overview.side_panel.description")) + end + menu.with_item( + label: t("settings.project_attributes.sections.display_representation.overview.main_area.label"), + active: @project_custom_field_section.shown_in_overview_main_area?, + test_selector: "section-position-selector--main-section-option", + **menu_item_options_for(@project_custom_field_section, ProjectCustomFieldSection::OVERVIEW__MAIN_AREA_KEY) + ) do |item| + item.with_leading_visual_icon(icon: :"op-view-cards") + item.with_description.with_content(t("settings.project_attributes.sections.display_representation.overview.main_area.description")) end end end @@ -96,7 +94,7 @@ OpenProject::CustomFieldFormat.available_for_class_name("Project") .sort_by(&:name) .map do |format| - action_menu_item_for_custom_field_format(menu, format) + action_menu_item_for_custom_field_format(menu, format) end end end diff --git a/app/components/settings/project_custom_fields/edit_form_header_component.rb b/app/components/settings/project_custom_fields/edit_form_header_component.rb index 4f52c6e58d1..3a2bda6d415 100644 --- a/app/components/settings/project_custom_fields/edit_form_header_component.rb +++ b/app/components/settings/project_custom_fields/edit_form_header_component.rb @@ -51,6 +51,12 @@ module Settings path: admin_settings_project_custom_field_items_path(@custom_field), label: t(:label_item_plural) } + elsif @custom_field.list? + tabs << { + name: "items", + path: list_items_admin_settings_project_custom_field_path(@custom_field), + label: t(:label_item_plural) + } end if @custom_field.user? diff --git a/app/components/table_component.html.erb b/app/components/table_component.html.erb index f3125367435..9e1c00c35b1 100644 --- a/app/components/table_component.html.erb +++ b/app/components/table_component.html.erb @@ -29,7 +29,7 @@ See COPYRIGHT and LICENSE files for more details.
- + <%= render(Primer::BaseComponent.new(**@system_arguments)) do %> <% headers.each do |_name, _options| %> @@ -67,7 +67,7 @@ See COPYRIGHT and LICENSE files for more details. <% end %> <%= render_collection rows %> -
+ <% end %> <% if inline_create_link %>
<%= inline_create_link %> diff --git a/app/components/table_component.rb b/app/components/table_component.rb index 369f8a9c69c..efe7f01ebd8 100644 --- a/app/components/table_component.rb +++ b/app/components/table_component.rb @@ -31,8 +31,22 @@ ## # Abstract view component. Subclass this for a concrete table. class TableComponent < ApplicationComponent - def initialize(rows: [], **) + include Primer::AttributesHelper + + def initialize(rows: [], table_arguments: {}, **) super(rows, **) + + @system_arguments = table_arguments + @system_arguments[:tag] = :table + @system_arguments[:classes] = class_names( + @system_arguments[:classes], + "generic-table" + ) + @system_arguments[:data] ||= {} + @system_arguments[:data] = merge_data( + @system_arguments, + data: { controller: "table-highlighting" } + ) end class << self diff --git a/app/controllers/admin/settings/project_custom_fields_controller.rb b/app/controllers/admin/settings/project_custom_fields_controller.rb index 4e5c6ac031d..75c3460e77e 100644 --- a/app/controllers/admin/settings/project_custom_fields_controller.rb +++ b/app/controllers/admin/settings/project_custom_fields_controller.rb @@ -43,7 +43,7 @@ module Admin::Settings before_action :find_custom_field, only: %i(show edit project_mappings new_link link unlink update destroy delete_option reorder_alphabetical move drop role_assignment update_role_assignment role_assignment_preview_dialog - attribute_help_text update_attribute_help_text) + attribute_help_text update_attribute_help_text list_items) before_action :prepare_custom_option_position, only: %i(update create) before_action :find_custom_option, only: :delete_option before_action :project_custom_field_mappings_query, only: %i[project_mappings unlink] @@ -74,6 +74,8 @@ module Admin::Settings def edit; end + def list_items; end + def project_mappings; end def role_assignment; end diff --git a/app/controllers/concerns/custom_fields/shared_actions.rb b/app/controllers/concerns/custom_fields/shared_actions.rb index 625a1e7e369..30d4a7c5e0c 100644 --- a/app/controllers/concerns/custom_fields/shared_actions.rb +++ b/app/controllers/concerns/custom_fields/shared_actions.rb @@ -49,6 +49,14 @@ module CustomFields end end + def list_item_path(custom_field, params = {}) + if custom_field.type == "ProjectCustomField" + list_items_admin_settings_project_custom_field_path(**params) + else + list_items_custom_field_path(**params) + end + end + def create # rubocop:disable Metrics/AbcSize call = ::CustomFields::CreateService .new(user: current_user) @@ -66,10 +74,14 @@ module CustomFields end def update - perform_update(get_custom_field_params) + if custom_options_attributes + perform_update(get_custom_field_params, tab: :list_items) + else + perform_update(get_custom_field_params) + end end - def perform_update(custom_field_params) + def perform_update(custom_field_params, tab: :edit) call = ::CustomFields::UpdateService .new(user: current_user, model: @custom_field) .call(custom_field_params) @@ -77,7 +89,13 @@ module CustomFields if call.success? flash[:notice] = t(:notice_successful_update) call_hook(:controller_custom_fields_edit_after_save, custom_field: @custom_field) - redirect_back_or_default(edit_path(@custom_field, id: @custom_field.id)) + path = if tab == :list_items + list_item_path(@custom_field, + id: @custom_field.id) + else + edit_path(@custom_field, id: @custom_field.id) + end + redirect_to(path) else render action: :edit, status: :unprocessable_entity end @@ -89,10 +107,10 @@ module CustomFields .sort_by(&:value) .each_with_index .map do |custom_option, index| - { id: custom_option.id, position: index + 1 } + { id: custom_option.id, position: index + 1 } end - perform_update(custom_options_attributes: reordered_options) + perform_update({ custom_options_attributes: reordered_options }, tab: :list_items) end def destroy @@ -115,7 +133,7 @@ module CustomFields flash[:error] = @custom_option.errors.full_messages end - redirect_to edit_path(@custom_field, id: @custom_field.id), status: :see_other + redirect_to list_item_path(@custom_field, id: @custom_field.id), status: :see_other end def new_custom_field @@ -139,14 +157,20 @@ module CustomFields end def prepare_custom_option_position - return unless params[:custom_field][:custom_options_attributes] + return unless custom_options_attributes index = 0 - params[:custom_field][:custom_options_attributes].each_value do |attributes| + custom_options_attributes.each_value do |attributes| attributes[:position] = (index = index + 1) end end + + def custom_options_attributes + return unless params[:custom_field] + + params[:custom_field][:custom_options_attributes] + end end end end diff --git a/app/controllers/custom_fields_controller.rb b/app/controllers/custom_fields_controller.rb index 37180bfc507..70bc7741c7d 100644 --- a/app/controllers/custom_fields_controller.rb +++ b/app/controllers/custom_fields_controller.rb @@ -31,11 +31,14 @@ class CustomFieldsController < ApplicationController include CustomFields::SharedActions # share logic with ProjectCustomFieldsControlller include CustomFields::AttributeHelpTextActions + layout "admin" # rubocop:disable Rails/LexicallyScopedActionFilter before_action :require_admin - before_action :find_custom_field, only: %i(edit update destroy delete_option reorder_alphabetical attribute_help_text update_attribute_help_text) + before_action :find_custom_field, + only: %i(edit update destroy delete_option reorder_alphabetical attribute_help_text update_attribute_help_text + list_items) before_action :prepare_custom_option_position, only: %i(update create) before_action :find_custom_option, only: :delete_option before_action :validate_enterprise_token, only: %i(create) @@ -67,6 +70,8 @@ class CustomFieldsController < ApplicationController render_attribute_help_text_form end + def list_items; end + def update_attribute_help_text update_help_text end diff --git a/app/controllers/work_packages/activities_tab_controller.rb b/app/controllers/work_packages/activities_tab_controller.rb index 5d92a77a695..d585e4d4379 100644 --- a/app/controllers/work_packages/activities_tab_controller.rb +++ b/app/controllers/work_packages/activities_tab_controller.rb @@ -224,11 +224,19 @@ class WorkPackages::ActivitiesTabController < ApplicationController status: :not_found ) end + # turbo_stream requests (tab is already rendered and an error occured in subsequent requests) are handled below + format.turbo_stream do + @turbo_status = :not_found + render_error_flash_message_via_turbo_stream(message: error_message) + render turbo_stream: turbo_streams, status: :not_found + end end end def find_journal - @journal = Journal + @journal = @work_package + .journals + .internal_visible .with_sequence_version .find(params[:id]) rescue ActiveRecord::RecordNotFound diff --git a/app/forms/custom_fields/details_form.rb b/app/forms/custom_fields/details_form.rb index 44d17c05556..eb37d53f2ee 100644 --- a/app/forms/custom_fields/details_form.rb +++ b/app/forms/custom_fields/details_form.rb @@ -60,11 +60,30 @@ module CustomFields end end - if show_multi_value_field? - details_form.check_box( - name: :multi_value, - label: label(:multi_value), - caption: instructions(:multi_select) + if show_min_max_field? + details_form.text_field( + name: :min_length, + type: :number, + label: label(:min_length), + caption: instructions(:min_max), + input_width: :small + ) + + details_form.text_field( + name: :max_length, + type: :number, + label: label(:max_length), + caption: instructions(:min_max), + input_width: :small + ) + end + + if show_regex_field? + details_form.text_field( + name: :regexp, + label: label(:regexp), + caption: instructions(:regexp), + input_width: :medium ) end @@ -86,6 +105,37 @@ module CustomFields ) end + if show_default_text_field? + details_form.text_field( + name: :default_value, + label: label(:default_value), + input_width: :medium + ) + end + + if show_default_rich_text_field? + details_form.rich_text_area( + name: :default_value, + label: label(:default_value), + rich_text_options: { resource: nil, macros: :none } + ) + end + + if show_multi_value_field? + details_form.check_box( + name: :multi_value, + label: label(:multi_value), + caption: instructions(:multi_select) + ) + end + + if show_non_open_versions_field? + details_form.check_box( + name: :allow_non_open_versions, + label: label(:allow_non_open_versions) + ) + end + if show_is_required_field? details_form.check_box( name: :is_required, @@ -110,6 +160,21 @@ module CustomFields ) end + if show_is_searchable_field? + details_form.check_box( + name: :searchable, + label: label(:searchable), + caption: instructions(:searchable) + ) + end + + if show_right_to_left_field? + details_form.check_box( + name: :content_right_to_left, + label: label(:content_right_to_left) + ) + end + if show_admin_only_field? details_form.check_box( name: :admin_only, @@ -151,10 +216,30 @@ module CustomFields %w[bool].include?(model.field_format) end + def show_default_text_field? + %w[list bool date text user version hierarchy weighted_item_list calculated_value].exclude?(model.field_format) + end + + def show_default_rich_text_field? + %w[text].include?(model.field_format) + end + def show_is_required_field? %w[calculated_value bool].exclude?(model.field_format) end + def show_min_max_field? + %w[list bool date user version link hierarchy weighted_item_list calculated_value].exclude?(model.field_format) + end + + def show_regex_field? + %w[list bool date user version hierarchy weighted_item_list calculated_value].exclude?(model.field_format) + end + + def show_right_to_left_field? + model.is_a?(WorkPackageCustomField) && %w[text].include?(model.field_format) + end + def show_multi_value_field? model.multi_value_possible? end @@ -171,6 +256,15 @@ module CustomFields model.is_a?(WorkPackageCustomField) end + def show_is_searchable_field? + (model.is_a?(WorkPackageCustomField) || model.is_a?(ProjectCustomField)) && + %w[bool date float int user version hierarchy weighted_item_list calculated_value].exclude?(model.field_format) + end + + def show_non_open_versions_field? + %w[version].include?(model.field_format) && model.allow_non_open_versions_possible? + end + def show_admin_only_field? model.is_a?(ProjectCustomField) || model.is_a?(UserCustomField) end diff --git a/app/forms/groups/form.rb b/app/forms/groups/form.rb new file mode 100644 index 00000000000..c63fd9f4b24 --- /dev/null +++ b/app/forms/groups/form.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +# -- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +# ++ +module Groups + class Form < ApplicationForm + include CustomFields::CustomFieldRendering + + form do |f| + f.text_field( + name: :lastname, + label: Group.human_attribute_name(:name), + required: true, + input_width: :medium, + autocomplete: "off" + ) + + render_custom_fields(form: f) + + f.submit( + name: :submit, + label: submit_label, + scheme: :primary + ) + end + + def initialize(submit_label: I18n.t(:button_save)) + super() + @submit_label = submit_label + end + + private + + attr_reader :submit_label + + def custom_fields + model.available_custom_fields + end + end +end diff --git a/app/forms/versions/form.rb b/app/forms/versions/form.rb new file mode 100644 index 00000000000..a1b17760550 --- /dev/null +++ b/app/forms/versions/form.rb @@ -0,0 +1,205 @@ +# 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 Versions + class Form < ApplicationForm + include CustomFields::CustomFieldRendering + include VersionsHelper + include WikiHelper + + form do |f| + f.text_field( + name: :name, + label: attribute_name(:name), + required: true, + input_width: :large, + autocomplete: :off + ) + + f.text_field( + name: :description, + label: attribute_name(:description), + input_width: :large + ) + + f.select_list( + name: :status, + label: attribute_name(:status), + input_width: :xsmall + ) do |list| + contract.assignable_statuses.each do |s| + list.option( + label: I18n.t("version_status_#{s}"), + value: s, + selected: version.status == s + ) + end + end + + f.select_list( + name: :wiki_page_title, + label: I18n.t(:label_wiki_page), + include_blank: true, + disabled: wiki_pages_disabled?, + input_width: :large + ) do |list| + wiki_page_options_for_select( + contract.assignable_wiki_pages.includes(:parent), + placeholder: false, + ids: false + ).each do |label, value| + list.option( + label:, + value:, + selected: version.wiki_page_title == value + ) + end + end + + f.single_date_picker( + name: :start_date, + label: attribute_name(:start_date), + input_width: :xsmall, + leading_visual: { icon: :calendar } + ) + + f.single_date_picker( + name: :effective_date, + label: attribute_name(:effective_date), + input_width: :xsmall, + leading_visual: { icon: :calendar } + ) + + f.select_list( + name: :sharing, + label: attribute_name(:sharing), + input_width: :small + ) do |list| + contract.assignable_sharings.each do |v| + list.option( + label: format_version_sharing(v), + value: v, + selected: version.sharing == v + ) + 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 + + render_custom_fields(form: f) + + f.submit( + name: :submit, + label: submit_label, + scheme: :primary + ) + end + + def initialize(project: nil, submit_label: I18n.t(:button_save)) + super() + @project = project + @submit_label = submit_label + end + + private + + attr_reader :submit_label, :project + + def version + model + end + + def contract + @contract ||= if version.new_record? + Versions::CreateContract.new(version, User.current) + else + Versions::UpdateContract.new(version, User.current) + end + end + + def custom_fields + version.available_custom_fields + end + + def wiki_pages_disabled? + contract.assignable_wiki_pages.none? + end + + def backlogs_enabled? + resolved_project.backlogs_enabled? + 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/application_helper.rb b/app/helpers/application_helper.rb index 2358957522e..13d7784cf30 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -293,6 +293,7 @@ module ApplicationHelper controller: "application auto-theme-switcher hover-card-trigger beforeunload external-links highlight-target-element", relative_url_root: root_path, overflowing_identifier: ".__overflowing_body", + external_links_enabled_value: Setting.capture_external_links?, rendered_at: Time.zone.now.iso8601, turbo: local_assigns[:turbo_opt_out] ? "false" : nil }.merge(user_theme_data_attributes) diff --git a/app/helpers/versions_helper.rb b/app/helpers/versions_helper.rb index 0c94655b050..bbe9750bc07 100644 --- a/app/helpers/versions_helper.rb +++ b/app/helpers/versions_helper.rb @@ -36,7 +36,7 @@ module VersionsHelper if grouped.size > 1 grouped_options_for_select(grouped, selected&.id) else - options_for_select((grouped.values.first || []), selected&.id) + options_for_select(grouped.values.first || [], selected&.id) end end @@ -68,17 +68,9 @@ module VersionsHelper h(version.to_s_for_project(project)) end - def version_contract(version) - if version.new_record? - Versions::CreateContract.new(version, User.current) - else - Versions::UpdateContract.new(version, User.current) - end - end - def format_version_sharing(sharing) sharing = "none" unless Version::VERSION_SHARINGS.include?(sharing) - t("label_version_sharing_#{sharing}") + I18n.t("label_version_sharing_#{sharing}") end def versions_by_project(versions) diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb index 0403484fa91..2a46a596149 100644 --- a/app/helpers/wiki_helper.rb +++ b/app/helpers/wiki_helper.rb @@ -100,6 +100,6 @@ module WikiHelper def wiki_page_option(page, level, ids) indent = level.positive? ? "#{"\u00A0" * level * 2}» " : "" id = ids ? page.id : page.title - [indent + h(page.title), id] + [indent + page.title, id] end end diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb index 55b4d5b62e7..9ed29fbb74c 100644 --- a/app/models/custom_field.rb +++ b/app/models/custom_field.rb @@ -61,9 +61,6 @@ class CustomField < ApplicationRecord acts_as_list scope: [:type] validates :field_format, presence: true - validates :custom_options, - presence: { message: ->(*) { I18n.t(:"activerecord.errors.models.custom_field.at_least_one_custom_option") } }, - if: ->(*) { field_format == "list" } validates :name, presence: true, length: { maximum: 256 }, diff --git a/app/views/admin/settings/project_custom_fields/edit.html.erb b/app/views/admin/settings/project_custom_fields/edit.html.erb index 644f966d3a0..404b332535b 100644 --- a/app/views/admin/settings/project_custom_fields/edit.html.erb +++ b/app/views/admin/settings/project_custom_fields/edit.html.erb @@ -38,22 +38,4 @@ See COPYRIGHT and LICENSE files for more details. ) %> -<% if CustomFields::DetailsComponent.supported?(@custom_field) %> - <%= render CustomFields::DetailsComponent.new(@custom_field) %> -<% else %> - <%= error_messages_for "custom_field" %> - - <% content_controller "admin--custom-fields", - "admin--custom-fields-format-value": @custom_field.field_format, - "admin--custom-fields-format-config-value": OpenProject::CustomFieldFormatDependent.stimulus_config %> - - <%= labelled_tabular_form_for @custom_field, as: :custom_field, - url: admin_settings_project_custom_field_path(@custom_field), - html: { method: :put, id: "custom_field_form" } do |f| %> - <%= render partial: "custom_fields/form", locals: { f: f, custom_field: @custom_field } %> - <% if @custom_field.new_record? %> - <%= hidden_field_tag "type", @custom_field.type %> - <% end %> - <%= styled_button_tag t(:button_save), class: "-highlight -with-icon icon-checkmark" %> - <% end %> -<% end %> +<%= render CustomFields::DetailsComponent.new(@custom_field) %> diff --git a/app/views/admin/settings/project_custom_fields/list_items.html.erb b/app/views/admin/settings/project_custom_fields/list_items.html.erb new file mode 100644 index 00000000000..6247d928387 --- /dev/null +++ b/app/views/admin/settings/project_custom_fields/list_items.html.erb @@ -0,0 +1,76 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + +<% html_title t(:label_administration), t("settings.project_attributes.heading"), @custom_field.name %> + +<%= + render( + Settings::ProjectCustomFields::EditFormHeaderComponent.new( + custom_field: @custom_field, + selected: :project_custom_field_edit + ) + ) +%> + +<%= settings_primer_form_with( + model: @custom_field, + scope: :custom_field, + url: admin_settings_project_custom_field_path(@custom_field), + html: { method: :put, id: "custom_field_form" } + ) do |f| %> + <%= render partial: "custom_fields/custom_options", locals: { custom_field: @custom_field, f: f } %> + + <%= + flex_layout(mt: 4) do |flex| + flex.with_column do + render Primer::Beta::Button.new( + scheme: :secondary, + tag: :a, + href: reorder_alphabetical_admin_settings_project_custom_field_path(@custom_field), + data: { + turbo_method: :post, + turbo_confirm: t("custom_fields.reorder_confirmation") + }, + mr: 2 + ) do + t("custom_fields.reorder_alphabetical") + end + end + flex.with_column do + render Primer::Beta::Button.new( + scheme: :primary, + type: :submit + ) do |button| + button.with_leading_visual_icon(icon: :check) + t(:button_save) + end + end + end + %> +<% end %> diff --git a/app/views/admin/settings/project_custom_fields/new.html.erb b/app/views/admin/settings/project_custom_fields/new.html.erb index d36d93794f2..9719b1cced0 100644 --- a/app/views/admin/settings/project_custom_fields/new.html.erb +++ b/app/views/admin/settings/project_custom_fields/new.html.erb @@ -31,22 +31,4 @@ See COPYRIGHT and LICENSE files for more details. <%= render(Settings::ProjectCustomFields::NewFormHeaderComponent.new(@custom_field)) %> -<% if CustomFields::DetailsComponent.supported?(@custom_field) %> - <%= render CustomFields::DetailsComponent.new(@custom_field) %> -<% else %> - <%= error_messages_for "custom_field" %> - - <% content_controller "admin--custom-fields", - "admin--custom-fields-format-value": @custom_field.field_format, - "admin--custom-fields-format-config-value": OpenProject::CustomFieldFormatDependent.stimulus_config %> - - <%= labelled_tabular_form_for @custom_field, as: :custom_field, - url: admin_settings_project_custom_fields_path, - html: { id: "custom_field_form" } do |f| %> - <%= render partial: "custom_fields/form", locals: { f: f, custom_field: @custom_field } %> - <% if @custom_field.new_record? %> - <%= hidden_field_tag "type", @custom_field.type %> - <% end %> - <%= styled_button_tag t(:button_save), class: "-highlight -with-icon icon-checkmark" %> - <% end %> -<% end %> +<%= render CustomFields::DetailsComponent.new(@custom_field) %> diff --git a/app/views/custom_fields/_custom_options.html.erb b/app/views/custom_fields/_custom_options.html.erb index 4684985f9b5..fa89235fbe6 100644 --- a/app/views/custom_fields/_custom_options.html.erb +++ b/app/views/custom_fields/_custom_options.html.erb @@ -26,8 +26,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. See COPYRIGHT and LICENSE files for more details. ++#%> +<% content_controller "admin--custom-fields", + "admin--custom-fields-multi-select-value": @custom_field.multi_value? %> -<% custom_field = f.object %> <% custom_field.custom_options.build if custom_field.custom_options.empty? %>
@@ -86,27 +87,25 @@ See COPYRIGHT and LICENSE files for more details. <%= co_f.hidden_field :id, - disabled: true, class: "custom-option-id" %> - <%= co_f.text_field :value, - disabled: true, - container_class: "custom-option-value", - no_label: true %> + + <%= co_f.text_field :value, + no_label: true %> + - + <%= co_f.check_box :default_value, - disabled: true, container_class: "custom-option-default-value", data: { "admin--custom-fields-target": "customOptionDefaults", - action: "admin--custom-fields#uncheckOtherDefaults" + action: "click->admin--custom-fields#uncheckOtherDefaults" }, no_label: true %> <%= op_icon("icon-context icon-arrow-down2 icon-small") %>
+ +<%= + render Primer::Beta::Button.new( + scheme: :link, + test_selector: "add-custom-option", + data: { action: "admin--custom-fields#addOption" }, + mt: 2 + ) do |button| + button.with_leading_visual_icon(icon: :plus) + t(:button_add) + end +%> diff --git a/app/views/custom_fields/_form.html.erb b/app/views/custom_fields/_form.html.erb deleted file mode 100644 index bab5ef3c4a8..00000000000 --- a/app/views/custom_fields/_form.html.erb +++ /dev/null @@ -1,251 +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. - -++# %> - -<% format_dependent = OpenProject::CustomFieldFormatDependent.new(@custom_field.field_format) %> - -
- - <%= f.hidden_field :field_format if @custom_field.new_record? %> - -
- <%= f.text_field :name, - required: true, - container_class: "-middle", - "data-test-selector": "op-custom-fields--new-custom-field-name" %> -
- <% if @custom_field.type == 'ProjectCustomField' %> -
- <%= f.select :custom_field_section_id, - ProjectCustomFieldSection.all.collect { |s| [s.name, s.id] }, - { container_class: "-slim" }, - required: true %> -
- <% end %> - -
> -
- <%= t(:label_min_max_length) %>
- (<%= t(:text_min_max_length_info) %>) -
-
-
- <%= f.number_field :min_length, - container_class: "-xslim" %> -
-
- <%= f.number_field :max_length, - container_class: "-xslim" %> -
-
-
- -
> - <%= f.text_field :regexp, - size: 50, - container_class: "-wide" %> - - <%= t(:text_regexp_info) %> - -
- - <% if @custom_field.new_record? || @custom_field.multi_value_possible? %> -
> - <%= f.check_box :multi_value, - data: { action: "admin--custom-fields#checkOnlyOne" } %> -
-

- <% if @custom_field.is_a?(ProjectCustomField) %> - <%= t("custom_fields.instructions.multi_select.project") %> - <% else %> - <%= t("custom_fields.instructions.multi_select.all") %> - <% end %> -

-
-
- -
> - <%= I18n.t("activerecord.attributes.custom_field.possible_values") %> - <% if @custom_field.persisted? %> -
- - <%= link_to t("custom_fields.reorder_alphabetical"), - { action: :reorder_alphabetical }, - data: { - turbo_method: :post, - turbo_confirm: t("custom_fields.reorder_confirmation") - } %> - -
- <% end %> -
- <%= render partial: "custom_fields/custom_options", locals: { custom_field: @custom_field, f: f } %> - <%= - render Primer::Beta::Button.new( - scheme: :link, - test_selector: "add-custom-option", - data: { action: "admin--custom-fields#addOption" }, - mt: 2 - ) do |button| - button.with_leading_visual_icon(icon: :plus) - t(:button_add) - end - %> -
-
- <% end %> - - <% if @custom_field.new_record? || @custom_field.allow_non_open_versions_possible? %> -
> - <%= f.check_box :allow_non_open_versions %> -
- <% end %> - - <% if @custom_field.new_record? || !%w[text bool].include?(@custom_field.field_format) %> -
> - <%= f.text_field :default_value, - id: "custom_fields_default_value_text", - for: "custom_fields_default_value_text", - container_class: "-wide" %> -
- <% end %> - <% if @custom_field.new_record? || @custom_field.field_format == 'bool' %> -
> - <%= f.check_box :default_value, - id: "custom_fields_default_value_bool", - for: "custom_fields_default_value_bool" %> -
- <% end %> - <% if @custom_field.new_record? || @custom_field.field_format == 'text' %> -
> - <%= f.text_area :default_value, - id: "custom_fields_default_value_longtext", - for: "custom_fields_default_value_longtext", - cols: 100, - rows: 20, - class: "wiki-edit", - macros: "none", - with_text_formatting: true %> -
- <% end %> - <%= call_hook(:view_custom_fields_form_upper_box, custom_field: @custom_field, form: f) %> -
- -
- <% case @custom_field.class.name %> - <% when "WorkPackageCustomField" %> -
- <%= f.check_box :is_required %> -
-

<%= t("custom_fields.instructions.is_required.all") %>

-
-
-
- <%= f.check_box :is_for_all %> -
-

<%= t("custom_fields.instructions.is_for_all.all") %>

-
-
-
- <%= f.check_box :is_filter %> -
-

<%= t("custom_fields.instructions.is_filter.all") %>

-
-
-
> - <%= f.check_box :searchable %> -
-

<%= t("custom_fields.instructions.searchable.all") %>

-
-
-
> - <%= f.check_box :content_right_to_left %> -
- <% when "UserCustomField" %> -
- <%= f.check_box :is_required %> -
-

<%= t("custom_fields.instructions.is_required.all") %>

-
-
-
- <%= f.check_box :admin_only %> -
-

<%= t("custom_fields.instructions.admin_only.all") %>

-
-
-
- <%= f.check_box :editable %> -
-

<%= t("custom_fields.instructions.editable.all") %>

-
-
- <% when "ProjectCustomField" %> -
- <%= f.check_box :is_required %> -
-

<%= t("custom_fields.instructions.is_required.project") %>

-
-
-
- <%= f.check_box :is_for_all %> -
-

<%= t("custom_fields.instructions.is_for_all.project") %>

-
-
-
- <%= f.check_box :admin_only %> -
-

<%= t("custom_fields.instructions.admin_only.project") %>

-
-
-
> - <%= f.check_box :searchable %> -
-

<%= t("custom_fields.instructions.searchable.project") %>

-
-
- <% when "TimeEntryCustomField" %> -
- <%= f.check_box :is_required %> -
-

<%= t("custom_fields.instructions.is_required.all") %>

-
-
- <% else %> -
- <%= f.check_box :is_required %> -
-

<%= t("custom_fields.instructions.is_required.all") %>

-
-
- <% end %> - <%= call_hook(:"view_custom_fields_form_#{@custom_field.type.to_s.underscore}", custom_field: @custom_field, form: f) %> -
diff --git a/app/views/custom_fields/edit.html.erb b/app/views/custom_fields/edit.html.erb index 937e22cd26d..70fbff4a008 100644 --- a/app/views/custom_fields/edit.html.erb +++ b/app/views/custom_fields/edit.html.erb @@ -31,20 +31,4 @@ See COPYRIGHT and LICENSE files for more details. <%= render(Admin::CustomFields::EditFormHeaderComponent.new(custom_field: @custom_field, selected: :edit)) %> -<% if CustomFields::DetailsComponent.supported?(@custom_field) %> - <%= render CustomFields::DetailsComponent.new(@custom_field) %> -<% else %> - <%= error_messages_for "custom_field" %> - - <% content_controller "admin--custom-fields", - "admin--custom-fields-format-config-value": OpenProject::CustomFieldFormatDependent.stimulus_config, - "admin--custom-fields-format-value": @custom_field.field_format %> - - <%= labelled_tabular_form_for @custom_field, - as: :custom_field, - url: custom_field_path(@custom_field), - html: { method: :put, id: "custom_field_form" } do |f| %> - <%= render partial: "form", locals: { f: f } %> - <%= styled_button_tag t(:button_save), class: "-primary -with-icon icon-checkmark" %> - <% end %> -<% end %> +<%= render CustomFields::DetailsComponent.new(@custom_field) %> diff --git a/app/views/custom_fields/list_items.html.erb b/app/views/custom_fields/list_items.html.erb new file mode 100644 index 00000000000..14c831b6eb0 --- /dev/null +++ b/app/views/custom_fields/list_items.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. + +++#%> + +<% html_title t(:label_administration), "#{CustomField.model_name.human} #{h @custom_field.name}", t(:label_item_plural) %> + +<%= render(Admin::CustomFields::EditFormHeaderComponent.new(custom_field: @custom_field, selected: :items)) %> + +<%= settings_primer_form_with( + model: @custom_field, + scope: :custom_field, + url: custom_field_path(@custom_field), + html: { id: "custom_field_form" } + ) do |f| %> + <%= render partial: "custom_fields/custom_options", locals: { custom_field: @custom_field, f: f } %> + + <%= + flex_layout(mt: 4) do |flex| + flex.with_column do + render Primer::Beta::Button.new( + scheme: :secondary, + tag: :a, + href: reorder_alphabetical_custom_field_path(@custom_field), + data: { + turbo_method: :post, + turbo_confirm: t("custom_fields.reorder_confirmation") + }, + mr: 2 + ) do + t("custom_fields.reorder_alphabetical") + end + end + flex.with_column do + render Primer::Beta::Button.new( + scheme: :primary, + type: :submit + ) do |button| + button.with_leading_visual_icon(icon: :check) + t(:button_save) + end + end + end + %> +<% end %> diff --git a/app/views/custom_fields/new.html.erb b/app/views/custom_fields/new.html.erb index 47f84a9c93d..e8e3048b24a 100644 --- a/app/views/custom_fields/new.html.erb +++ b/app/views/custom_fields/new.html.erb @@ -45,26 +45,4 @@ See COPYRIGHT and LICENSE files for more details. end %> -<% if CustomFields::DetailsComponent.supported?(@custom_field) %> - <%= render CustomFields::DetailsComponent.new(@custom_field) %> -<% else %> - <%= error_messages_for "custom_field" %> - - <% content_controller "admin--custom-fields", - "admin--custom-fields-format-config-value": OpenProject::CustomFieldFormatDependent.stimulus_config, - "admin--custom-fields-format-value": @custom_field.field_format, - "admin--custom-fields-hierarchy-enabled-value": EnterpriseToken.allows_to?(:custom_field_hierarchies) %> - - <%= labelled_tabular_form_for @custom_field, - as: :custom_field, - url: custom_fields_path, - html: { id: "custom_field_form", class: "-wide-labels" } do |f| %> - <%= render partial: "form", locals: { f: f } %> - <%= hidden_field_tag "type", @custom_field.type %> - <%= styled_button_tag t(:button_save), - class: "-primary -with-icon icon-checkmark", - data: { - "admin--custom-fields-target": "submitButton" - } %> - <% end %> -<% end %> +<%= render CustomFields::DetailsComponent.new(@custom_field) %> diff --git a/app/views/groups/_general.html.erb b/app/views/groups/_general.html.erb index d6387e18b5e..30252135ca5 100644 --- a/app/views/groups/_general.html.erb +++ b/app/views/groups/_general.html.erb @@ -27,7 +27,8 @@ See COPYRIGHT and LICENSE files for more details. ++#%> -<%= labelled_tabular_form_for @group, url: group_path(@group), html: { method: :put }, as: :group do |f| %> - <%= render partial: "form", locals: { f: f } %> - <%= styled_button_tag t(:button_save), class: "-primary -with-icon icon-checkmark" %> -<% end %> +<%= + settings_primer_form_with(model: @group, url: group_path(@group), method: :put) do |f| + render Groups::Form.new(f) + end +%> diff --git a/app/views/groups/new.html.erb b/app/views/groups/new.html.erb index e0faf978cb0..62d3cc360de 100644 --- a/app/views/groups/new.html.erb +++ b/app/views/groups/new.html.erb @@ -41,7 +41,10 @@ See COPYRIGHT and LICENSE files for more details. end %> -<%= labelled_tabular_form_for(@group) do |f| %> - <%= render partial: "form", locals: { f: f } %> -
<%= f.button t(:button_create), class: "button -primary -with-icon icon-checkmark" %>
-<% end %> +<%= error_messages_for @group %> + +<%= + settings_primer_form_with(model: @group) do |f| + render Groups::Form.new(f, submit_label: I18n.t(:button_create)) + end +%> diff --git a/app/views/versions/_form.html.erb b/app/views/versions/_form.html.erb deleted file mode 100644 index 278aaf67178..00000000000 --- a/app/views/versions/_form.html.erb +++ /dev/null @@ -1,81 +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. - -++#%> - -<% - # locals: f, project, errors - version = f.object - contract = version_contract(version) -%> - -<%= error_messages_for version %> -<%= back_url_hidden_field_tag %> - -
- <%= f.text_field :name, required: true, container_class: "-wide" %> -
- -
- <%= f.text_field :description, container_class: "-wide" %> -
- -
- <%= f.select :status, - contract.assignable_statuses.map { |s| [t("version_status_#{s}"), s] }, - container_class: "-slim" %> -
- -
- <%= f.select :wiki_page_title, - wiki_page_options_for_select(contract.assignable_wiki_pages.includes(:parent), placeholder: false, ids: false), - { label: :label_wiki_page, include_blank: true }, - { container_class: "-wide", disabled: contract.assignable_wiki_pages.none? } %> -
- -
- <%= f.date_picker :start_date %> -
- -
- <%= f.date_picker :effective_date %> -
- -
- <%= f.select :sharing, - contract.assignable_sharings.map { |v| [format_version_sharing(v), v] }, - container_class: "-middle" %> -
- -<% if project.enabled_modules.map(&:name).include?("backlogs") %> - - <%= version_settings_fields(version, project) %> - -<% end %> - -<%= render partial: "customizable/form", - locals: { form: f, all_fields: true, only_required: false } %> diff --git a/app/views/versions/edit.html.erb b/app/views/versions/edit.html.erb index 0c3bbba6650..ea046daf95b 100644 --- a/app/views/versions/edit.html.erb +++ b/app/views/versions/edit.html.erb @@ -41,10 +41,7 @@ See COPYRIGHT and LICENSE files for more details. end %> -<%= labelled_tabular_form_for @version do |f| %> - - <%= render partial: "form", locals: { f: f, - project: @project } %> - - <%= styled_button_tag t(:button_save), class: "-primary -with-icon icon-checkmark" %> +<%= settings_primer_form_with(model: @version, url: version_path(@version), method: :patch) do |f| %> + <%= back_url_hidden_field_tag %> + <%= render Versions::Form.new(f, project: @project) %> <% end %> diff --git a/app/views/versions/new.html.erb b/app/views/versions/new.html.erb index 140aa735cba..ceccdd93f40 100644 --- a/app/views/versions/new.html.erb +++ b/app/views/versions/new.html.erb @@ -40,10 +40,7 @@ See COPYRIGHT and LICENSE files for more details. end %> -<%= labelled_tabular_form_for [@project, @version] do |f| %> - - <%= render partial: "versions/form", locals: { f: f, - project: @project } %> - - <%= styled_button_tag t(:button_create), class: "-primary -with-icon icon-checkmark" %> +<%= settings_primer_form_with(model: [@project, @version]) do |f| %> + <%= back_url_hidden_field_tag %> + <%= render Versions::Form.new(f, project: @project, submit_label: I18n.t(:button_create)) %> <% end %> diff --git a/config/initializers/feature_decisions.rb b/config/initializers/feature_decisions.rb index 7ad70f1725c..30aa767af54 100644 --- a/config/initializers/feature_decisions.rb +++ b/config/initializers/feature_decisions.rb @@ -60,10 +60,6 @@ OpenProject::FeatureDecisions.add :portfolio_models, description: "Enables the creation and management of portfolio and program work spaces.", force_active: true -OpenProject::FeatureDecisions.add :new_project_overview, - description: "Enables the new project overview experience.", - force_active: true - OpenProject::FeatureDecisions.add :jira_import, description: "Enables Jira Migration Tool.", force_active: false diff --git a/config/initializers/menus.rb b/config/initializers/menus.rb index 988d878612d..4033af892c9 100644 --- a/config/initializers/menus.rb +++ b/config/initializers/menus.rb @@ -480,7 +480,7 @@ Redmine::MenuManager.map :admin_menu do |menu| { controller: "/admin/mcp_configurations", action: :index }, if: ->(_) { User.current.admin? && OpenProject::FeatureDecisions.mcp_server_active? }, caption: I18n.t("menus.admin.ai"), - icon: :"sparkle-fill" + icon: :sparkle menu.push :mcp_configurations, { controller: "/admin/mcp_configurations", action: :index }, diff --git a/config/locales/crowdin/af.yml b/config/locales/crowdin/af.yml index c4cdcbc341a..3f492be6b8a 100644 --- a/config/locales/crowdin/af.yml +++ b/config/locales/crowdin/af.yml @@ -371,7 +371,7 @@ af: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ af: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4813,7 +4819,6 @@ af: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4828,7 +4833,6 @@ af: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'Die regex is toegepas in ''n multi-lyn manier. bv. ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/ar.yml b/config/locales/crowdin/ar.yml index 08a456554e5..2af8df5225c 100644 --- a/config/locales/crowdin/ar.yml +++ b/config/locales/crowdin/ar.yml @@ -371,7 +371,7 @@ ar: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ ar: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: لا يوجد حالياً ملفات للزبائن. no_results_content_text: إنشاء ملف زبون جديد @@ -5015,7 +5021,6 @@ ar: text_length_between: "الطول بين %{min} و %{max} محارف." text_line_separated: "القيم المتعددة المسموح بها (سطر واحد لكل قيمة)." text_load_default_configuration: "تحميل التكوين الافتراضي" - text_min_max_length_info: "0 يعني عدم تقييد" text_no_roles_defined: لا توجد مجموعات معرفة. text_no_access_tokens_configurable: "لا توجد أية رموز الوصول المميزة التي يمكن تكوينها." text_no_configuration_data: "لم يتم تكوين الأدوار, الأنواع, حالات مجموعات العمل وتدفقات العمل حتى الآن. فإنه ينصح بشدة تحميل التكوين الافتراضي. سوف تكون قادراً على تعديله حالما يتم التحميل." @@ -5030,7 +5035,6 @@ ar: text_powered_by: "بواسطة %{link}" text_project_identifier_info: "يتم السماح فقط رسائل الحالة الأدنى (أ-ي) وأرقام، والشرطات وتسطير، يجب أن تبدأ بحرف حالة الأدنى." text_reassign: "تعيين أن تعمل الحزمة:" - text_regexp_info: "على سبيل المثال. ^[A-Z0-9] + $" text_regexp_multiline: 'يتم تطبيق التعبير الاعتيادي في وضع متعدد الأسطر. على سبيل المثال: ^---\s+' text_repository_usernames_mapping: "قم باختيار أو تحديث مستخدم \"OpenProject: المشروع المفتوح\" الذي تم تعيينه لكل اسم مستخدم وُجِد في سجل المستودع.\nالمستخدمون الذين لديهم نفس اسم المستخدم أو عنوان البريد الإلكتروني في المشروع المفتوح OpenProject والمستودع يتم تعيينهم تلقائيًّا." text_status_changed_by_changeset: "تطبيق التغييرات %{value}." diff --git a/config/locales/crowdin/az.yml b/config/locales/crowdin/az.yml index 5b8fce3ba5a..28181438295 100644 --- a/config/locales/crowdin/az.yml +++ b/config/locales/crowdin/az.yml @@ -371,7 +371,7 @@ az: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ az: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4813,7 +4819,6 @@ az: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4828,7 +4833,6 @@ az: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/be.yml b/config/locales/crowdin/be.yml index 1c58e3dbaca..fc01637cc31 100644 --- a/config/locales/crowdin/be.yml +++ b/config/locales/crowdin/be.yml @@ -371,7 +371,7 @@ be: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ be: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4915,7 +4921,6 @@ be: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4930,7 +4935,6 @@ be: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/bg.yml b/config/locales/crowdin/bg.yml index 71d016cd959..e564fb86a30 100644 --- a/config/locales/crowdin/bg.yml +++ b/config/locales/crowdin/bg.yml @@ -371,7 +371,7 @@ bg: contained_in_type: "Съдържа се в тип" confirm_destroy_option: "Изтриването на опция ще изтрие всички нейни срещания (напр. в работните пакети). Сигурни ли сте, че искате да я изтриете?" reorder_alphabetical: "Пренареждане на стойностите по азбучен ред" - reorder_confirmation: "Внимание: Текущият ред на наличните стойности ще бъде загубен. Желаете ли да продължите?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ bg: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: В момента няма потребителски полета. no_results_content_text: Създаване на ново персонализирано поле @@ -4813,7 +4819,6 @@ bg: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 - без ограничения" text_no_roles_defined: Няма определени роли. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4828,7 +4833,6 @@ bg: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'Уеднаквяването се прилага в многоредов режим. например ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/ca.yml b/config/locales/crowdin/ca.yml index 89920eb9172..68af262785d 100644 --- a/config/locales/crowdin/ca.yml +++ b/config/locales/crowdin/ca.yml @@ -368,7 +368,7 @@ ca: contained_in_type: "Contingut a la classe" confirm_destroy_option: "Eliminant una opció n'eliminareu totes les ocurrències (ex. en un paquet de treball). Segur que vols eliminar-ho?" reorder_alphabetical: "Reorganitza els valors alfabèticament" - reorder_confirmation: "Alerta: L'ordre actual dels valors disponibles es perdran. Vols continuar?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -395,6 +395,12 @@ ca: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Actualment no hi ha camps personalitzats. no_results_content_text: Crea un camp personalitzat nou @@ -4804,7 +4810,6 @@ ca: text_length_between: "Longitud entre %{min} i %{max} caràcters." text_line_separated: "Es permeten diversos valors (una línia per cada valor)." text_load_default_configuration: "Carregar la configuració predeterminada" - text_min_max_length_info: "0 significa sense restricció" text_no_roles_defined: No hi ha rols definits. text_no_access_tokens_configurable: "No hi ha tokens d'accés que siguin configurables." text_no_configuration_data: "Encara no s'han configurat els rols, tipus, estats dels paquet de treball i flux de treball.\nÉs altament recomanable que carreguis la configuració predeterminada. Podràs modificar-la un cop carregada." @@ -4819,7 +4824,6 @@ ca: text_powered_by: "Desenvolupat per %{link}" text_project_identifier_info: "Només es permeten lletres minúscules (a-z), números, guions i guions baixos, i ha de començar amb una lletra minúscula." text_reassign: "Reassigna al paquet de treball:" - text_regexp_info: "ex. ^[A-Z0-9]+$" text_regexp_multiline: 'S''aplica l''expressió regular en mode de múltilínia. Per exemple, ^---\s+' text_repository_usernames_mapping: "Seleccioneu l'assignació entre els usuaris del OpenProject i cada nom d'usuari trobat al repositori.\nEls usuaris amb el mateix nom d'usuari o correu del OpenProject i del repositori s'assignaran automàticament." text_status_changed_by_changeset: "Aplicat en el conjunt de canvis %{value}." diff --git a/config/locales/crowdin/ckb-IR.yml b/config/locales/crowdin/ckb-IR.yml index 7c9389736cc..5f44bc17690 100644 --- a/config/locales/crowdin/ckb-IR.yml +++ b/config/locales/crowdin/ckb-IR.yml @@ -371,7 +371,7 @@ ckb-IR: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ ckb-IR: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4813,7 +4819,6 @@ ckb-IR: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4828,7 +4833,6 @@ ckb-IR: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/cs.yml b/config/locales/crowdin/cs.yml index c86d14c17da..a737a1f2246 100644 --- a/config/locales/crowdin/cs.yml +++ b/config/locales/crowdin/cs.yml @@ -371,7 +371,7 @@ cs: contained_in_type: "Obsahuje typ" confirm_destroy_option: "Smazáním možnosti smažete všechny výskyty (např. v pracovních balíčcích). Opravdu ji chcete odstranit?" reorder_alphabetical: "Změnit pořadí abecedně" - reorder_confirmation: "Varování: Aktuální pořadí dostupných hodnot bude ztraceno. Pokračovat?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Nejprve je nutné vybrat pracovní balíček nebo projekt" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ cs: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: V současné době nejsou žádná vlastní pole. no_results_content_text: Vytvořit nové vlastní pole @@ -4914,7 +4920,6 @@ cs: text_length_between: "Délka mezi %{min} a %{max} znaky." text_line_separated: "Více hodnot povoleno (jeden řádek pro každou hodnotu)." text_load_default_configuration: "Nahrát výchozí konfiguraci" - text_min_max_length_info: "0 znamená bez omezení" text_no_roles_defined: Nebyly definovány žádné role. text_no_access_tokens_configurable: "Neexistují žádné přístupové tokeny, které by mohly být nakonfigurovány." text_no_configuration_data: "Role, typy, stavy úkolů ani průběh práce nebyly zatím nakonfigurovány.\nJe silně doporučeno nahrát výchozí konfiguraci. Poté si můžete vše upravit." @@ -4929,7 +4934,6 @@ cs: text_powered_by: "Běží na %{link}" text_project_identifier_info: "Jsou povolena pouze malá písmena (a-z), číslice, pomlčky a podtržítka. Musí začínat malým písmenem." text_reassign: "Přiřadit k pracovnímu balíčku:" - text_regexp_info: "např. ^[A-Z0-9]+$" text_regexp_multiline: 'regex je použit v režimu více řádků. např.: ^---\s+' text_repository_usernames_mapping: "Vyberte nebo aktualizujte mapovaný uživatel OpenProject ke každému uživatelskému jménu nalezenému v protokolu repozitáře.\nUživatelé se stejným OpenProject a repozitářovým jménem nebo e-mailem jsou automaticky mapováni." text_status_changed_by_changeset: "Aplikováno v sadě změn %{value}." diff --git a/config/locales/crowdin/da.yml b/config/locales/crowdin/da.yml index f2857a0c5a1..9b023a6cfaf 100644 --- a/config/locales/crowdin/da.yml +++ b/config/locales/crowdin/da.yml @@ -369,7 +369,7 @@ da: contained_in_type: "Indeholdt i type" confirm_destroy_option: "Sletning af en indstilling vil slette alle dens forekomster (f.eks. i arbejdspakker). Er du sikker på, at du vil slette den?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -396,6 +396,12 @@ da: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Der er i øjeblikket ingen brugerdefinerede felter. no_results_content_text: Oprette en ny brugerdefineret felt @@ -4809,7 +4815,6 @@ da: text_length_between: "Længde mellem %{min} og %{max} tegn." text_line_separated: "Flere værdier tilladt (én linje for hver værdi)." text_load_default_configuration: "Hent forhåndsvalgt opsætning" - text_min_max_length_info: "0 betyder ingen begrænsning" text_no_roles_defined: Der er ikke defineret nogle roller. text_no_access_tokens_configurable: "Der er ingen adgangstegn, som kan konfigureres." text_no_configuration_data: "Roller, typer, statusser for arbejdspakker og arbejdsgange er endnu ikke sat op. Det anbefales stærkt at hente forhåndsopsætningen. Du vil kunne ændre den når den er indlæst." @@ -4824,7 +4829,6 @@ da: text_powered_by: "Drevet af %{link}" text_project_identifier_info: "Kun små bogstaver (a-z), tal, - og _ er tilladt; først tegn skal være et bogstav." text_reassign: "Gentildel til arbejdspakke:" - text_regexp_info: "fx ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Vælg eller opdatér den OpenProject-bruger, der er tilknyttet hvert brugernavn i loggen for projektarkivet.\nBrugere med det samme brugernavn eller mailadresse i OpenProject og i projektarkivet vil automatisk blive tilknyttet." text_status_changed_by_changeset: "Indføjet i pakken af ændringer, %{value}." diff --git a/config/locales/crowdin/de.yml b/config/locales/crowdin/de.yml index fb282d6a7ae..9009929c323 100644 --- a/config/locales/crowdin/de.yml +++ b/config/locales/crowdin/de.yml @@ -368,7 +368,7 @@ de: contained_in_type: "In Typ enthalten" confirm_destroy_option: "Löschen eines Werts entfernt alle bereits gesetzten Werte (z.B. bei Arbeitspaketen). Sind Sie sicher, dass Sie den Wert löschen möchten?" reorder_alphabetical: "Alphabetisch umsortieren" - reorder_confirmation: "Warnung: Die aktuelle Reihenfolge der verfügbaren Werte geht verloren. Fortfahren?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Zunächst muss ein Arbeitspaket oder ein Projekt ausgewählt werden" calculated_field_not_editable: "Nicht editierbares Attribut. Dieser Wert wird automatisch berechnet." no_role_assigment: "Keine Rollenzuweisung" @@ -395,6 +395,12 @@ de: Erlaubt das benutzerdefinierte Feld in einem Filter in der Arbeitspaket-Ansicht zu verwenden. Beachten Sie, dass, nur wenn "Für alle Projekte" ausgewählt ist, das benutzerdefinierte Feld in globalen Ansichten angezeigt wird. formula: project: "Fügen Sie numerische Werte hinzu oder geben Sie / ein, um nach einem Attribut oder einem mathematischen Operator zu suchen." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Zur Zeit existieren keine benutzerdefinierten Felder. no_results_content_text: Neues benutzerdefiniertes Feld anlegen @@ -3248,7 +3254,7 @@ de: label_equals_with_descendants: "ist (ODER) inkl. Unterelementen " label_everywhere: "überall" label_example: "Beispiel" - label_experimental: "Experimentel" + label_experimental: "Experimentell" label_i_am_member: "Ich bin Mitglied" label_ifc_viewer: "IFC-Viewer" label_ifc_model_plural: "IFC-Modelle" @@ -4807,7 +4813,6 @@ de: text_length_between: "Länge zwischen %{min} und %{max} Zeichen." text_line_separated: "Mehrere Werte sind erlaubt (eine Zeile pro Wert)." text_load_default_configuration: "Standard-Konfiguration laden" - text_min_max_length_info: "0 heißt keine Beschränkung" text_no_roles_defined: Es wurden keine Rollen definiert. text_no_access_tokens_configurable: "Es existieren keine Zugangs-Tokens, die konfiguriert werden können." text_no_configuration_data: "Rollen, Typen, Arbeitspaket-Status und Workflows wurden noch nicht konfiguriert. Es wird empfohlen die Standard-Konfiguration zu laden. Sobald die Konfiguration geladen ist, kann diese geändert werden." @@ -4822,7 +4827,6 @@ de: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Kleinbuchstaben (a-z), Ziffern, Binde- und Unterstriche erlaubt. Muss mit einem Kleinbuchstaben beginnen." text_reassign: "Zuweisung zu Arbeitspaket:" - text_regexp_info: "z. B. ^[A-Z0-9]+$" text_regexp_multiline: 'Der reguläre Ausdruck wird in einem mehrzeiligen Modus angewandt, z.B. ^---\s+' text_repository_usernames_mapping: "Bitte legen Sie die Zuordnung der OpenProject-Benutzer zu den Benutzernamen der Commit-Log-Meldungen des Projektarchivs fest.\nBenutzer mit identischen OpenProject- und Projektarchiv-Benutzernamen oder -E-Mail-Adressen werden automatisch zugeordnet." text_status_changed_by_changeset: "Status geändert durch Projektarchiv-Änderung %{value}." diff --git a/config/locales/crowdin/el.yml b/config/locales/crowdin/el.yml index dbf74f21914..4277576e8a1 100644 --- a/config/locales/crowdin/el.yml +++ b/config/locales/crowdin/el.yml @@ -367,7 +367,7 @@ el: contained_in_type: "Περιέχονται στον τύπο" confirm_destroy_option: "Η διαγραφή μιας επιλογής θα διαγράψει όλα τα περιστατικά της (π.χ. σε πακέτα εργασίας). Είστε βέβαιοι ότι θέλετε να το διαγράψετε;" reorder_alphabetical: "Αναδιάταξη τιμών αλφαβητικά" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -394,6 +394,12 @@ el: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Προς το παρόν δεν υπάρχουν προσαρμοσμένα πεδία. no_results_content_text: Δημιουργία νέου προσαρμοσμένου πεδίου @@ -4808,7 +4814,6 @@ el: text_length_between: "Μήκος μεταξύ %{min} και %{max} χαρακτήρων." text_line_separated: "Επιτρέπονται πολλαπλές τιμές (μία γραμμή για κάθε τιμή)." text_load_default_configuration: "Φόρτωση προεπιλεγμένων ρυθμίσεων διαμόρφωσης" - text_min_max_length_info: "Το 0 σημαίνει ότι δεν υπάρχουν περιορισμοί" text_no_roles_defined: Δεν έχουν οριστεί ρόλοι. text_no_access_tokens_configurable: "Δεν υπάρχουν tokens πρόσβασης που μπορούν να διαμορφωθούν." text_no_configuration_data: "Οι ρόλοι, οι τύποι, η καταστάσεις των πακέτων εργασίας και η ροή εργασίας δεν έχουν ρυθμιστεί ακόμα.\nΣυνιστάται ιδιαίτερα να φορτώσετε τις προεπιλεγμένες ρυθμίσεις. Θα είστε σε θέση να τις τροποποιήσετε μετά τη φόρτωση τους." @@ -4823,7 +4828,6 @@ el: text_powered_by: "Υποστηρίζεται από %{link}" text_project_identifier_info: "Επιτρέπονται μόνο πεζά γράμματα (α-ω), αριθμοί, παύλες και κάτω παύλες, πρέπει να αρχίζουν με πεζό γράμμα." text_reassign: "Επανεκχώρηση στο πακέτα εργασίας:" - text_regexp_info: "π.χ. ^[A-Z0-9]+$" text_regexp_multiline: 'Το regex εφαρμόστηκε σε μια λειτουργία πολλαπλών γραμμών. π.χ., ^---\s+' text_repository_usernames_mapping: "Επιλέξτε ή ενημερώστε τον χρήστη OpenProject που αντιστοιχεί σε κάθε όνομα χρήστη στο ιστορικό του αποθετηρίου.\nΧρήστες με το ίδιο όνομα χρήστη στο OpenProject και το αποθετήριο ή email αντιστοιχίζονται αυτόματα." text_status_changed_by_changeset: "Εφαρμόστηκε στις αλλαγές %{value}." diff --git a/config/locales/crowdin/eo.yml b/config/locales/crowdin/eo.yml index 9e4f1048d07..c02b4e62206 100644 --- a/config/locales/crowdin/eo.yml +++ b/config/locales/crowdin/eo.yml @@ -371,7 +371,7 @@ eo: contained_in_type: "Inkludita en la speco" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ eo: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Nun estas neniu adaptita kampo. no_results_content_text: Krei novan adaptitan kampon @@ -4813,7 +4819,6 @@ eo: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4828,7 +4833,6 @@ eo: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/es.yml b/config/locales/crowdin/es.yml index 93004f9b064..f5f1a4dca3e 100644 --- a/config/locales/crowdin/es.yml +++ b/config/locales/crowdin/es.yml @@ -369,7 +369,7 @@ es: contained_in_type: "Incluido en el tipo" confirm_destroy_option: "Al eliminar una opción, se eliminarán todas las repeticiones (por ejemplo, en paquetes de trabajo). ¿Está seguro de que desea eliminarla?" reorder_alphabetical: "Ordenar los valores alfabéticamente" - reorder_confirmation: "Advertencia: Se perderá el orden actual de los valores disponibles. ¿Quiere continuar?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Primero debe seleccionar el paquete de trabajo o el proyecto" calculated_field_not_editable: "Atributo no editable. Este valor se calcula automáticamente." no_role_assigment: "Sin asignación de roles" @@ -396,6 +396,12 @@ es: Permitir que el campo personalizado se utilice como filtro en las vistas de paquetes de trabajo. Tenga en cuenta que solo 'Para todos los proyectos' seleccionados, el campo personalizado se mostrará en vistas globales. formula: project: "Añada valores numéricos o escriba/para buscar un atributo o un operador matemático." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Actualmente no hay campos personalizados. no_results_content_text: Crear un nuevo campo personalizado @@ -4809,7 +4815,6 @@ es: text_length_between: "Longitud entre %{min} y %{max} caracteres." text_line_separated: "Múltiples valores permitidos (una línea para cada valor)." text_load_default_configuration: "Cargar la configuración predeterminada" - text_min_max_length_info: "0 significa sin restricción" text_no_roles_defined: No hay ningun rol definido. text_no_access_tokens_configurable: "No hay ningun token de acceso que pueda ser configurado." text_no_configuration_data: "Roles, tipos, estado de petición y flujo de trabajo no han sido configurados. Se recomienda cargar la configuración por defecto. Podrá modificarla cuando haya sido cargada." @@ -4824,7 +4829,6 @@ es: text_powered_by: "Con tecnología de %{link}" text_project_identifier_info: "Se permiten sólo letras minúsculas (a-z), números, guiones y guiones bajos, debe comenzar con una letra minúscula." text_reassign: "Reasignar al paquete de trabajo:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'La expresión regular se aplica en modo multilínea. Por ejemplo, ^---\s+' text_repository_usernames_mapping: "Seleccione o actualize el usuario de OpenProject asignado a cada nombre en el registro del repositorio. Los usuarios con el mismo nombre o email serán mapeados automáticamente." text_status_changed_by_changeset: "Aplicado en el conjunto de cambios %{value}." diff --git a/config/locales/crowdin/et.yml b/config/locales/crowdin/et.yml index ce7e92276a0..79ba6ed00a5 100644 --- a/config/locales/crowdin/et.yml +++ b/config/locales/crowdin/et.yml @@ -371,7 +371,7 @@ et: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ et: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4813,7 +4819,6 @@ et: text_length_between: "Pikkus peab olema %{min} kuni %{max} märki." text_line_separated: "Lubatud erinevad väärtused (igaüks eraldi real)." text_load_default_configuration: "Laadi vaikeasetused" - text_min_max_length_info: "0 tähendab, et piiranguid ei ole" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Rollid, valdkonnad, olekud ja töövood ei ole veel seadistatud.\\nVäga soovitav on laadida vaikeasetused. Peale laadimist saad neid ise muuta." @@ -4828,7 +4833,6 @@ et: text_powered_by: "Jooksutab %{link}" text_project_identifier_info: "Lubatud on ainult väikesed tähed (a-z), numbrid ja kriipsud. Peab algama väikse tähega." text_reassign: "Reassign to work package:" - text_regexp_info: "ehk teisisõnu ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Seosta OpenProject kasutaja hoidlasse sissekannete tegijaga. Sama nime või e-postiga kasutajad seostatakse automaatselt." text_status_changed_by_changeset: "Kehtestati toimikus %{value}." diff --git a/config/locales/crowdin/eu.yml b/config/locales/crowdin/eu.yml index 06b5fe3b7e8..25080d4132a 100644 --- a/config/locales/crowdin/eu.yml +++ b/config/locales/crowdin/eu.yml @@ -371,7 +371,7 @@ eu: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ eu: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4813,7 +4819,6 @@ eu: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4828,7 +4833,6 @@ eu: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/fa.yml b/config/locales/crowdin/fa.yml index 60de89095b5..cd362755e22 100644 --- a/config/locales/crowdin/fa.yml +++ b/config/locales/crowdin/fa.yml @@ -371,7 +371,7 @@ fa: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ fa: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4813,7 +4819,6 @@ fa: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4828,7 +4833,6 @@ fa: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/fi.yml b/config/locales/crowdin/fi.yml index 274c3a41064..b8aaa5f6e26 100644 --- a/config/locales/crowdin/fi.yml +++ b/config/locales/crowdin/fi.yml @@ -371,7 +371,7 @@ fi: contained_in_type: "Sisältyy tyyppiin" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ fi: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Tällä hetkellä ei ole mukautettuja kenttiä. no_results_content_text: Luo uusi mukautettu kenttä @@ -4813,7 +4819,6 @@ fi: text_length_between: "Pituus välillä %{min} ja %{max} merkkiä." text_line_separated: "Useat arvot sallittu (yksi rivi kullekin)." text_load_default_configuration: "Lataa oletusasetukset" - text_min_max_length_info: "0 tarkoittaa ei rajoitusta" text_no_roles_defined: Yhtään roolia ei ole määritetty. text_no_access_tokens_configurable: "Ei ole olemassa pääsyavaimia joita voisi konfiguroida." text_no_configuration_data: "Rooleja, tapahtumien tiloja ja työnkulkua ei vielä olla määritelty.\nOn erittäin suotavaa ladata vakioasetukset. Voit muuttaa sitä latauksen jälkeen." @@ -4828,7 +4833,6 @@ fi: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Vain pienet kirjaimet (a-z), numerot, väliviivat ja alaviivat ovat sallittuja. Ensimmäisenä tulee olla pieni kirjain." text_reassign: "Reassign to work package:" - text_regexp_info: "esim. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Valitse päivittääksesi Redmine käyttäjä jokaiseen käyttäjään joka löytyy tietovaraston lokista.\nKäyttäjät joilla on sama Redmine ja tietovaraston käyttäjänimi tai sähköpostiosoite, yhdistetään automaattisesti." text_status_changed_by_changeset: "Päivitetty muutosversioon %{value}." diff --git a/config/locales/crowdin/fil.yml b/config/locales/crowdin/fil.yml index 59eb624db33..2f81080692e 100644 --- a/config/locales/crowdin/fil.yml +++ b/config/locales/crowdin/fil.yml @@ -371,7 +371,7 @@ fil: contained_in_type: "Naglaman ng uri" confirm_destroy_option: "Kapag nagbubura ng opsyon ay makakabura sa lahat ng mga pangyayari nito (e.g. sa mga package na trabaho). Sigurado ka bang gusto mong burahin ito?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ fil: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Sa kasalukuyan ay walang mga custom field. no_results_content_text: Gumawa ng bagong patlang na custom @@ -4811,7 +4817,6 @@ fil: text_length_between: "Taas pagitan ng %{min} at %{max} na mga karakter." text_line_separated: "Maramihang halaga ang pinahintulutan (isang linya para sa bawat halaga)." text_load_default_configuration: "I-load ang kumpigurasyong default" - text_min_max_length_info: "0 ay nangunguhugan walang paghihigpit" text_no_roles_defined: Walang mga role na niliwanag. text_no_access_tokens_configurable: "Walang mga access tokens na pweding ma configure." text_no_configuration_data: "Ang mga tungkulin, uri, estado ng work package at daloy na trabaho ay hindi pa na configure.\nIto ay lubos na inirerekomenda upang i-lpad anh default na kumpigurasyon. Magagawa mong baguhin ito pag naka-load." @@ -4826,7 +4831,6 @@ fil: text_powered_by: "Pinalatakbo ng %{link}" text_project_identifier_info: "Maliit na titik lamang (a-z), mga numero, mga dash at underscore ang pinahintulutan, dapat magsimula sa maliit na titik." text_reassign: "I-reassign sa work package:" - text_regexp_info: "e. g ^[A-Z0-9]+$" text_regexp_multiline: 'Ang regex ay nakalagay sa multi-line mlde. hal., ^---\s+' text_repository_usernames_mapping: "Piliin o i-update ang OpenProject usrt naka-map sa bawat username nakita sa repositoryo log.\nAng mga gumagamit sa kaparehong OpenProject at repositoryong username o ang email ay automatikong naka-map." text_status_changed_by_changeset: "Ilapaylt sa changeset %{value}." diff --git a/config/locales/crowdin/fr.yml b/config/locales/crowdin/fr.yml index 44ee530fcfe..7a03a59a514 100644 --- a/config/locales/crowdin/fr.yml +++ b/config/locales/crowdin/fr.yml @@ -113,7 +113,7 @@ fr: index: description: "Le protocole de contexte de modèle permet aux agents d'intelligence artificielle de fournir à leurs utilisateurs les outils et les ressources exposés par cette instance d'OpenProject." resources_heading: "Ressources" - resources_description: "OpenProject implements the following resources. Each can be enabled, renamed and described as you want. For more information, please refer to the [documentation on MCP resources](docs_url)." + resources_description: "OpenProject met en œuvre les ressources suivantes. Chacune d'entre elles peut être activée, renommée et décrite comme vous le souhaitez. Pour plus d'informations, veuillez vous référer à la [documentation sur les ressources MCP](docs_url)." resources_submit: "Mettre à jour les ressources" tools_heading: "Outils" tools_description: "OpenProject implémente les outils suivants. Chacun d'entre eux peut être activé, renommé et décrit comme vous le souhaitez. Pour en savoir plus, veuillez vous référer à la [documentation sur les outils MCP](docs_url)." @@ -122,17 +122,17 @@ fr: success: "Les configurations MCP ont été mises à jour avec succès." server_form: description_caption: "Comment le serveur MCP sera décrit aux autres applications qui s'y connectent." - title_caption: "A short title shown to applications that connect to the MCP server." - tool_response_format: "Tool response format" - tool_response_format_content_only_label: "Content only" + title_caption: "Titre court affiché aux applications qui se connectent au serveur MCP." + tool_response_format: "Format de réponse de l'outil" + tool_response_format_content_only_label: "Contenu uniquement" tool_response_format_content_only_caption: > - Choose this if MCP clients connecting to this instance do not support structured content. Tool responses will only contain plain text content and leave out the structured version. - tool_response_format_full_label: "Full" + Choisissez cette option si les clients MCP qui se connectent à cette instance ne prennent pas en charge le contenu structuré. Les réponses de l'outil ne contiendront que du texte brut et ne tiendront pas compte de la version structurée. + tool_response_format_full_label: "Complet" tool_response_format_full_caption: > - The most compatible option. Tool responses will include both regular and structured content, allowing MCP clients to choose which format they want to read. This may increase the number of tokens that the language model has to process, potentially increasing cost and decreasing performance. - tool_response_format_structured_only_label: "Structured content only" + L'option la plus compatible. Les réponses de l'outil comprendront à la fois du contenu régulier et structuré, ce qui permettra aux clients MCP de choisir le format qu'ils souhaitent lire. Cela peut augmenter le nombre de jetons que le modèle linguistique doit traiter, ce qui peut entraîner une augmentation des coûts et une diminution des performances. + tool_response_format_structured_only_label: "Contenu structuré uniquement" tool_response_format_structured_only_caption: > - Choose this if you are certain that MCP clients connecting to this instance support structured content. Tool responses will only include structured content and leave out its text representation. + Choisissez cette option si vous êtes certain que les clients MCP qui se connectent à cette instance prennent en charge le contenu structuré. Les réponses de l'outil n'incluront que le contenu structuré et ne tiendront pas compte de sa représentation textuelle. update: failure: "La configuration MCP n'a pas pu être mise à jour." success: "La configuration MCP a été mise à jour avec succès." @@ -371,7 +371,7 @@ fr: contained_in_type: "Figurant dans le type" confirm_destroy_option: "Supprimer une option supprimera toutes ses occurrences (ex. dans les plans de travail). Êtes-vous sûr de vouloir le supprimer ?" reorder_alphabetical: "Réorganiser les valeurs par ordre alphabétique" - reorder_confirmation: "Attention : l'ordre actuel des valeurs disponibles sera perdu. Continuer ?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "La sélection d'un lot de travaux ou d'un projet est requise en premier lieu" calculated_field_not_editable: "Attribut non modifiable. Cette valeur est calculée automatiquement." no_role_assigment: "Pas d'attribution de rôle" @@ -398,6 +398,12 @@ fr: Permet au champ personnalisé d'être utilisé dans un filtre dans les vues des lots de travaux. Notez que le champ personnalisé n'apparaîtra dans les vues globales que si l'option « Pour tous les projets » est sélectionnée. formula: project: "Ajoutez des valeurs numériques ou saisissez / pour rechercher un attribut ou un opérateur mathématique." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Il n'y a actuellement aucun champ personnalisé. no_results_content_text: Créer un nouveau champ personnalisé @@ -618,7 +624,7 @@ fr: is_for_all_blank_slate: heading: Pour tous les projets description: Cet attribut de projet est activé dans tous les projets, car l'option « Pour tous les projets » est cochée. Il ne peut pas être désactivé pour les projets individuels. - enabled_via_assignee_when_submitted_html: This project attribute cannot be disabled since it is set as
assignee when submitted for project initiation requests. + enabled_via_assignee_when_submitted_html: Cet attribut de projet ne peut pas être désactivé car il est défini comme assignee when submitted pour les demandes d'initiation de projet. types: no_results_title_text: Il n'y a actuellement aucun type disponible. form: @@ -634,8 +640,8 @@ fr: new_label: "Nouvelle priorité" creation_wizard: errors: - no_work_package_type: "Failed to enable project initiation request because it requires at least one active work package type and this project has none. Please add at least one work package type to this project." - no_status_when_submitted: "Failed to enable project initiation request because work package type %{type} requires at least one status associated with it. Please enable at least one status workflow for this work package type." + no_work_package_type: "La demande d'initiation de projet n'a pas pu être activée car elle nécessite au moins un type de lot de travaux actif et ce projet n'en a pas. Veuillez ajouter au moins un type de lot de travail à ce projet." + no_status_when_submitted: "Échec de l'activation de la demande d'initiation de projet car le type de lot de travail %{type} doit être associé à au moins un statut. Veuillez activer au moins un workflow de statut pour ce type de work package." export: description_attachment_export: "L'artefact généré sera enregistré en tant que pièce jointe au format PDF dans le lot de travaux de l'artefact." description_file_link_export: "Le lot de travaux de l'artefact contient un lien vers un fichier PDF stocké dans un espace de stockage de fichiers externe. Nécessite un stockage de fichiers de travail avec des dossiers de projet gérés automatiquement pour ce projet. Pour le moment, seuls les espaces de stockage de fichiers Nextcloud sont pris en charge." @@ -649,7 +655,7 @@ fr: label_request_submission: "Demande d'envoi" project_attributes_description: > Sélectionnez les attributs du projet à inclure dans la demande de lancement du projet. Cette liste ne comprend que les [attributs du projet](project_attributes_url) activés pour ce projet. - enabled_because_required_html: This project attribute cannot be disabled for this project initiation request since it is defined as required. This can be changed in the administration settings by the administrator of the instance. + enabled_because_required_html: Cet attribut de projet ne peut pas être désactivé pour cette demande d'initiation de projet puisqu'il est défini comme obligatoire. Il peut être modifié dans les paramètres d'administration par l'administrateur de l'instance. status: button_edit: Modifier le statut wizard: @@ -1172,8 +1178,8 @@ fr: activerecord: attributes: agile/sprint: - sharing: "Sharing" - finish_date: "End date" + sharing: "Partage" + finish_date: "Échéance" announcements: show_until: "Afficher jusqu'à" attachment: @@ -1548,7 +1554,7 @@ fr: not_available: "n'est pas disponible en raison d'une configuration système." not_deletable: "ne peut pas être supprimé" not_current_user: "n'est pas l'utilisateur actuel." - only_one_active_sprint_allowed: "only one active sprint is allowed per project." + only_one_active_sprint_allowed: "un seul sprint actif est autorisé par projet." not_found: "introuvable." not_a_date: "n'est pas une date valide." not_a_datetime: "n'est pas une heure valide." @@ -1656,7 +1662,7 @@ fr: meeting: error_conflict: "Impossible d'enregistrer, car la réunion a été mise à jour par quelqu'un d'autre entre-temps. Veuillez recharger la page." message: - cannot_move_message_to_forum_of_different_project: "A message cannot be moved to a forum of a different project." + cannot_move_message_to_forum_of_different_project: "Un message ne peut pas être déplacé vers un forum d'un autre projet." notifications: at_least_one_channel: "Au moins un canal pour envoyer des notifications doit être spécifié." attributes: @@ -2878,7 +2884,7 @@ fr: new_features_list: line_0: Lancement automatisé de projets (module complémentaire Enterprise). line_1: "Réunions : ajoutez des work packages nouveaux ou existants en tant que résultats." - line_2: "Meetings: show iCal responses in OpenProject." + line_2: "Réunions : afficher les réponses iCal dans OpenProject." line_3: "Réunions récurrentes : dupliquez les points de l'ordre du jour lors de la prochaine réunion." line_4: "Mise à disposition de la Communauté : Mise en évidence des attributs." line_5: Avertissement avant l'ouverture de liens externes dans le contenu fourni par l'utilisateur (module complémentaire Enterprise). @@ -3988,7 +3994,7 @@ fr: notice_successful_delete: "Suppression réussie." notice_successful_cancel: "Annulation réussie." notice_successful_update: "Mise à jour réussie." - notice_successful_move: "Successful move from %{from} to %{to}." + notice_successful_move: "Passage réussi de %{from} à %{to}." notice_unsuccessful_create: "Échec de la création." notice_unsuccessful_create_with_reason: "Échec de la création : %{reason}" notice_unsuccessful_update: "Mise à jour échouée." @@ -4150,7 +4156,7 @@ fr: permission_edit_project_query: "Modifier la requête du projet" placeholders: default: "-" - templated_hint: Automatically generated through type %{type} + templated_hint: Généré automatiquement par le type %{type} portfolio: count: zero: "0 portefeuille" @@ -4335,9 +4341,9 @@ fr: setting_capture_external_links: "Saisir les liens externes" setting_capture_external_links_text: > Lorsque cette option est activée, tous les liens externes en texte formaté sont redirigés vers une page d'avertissement avant de quitter l'application. Cela permet de protéger les utilisateurs contre les sites web externes potentiellement malveillants. - setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login: "Exiger que les utilisateurs soient connectés" setting_capture_external_links_require_login_text: > - When enabled, users wanting to click on external links need to be logged in before being able to continue. + Lorsque cette option est activée, les utilisateurs qui souhaitent cliquer sur des liens externes doivent être connectés avant de pouvoir continuer. setting_after_first_login_redirect_url: "Redirection de première connexion" setting_after_first_login_redirect_url_text_html: > Définissez un chemin pour rediriger les utilisateurs après leur première connexion. S’il est vide, les utilisateurs seront redirigés vers la page d'accueil de la visite d'intégration.
Exemple : /my/page @@ -4382,9 +4388,9 @@ fr: setting_smtp_password: "Mot de passe SMTP" setting_smtp_domain: "Domaine SMTP HELO" setting_activity_days_default: "Nombre des jours affichés dans l'activité du projet" - setting_api_tokens_enabled: "Enable API tokens" + setting_api_tokens_enabled: "Activer les jetons API" setting_api_tokens_enabled_caption: > - Decide whether users can create personal API tokens in their account settings. These tokens can be used to access the different APIs of OpenProject, such as APIv3 and MCP. + Décidez si les utilisateurs peuvent créer des jetons API personnels dans les paramètres de leur compte. Ces jetons peuvent être utilisés pour accéder aux différentes API d'OpenProject, telles que APIv3 et MCP. setting_app_subtitle: "Sous-titre de l'Application" setting_app_title: "Titre de l'Application" setting_attachment_max_size: "Taille maximale de la pièce jointe" @@ -4811,7 +4817,6 @@ fr: text_length_between: "Longueur entre %{min} et %{max} caractères." text_line_separated: "Valeurs multiples autorisées (une ligne par valeur)." text_load_default_configuration: "Charger la configuration par défaut" - text_min_max_length_info: "0 signifie aucune restriction" text_no_roles_defined: Il n'y a pas de rôles définis. text_no_access_tokens_configurable: "Il n'y a aucun jeton d'accès qui puisse être configuré." text_no_configuration_data: "Les rôles, les types, l'état des lots de travaux et les flux de travaux n'ont pas encore été configurés.\nIl est fortement recommandé de charger la configuration par défaut. Vous serrez capable de le modifier, une fois chargé." @@ -4826,7 +4831,6 @@ fr: text_powered_by: "Propulsé par %{link}" text_project_identifier_info: "Seulement les lettres en minuscule (a-z), les nombres, les tirets et les underscores sont autorisés, il est obligatoire de commencer avec une lettre en minuscule." text_reassign: "Réaffecter au lot de travaux :" - text_regexp_info: "ex. ^[A-Z0-9]+$" text_regexp_multiline: 'L''expression régulière est appliquée en mode multi-ligne, e.g. ^---\\s+' text_repository_usernames_mapping: "Définir ou modifier les associations entre les utilisateurs d'OpenProject et les utilisateurs trouvés dans le dépôt.\nLes utilisateurs ayant des noms ou des adresses e-mail identiques sont automatiquement associés." text_status_changed_by_changeset: "Appliqué dans « changeset » %{value}." @@ -4925,10 +4929,10 @@ fr: reset_failed_logins: "Réinitialiser les connexions echouées" status_user_and_brute_force: "%{user} et %{brute_force}" status_change: "Changement de statut" - text_change_disabled_for_provider_login: "The name and email is set by your login provider and can thus not be changed." + text_change_disabled_for_provider_login: "Le nom et l'adresse électronique sont définis par votre fournisseur d'accès et ne peuvent donc pas être modifiés." unlock: "Déverrouiller" unlock_and_reset_failed_logins: "Déverrouiller et réinitialiser les échecs de connexion" - error_cannot_delete_user: "User cannot be deleted" + error_cannot_delete_user: "L'utilisateur ne peut pas être supprimé" version_status_closed: "clôturé" version_status_locked: "verrouillé" version_status_open: "ouvert" diff --git a/config/locales/crowdin/he.yml b/config/locales/crowdin/he.yml index e168eb2b525..bb3435fa764 100644 --- a/config/locales/crowdin/he.yml +++ b/config/locales/crowdin/he.yml @@ -371,7 +371,7 @@ he: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ he: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4915,7 +4921,6 @@ he: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4930,7 +4935,6 @@ he: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "למשל ^[A-Z0-9] + $" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/hi.yml b/config/locales/crowdin/hi.yml index d98dda92ce6..75f8010a4ef 100644 --- a/config/locales/crowdin/hi.yml +++ b/config/locales/crowdin/hi.yml @@ -371,7 +371,7 @@ hi: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ hi: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4811,7 +4817,6 @@ hi: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4826,7 +4831,6 @@ hi: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/hr.yml b/config/locales/crowdin/hr.yml index 62246a2fd00..71fb74508f9 100644 --- a/config/locales/crowdin/hr.yml +++ b/config/locales/crowdin/hr.yml @@ -371,7 +371,7 @@ hr: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ hr: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Trenutno nije dostupno niti jedno prilagođeno polje. no_results_content_text: Novo prilagođeno polje @@ -4864,7 +4870,6 @@ hr: text_length_between: "Duljina između %{min} i %{max} znakova." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Učitaj zadanu konfiguraciju" - text_min_max_length_info: "0 znači da nema ograničenja" text_no_roles_defined: Nema definiranih rola. text_no_access_tokens_configurable: "Ne postoje pristupni tokeni koji mogu biti konfigurirani." text_no_configuration_data: "Role, tipovi, statusi radnih paketa i tijek rada nisu konfiguirani. Preporuka je da učitate zadanu konfiguraciju. Moći ćete ju modificirati naokn što se učita." @@ -4879,7 +4884,6 @@ hr: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Dodijeli radnom paketu:" - text_regexp_info: "npr. ^[A-Z0-9] + $" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Primjenjeno na changeset %{value}." diff --git a/config/locales/crowdin/hu.yml b/config/locales/crowdin/hu.yml index f24f3dd9296..4318a979c16 100644 --- a/config/locales/crowdin/hu.yml +++ b/config/locales/crowdin/hu.yml @@ -370,7 +370,7 @@ hu: contained_in_type: "A típus tartalmazza" confirm_destroy_option: "Egy opció törlése az összes eddigi használatánál (úgy, mint munkacsomagok) is törlődnek. Biztosan törölni szeretné?" reorder_alphabetical: "Név szerinti sorrendbe rendezés" - reorder_confirmation: "Figyelmeztetés: A rendelkezésre álló értékek jelenlegi sorrendje elveszik. Folytatni akarod?\n" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -397,6 +397,12 @@ hu: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Jelenleg nincsenek egyéni mezők. no_results_content_text: Új egyéni mező létrehozása @@ -4811,7 +4817,6 @@ hu: text_length_between: "%{min} és %{max} karakter közötti hosszúságú." text_line_separated: "A többszörös érték adás megengedett (minden érték külön sorban)." text_load_default_configuration: "Az alapértelmezett konfiguráció betöltése" - text_min_max_length_info: "0 azt jelenti, hogy nincs korlátozás" text_no_roles_defined: Nincsenek szerepkörök meghatározva. text_no_access_tokens_configurable: "Nincs hozzáférési token, amely konfigurálható." text_no_configuration_data: "Szerepkörök, típusok, a feladatcsoport állapotok és a munkafolyamatok nincsenek beállítva. Erősen ajánlott, az alapértelmezett konfiguráció betöltése. Akkor képes lesz arra, hogy módosítsa." @@ -4826,7 +4831,6 @@ hu: text_powered_by: "Készítette %{link}" text_project_identifier_info: "Csak kisbetű (a-z), számok, kötőjelek és aláhúzásjelek van engedélyezve, és kisbetűvel kell kezdődnie." text_reassign: "A munkacsomag ismételt hozzárendelése:" - text_regexp_info: "pl. ^[A-Z0-9]+$" text_regexp_multiline: 'A reguláris kifejezés többsoros módban legyen alkalmazva. pld. ^---\s+' text_repository_usernames_mapping: "Jelölje be, vagy frissítse az alkalmazás felhasználóit, minden felhasználónév eltárolva, megtalálhatóak a csomagtároló naplóban. A felhasználók ugyanazzal az alkalmazási és csomagtárolói felhasználó névvel vagy email címmel automatikusan tárolódnak." text_status_changed_by_changeset: "A %{value} commit-ban hozzáadva." diff --git a/config/locales/crowdin/id.yml b/config/locales/crowdin/id.yml index 433944465fc..08518a0762e 100644 --- a/config/locales/crowdin/id.yml +++ b/config/locales/crowdin/id.yml @@ -367,7 +367,7 @@ id: contained_in_type: "Terkandung dalam jenis" confirm_destroy_option: "Menghapus sebuah pilihan akan menghapus semua kemunculan yang terjadi (mis. dalam paket pekerjaan). Apakah Anda yakin ingin menghapusnya?" reorder_alphabetical: "Susun ulang nilai menurut abjad" - reorder_confirmation: "Peringatan: Urutan nilai yang tersedia saat ini akan hilang. Melanjutkan?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -394,6 +394,12 @@ id: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Tidak ada bidang kustom saat ini. no_results_content_text: Buat bidang kustom baru @@ -4755,7 +4761,6 @@ id: text_length_between: "Panjang antara %{min} dan %{max} karakter." text_line_separated: "Jika ada beberapa nilai, satu baris untuk tiap nilai." text_load_default_configuration: "Pakai konfigurasi default" - text_min_max_length_info: "0 berarti tidak ada pembatasan" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Role, Tipe, status Paket-Penugasan dan Alur-Tugas belum dikonfigurasi. Disarankan untuk menggunakan konfigurasi default. Anda bisa mengubahnya setelah konfigurasi dibuat." @@ -4770,7 +4775,6 @@ id: text_powered_by: "Didukung oleh %{link}" text_project_identifier_info: "Hanya huruf kecil (a-z), angka, garis dan garis bawah yang diperbolehkan, harus diawali dengan huruf kecil." text_reassign: "Reassign to work package:" - text_regexp_info: "spt. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Pilih atau update user untuk dipetakan ke setiap username yang ditemukan di log repositori. User dengan username dan repositori atau email yang sama otomatis akan dipetakan." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/it.yml b/config/locales/crowdin/it.yml index 1ba759e4c85..f2ee735185c 100644 --- a/config/locales/crowdin/it.yml +++ b/config/locales/crowdin/it.yml @@ -369,7 +369,7 @@ it: contained_in_type: "Contenuto nel tipo" confirm_destroy_option: "L'eliminazione di un'opzione eliminerà tutte le sue occorrenze (ad es. nelle macro-attività). Procedere all'eliminazione?" reorder_alphabetical: "Riordina i valori alfabeticamente" - reorder_confirmation: "Attenzione: l'ordine corrente dei valori disponibili sarà perso. Continuare?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Prima è necessario selezionare la macro-attività o il progetto" calculated_field_not_editable: "Attributo non modificabile. Questo valore viene calcolato automaticamente." no_role_assigment: "Nessuna assegnazione di ruolo" @@ -396,6 +396,12 @@ it: Consenti l'utilizzo del campo personalizzato in un filtro nelle visualizzazioni delle macro-attività. Tieni presente che solo con l'opzione "Per tutti i progetti" selezionata, il campo personalizzato verrà visualizzato nelle visualizzazioni globali. formula: project: "Aggiungi valori numerici o digita / per cercare un attributo o un operatore matematico." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Al momento non esistono campi personalizzati. no_results_content_text: Crea un nuovo campo personalizzato @@ -4810,7 +4816,6 @@ it: text_length_between: "Lunghezza compresa tra %{min} e %{max} caratteri." text_line_separated: "Valori multipli consentiti (una linea per ogni valore)." text_load_default_configuration: "Carica la configurazione predefinita" - text_min_max_length_info: "0 significa nessuna restrizione" text_no_roles_defined: Non ci sono ruoli definiti. text_no_access_tokens_configurable: "Non sono presenti token di accesso che possono essere configurati." text_no_configuration_data: "Ruoli, tipi, stati delle macro-attività e flusso di lavoro non sono stati ancora configurati. Si consiglia di caricare la configurazione predefinita. Sarà possibile modificarla in seguito." @@ -4825,7 +4830,6 @@ it: text_powered_by: "Offerto da %{link}" text_project_identifier_info: "Solo le lettere minuscole (a-z), numeri, trattini e trattini bassi sono consentiti, devi iniziare con una lettera maiuscola." text_reassign: "Riassegna alla macro-attività:" - text_regexp_info: "es. ^[A-Z0-9]+$" text_regexp_multiline: 'L''espressione regolare viene applicata in modalità multi-linea. ad esempio, ^---\s+' text_repository_usernames_mapping: "Seleziona o aggiorna l'utente OpenProject mappato per ogni nome utente trovato nel registro dell'archivio. Gli utenti con lo stesso nome utente e repository OpenProject o e-mail vengono mappati automaticamente." text_status_changed_by_changeset: "Stato variato a partire dalla modifica %{value}." diff --git a/config/locales/crowdin/ja.yml b/config/locales/crowdin/ja.yml index 2239181cf32..9db06a1e734 100644 --- a/config/locales/crowdin/ja.yml +++ b/config/locales/crowdin/ja.yml @@ -369,7 +369,7 @@ ja: contained_in_type: "タイプに含まれる" confirm_destroy_option: "オプションを削除すると、そのすべての派生 (例えばワークパッケージ) が削除されます。削除してもよろしいですか?" reorder_alphabetical: "値をアルファベット順に並べ替える" - reorder_confirmation: "警告:利用可能な値の現在の順序は失われます。続行しますか?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "最初にワークパッケージまたはプロジェクトの選択が必要です" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -396,6 +396,12 @@ ja: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: 現在、カスタム フィールドはありません。 no_results_content_text: 新しいカスタム フィールドを作成 @@ -4761,7 +4767,6 @@ ja: text_length_between: "長さは%{min}から%{max}文字までです。" text_line_separated: "(1行ごとに書くことで)複数の値を設定できます。" text_load_default_configuration: "デフォルト設定をロード" - text_min_max_length_info: "0だと無制限になります。" text_no_roles_defined: 役割が定義されていません。 text_no_access_tokens_configurable: "設定できるアクセストークンがありません。" text_no_configuration_data: "ロール、ワークパッケージのステータスとタイプ、ワークフローが未設定です。\nデフォルト設定のロードを強くお勧めします。ロードした後、それを修正することができます。" @@ -4776,7 +4781,6 @@ ja: text_powered_by: "Powered by %{link}" text_project_identifier_info: "アルファベット小文字(a-z)・数字・ハイフン・アンダースコアが使えます。アルファベット小文字で始まる必要があります。" text_reassign: "ワークパッケージへの再割り当て:" - text_regexp_info: "例) ^[A-Z0-9]+$" text_regexp_multiline: '正規表現は、複数行モードで適用されます。例えば、^---\s+' text_repository_usernames_mapping: "リポジトリのログから検出されたユーザ名をどのOpenProjectユーザに関連づけるかを選択してください。\nログ上のユーザ名またはメールアドレスがOpenProjectのユーザと一致する場合は自動的に関連づけます。" text_status_changed_by_changeset: "変更履歴%{value}で適用されました。" diff --git a/config/locales/crowdin/ka.yml b/config/locales/crowdin/ka.yml index 59a064cfe85..c5d0df294c1 100644 --- a/config/locales/crowdin/ka.yml +++ b/config/locales/crowdin/ka.yml @@ -371,7 +371,7 @@ ka: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ ka: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4813,7 +4819,6 @@ ka: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4828,7 +4833,6 @@ ka: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/kk.yml b/config/locales/crowdin/kk.yml index 19353915eef..aecbc892661 100644 --- a/config/locales/crowdin/kk.yml +++ b/config/locales/crowdin/kk.yml @@ -371,7 +371,7 @@ kk: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ kk: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4813,7 +4819,6 @@ kk: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4828,7 +4833,6 @@ kk: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/ko.yml b/config/locales/crowdin/ko.yml index 96e751cd7df..e7a8200c047 100644 --- a/config/locales/crowdin/ko.yml +++ b/config/locales/crowdin/ko.yml @@ -371,7 +371,7 @@ ko: contained_in_type: "타입에 포함됨." confirm_destroy_option: "옵션을 삭제하면 모든 해당 항목(예: 작업 패키지 내 모든 항목)이 삭제됩니다. 그래도 삭제하시겠습니까?" reorder_alphabetical: "알파벳순으로 값 재정렬" - reorder_confirmation: "경고: 사용 가능한 값의 현재 순서가 손실됩니다. 계속하시겠습니까?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "먼저 작업 패키지 또는 프로젝트를 선택해야 합니다" calculated_field_not_editable: "편집할 수 없는 특성입니다. 이 값은 자동으로 계산됩니다." no_role_assigment: "역할 할당 없음" @@ -398,6 +398,12 @@ ko: 작업 패키지 보기의 필터에서 사용자 지정 필드를 사용할 수 있도록 허용합니다. '모든 프로젝트용'을 선택한 경우에만 사용자 지정 필드가 글로벌 보기에 표시됩니다. formula: project: "숫자 값을 추가하거나 /를 입력하여 특성 또는 수학 연산자를 검색합니다." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: 사용자 필드가 없습니다. no_results_content_text: 새 사용자 필드 생성 @@ -4759,7 +4765,6 @@ ko: text_length_between: "%{min} ~ %{max}자 사이의 길이" text_line_separated: "여러 값이 허용됩니다(각 값에 대해 한 줄)." text_load_default_configuration: "기본 구성 로드" - text_min_max_length_info: "0은 제한이 없음을 의미함" text_no_roles_defined: 정의된 역할이 없습니다. text_no_access_tokens_configurable: "구성할 수 있는 액세스 토큰이 없습니다." text_no_configuration_data: "역할, 유형, 작업 패키지 상태 및 워크플로가 아직 구성되지 않았습니다.\n기본 구성을 로드하는 것이 좋습니다. 로드한 후에 수정할 수 있습니다." @@ -4774,7 +4779,6 @@ ko: text_powered_by: "%{link} 제공" text_project_identifier_info: "소문자(a-z), 숫자, 대시(-) 및 밑줄(_)만 허용됩니다. 소문자로 시작해야 합니다." text_reassign: "작업 패키지에 다시 할당:" - text_regexp_info: "예: ^[A-Z0-9]+$" text_regexp_multiline: '멀티라인 모드에서 정규식이 적용됩니다. 예, e.g., ^---\s+' text_repository_usernames_mapping: "리포지토리 로그에 있는 각 사용자 이름에 매핑된 OpenProject 사용자를 선택하거나 업데이트하세요.\n동일한 OpenProject 및 리포지토리 사용자 이름이나 이메일을 가진 사용자가 자동으로 매핑됩니다." text_status_changed_by_changeset: "변경 집합 %{value}에 적용되었습니다." diff --git a/config/locales/crowdin/lt.yml b/config/locales/crowdin/lt.yml index fd3fcbf1110..65917ab2720 100644 --- a/config/locales/crowdin/lt.yml +++ b/config/locales/crowdin/lt.yml @@ -368,7 +368,7 @@ lt: contained_in_type: "Laikomas tipe" confirm_destroy_option: "Ištrinant savybę, visi jos panaudojimai (pvz., darbų paketuose) taip pat bus ištrinti. Ar Jūs tikrai norite ištrinti?" reorder_alphabetical: "Perrikiuoti reikšmes alfabeto tvarka" - reorder_confirmation: "Dėmesio: Dabartinė galimų reikšmių tvarka bus prarasta. Tęsti?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -395,6 +395,12 @@ lt: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Nėra jokių pasirinktinų laukų. no_results_content_text: Sukurti naują pasirinktinį lauką @@ -4909,7 +4915,6 @@ lt: text_length_between: "Ilgis tarp %{min} ir %{max} simbolių." text_line_separated: "Galimos kelios reikšmės (viena eilutė vienai reikšmei)." text_load_default_configuration: "Įkelti numatytąją konfigūraciją" - text_min_max_length_info: "0 reiškia jokių apribojimų" text_no_roles_defined: Nėra nustatytų vaidmenų. text_no_access_tokens_configurable: "Nėra prieigos raktų, kurie gali būti konfigūruojami." text_no_configuration_data: "Vaidmenys, tipai, darbų paketo būsenos ir darbų eiga dar nesukonfigūruoti. Stipriai rekomenduojama įkelti numatytąją konfigūraciją. Jūs ją galėsite redaguoti iškart po įkėlimo." @@ -4924,7 +4929,6 @@ lt: text_powered_by: "Parengta pagal %{link}" text_project_identifier_info: "Leidžiamos tik mažosios raidės (a-z), skaitmenys, paprasti (-) ir žemi brūkšneliai (_). Taip pat privalo prasidėti mažąja raide." text_reassign: "Iš naujo priskirti darbų paketui:" - text_regexp_info: "pvz., ^[A-Z0-9]+$" text_regexp_multiline: 'Reguliarioji išraiška yra pritaikoma daug eilučių režime, pvz., ^---\s+' text_repository_usernames_mapping: "Parinkite ar atnaujinkite OpenProject naudotoją, kuris paminėtas repozitorijos žurnale.\nNaudotojai, turintys tą patį OpenProject ir repozitorijos vardą ar el. paštą yra automatiškai surišti." text_status_changed_by_changeset: "Atlikta pakeitimų pakete %{value}." diff --git a/config/locales/crowdin/lv.yml b/config/locales/crowdin/lv.yml index 75c4c216598..a34189c9bac 100644 --- a/config/locales/crowdin/lv.yml +++ b/config/locales/crowdin/lv.yml @@ -371,7 +371,7 @@ lv: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ lv: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Pašlaik nav neviena pielāgota lauka. no_results_content_text: Izveidot jaunu pielāgotu lauku @@ -4864,7 +4870,6 @@ lv: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 nozīmē, ka nekādu ierobežojumu" text_no_roles_defined: Nav nevienas definētas lomas. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4879,7 +4884,6 @@ lv: text_powered_by: "Darbojas uz %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/mn.yml b/config/locales/crowdin/mn.yml index 1fb4c103116..28ee62a8af5 100644 --- a/config/locales/crowdin/mn.yml +++ b/config/locales/crowdin/mn.yml @@ -371,7 +371,7 @@ mn: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ mn: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4813,7 +4819,6 @@ mn: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4828,7 +4833,6 @@ mn: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/ms.yml b/config/locales/crowdin/ms.yml index 0e50c55d3ad..602b28bfe98 100644 --- a/config/locales/crowdin/ms.yml +++ b/config/locales/crowdin/ms.yml @@ -370,7 +370,7 @@ ms: contained_in_type: "Terkandung dalam jenis" confirm_destroy_option: "Padamkan satu pilihan akan memadamkan semua kejadian yang berkaitan dengannya (cth. dalam pakej kerja). Adakah anda pasti anda ingin memadamkannya?" reorder_alphabetical: "Susun semula nilai mengikut abjad" - reorder_confirmation: "Amaran: Susunan semasa nilai yang sedia ada akan hilang. Teruskan?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -397,6 +397,12 @@ ms: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Tiada ruangan tersuai pada masa ini. no_results_content_text: Cipta ruangan tersuai baharu @@ -4759,7 +4765,6 @@ ms: text_length_between: "Panjang aksara antara %{min} dan %{max}." text_line_separated: "Lebihan nilai dibenarkan (satu baris untuk setiap nilai)." text_load_default_configuration: "Muatkan konfigurasi default" - text_min_max_length_info: "0 bermakna tiada had" text_no_roles_defined: Tiada peranan ditentukan. text_no_access_tokens_configurable: "Tiada token akses yang boleh dikonfigurasikan." text_no_configuration_data: "Peranan, jenis, status pakej kerja dan aliran kerja belum dikonfigurasikan lagi. Sangat disarankan untuk memuatkan konfigurasi default. Anda akan dapat mengubahnya setelah ianya dimuatkan." @@ -4774,7 +4779,6 @@ ms: text_powered_by: "Dikuasakan oleh %{link}" text_project_identifier_info: "Hanya huruf kecil (a-z), nombor, sengkang dan tanda garis bawah adalah dibenarkan, mesti mula dengan huruf kecil." text_reassign: "Tugaskan semula ke pakej kerja:" - text_regexp_info: "cth. ^[A-Z0-9]+$" text_regexp_multiline: 'Regex digunakan dalam mod berbilang baris. cth., ^---\s+' text_repository_usernames_mapping: "Pilih atau kemas kini pengguna OpenProject yang dipetakan ke setiap nama pengguna yang ditemui dalam log repositori.\nPengguna dengan OpenProject dan nama pengguna repositori atau e-mel yang sama dipetakan secara automatik. " text_status_changed_by_changeset: "Dilaksanakan dalam set perubahan %{value}." diff --git a/config/locales/crowdin/ne.yml b/config/locales/crowdin/ne.yml index 8e2b6ffbb2b..6b5a0594889 100644 --- a/config/locales/crowdin/ne.yml +++ b/config/locales/crowdin/ne.yml @@ -371,7 +371,7 @@ ne: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ ne: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4813,7 +4819,6 @@ ne: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4828,7 +4833,6 @@ ne: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/nl.yml b/config/locales/crowdin/nl.yml index 1e30bccd038..e3c22f1e8e1 100644 --- a/config/locales/crowdin/nl.yml +++ b/config/locales/crowdin/nl.yml @@ -368,7 +368,7 @@ nl: contained_in_type: "Opgenomen in type" confirm_destroy_option: "Het verwijderen van een optie zal alle voorkomingen verwijderen. Weet u zeker dat u deze optie wilt verwijderen?" reorder_alphabetical: "Rangschik waarden alfabetisch" - reorder_confirmation: "Waarschuwing: De huidige volgorde van beschikbare waarden zal verloren gaan. Doorgaan?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Werkpakket- of projectselectie is eerst vereist" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -395,6 +395,12 @@ nl: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Momenteel zijn er geen aangepaste velden. no_results_content_text: Maak een nieuw aangepast veld @@ -4808,7 +4814,6 @@ nl: text_length_between: "Lengte tussen %{min} en %{max} karakters." text_line_separated: "Meerdere waarden toegestaan (één regel voor elke waarde)." text_load_default_configuration: "De standaardconfiguratie laden" - text_min_max_length_info: "0 betekent geen beperking" text_no_roles_defined: Er zijn geen rollen gedefinieerd. text_no_access_tokens_configurable: "Er zijn geen toegangstokens die kunnen worden geconfigureerd." text_no_configuration_data: "Rollen, types, werkpakket statussen en workflow zijn nog niet geconfigureerd.\nHet wordt ten zeerste aangeraden om de standaard configuratie te laden. U kunt deze wijzigen zodra het is geladen." @@ -4823,7 +4828,6 @@ nl: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Alleen kleine letters (a-z), cijfers, streepjes en underscores (_) zijn toegestaan, moet beginnen met een kleine letter." text_reassign: "Opnieuw toewijzen aan werkpakket:" - text_regexp_info: "bijv. ^[A-Z0-9]+$" text_regexp_multiline: 'De regex wordt toegepast in een modus met meerdere regels. bijvoorbeeld, ^---\s+' text_repository_usernames_mapping: "Selecteer of update de gebruiker van de OpenProject gelinkt aan elke gebruikersnaam gevonden in het repository log. Gebruikers met de zelfde OpenProject en repository gebruikersnaam of e-mail worden automatisch gelinkt." text_status_changed_by_changeset: "Toegepast in changeset %{value}." diff --git a/config/locales/crowdin/no.yml b/config/locales/crowdin/no.yml index 5cc1466fe0c..635f993fd89 100644 --- a/config/locales/crowdin/no.yml +++ b/config/locales/crowdin/no.yml @@ -371,7 +371,7 @@ contained_in_type: "Type beholdt" confirm_destroy_option: "Sletting av et alternativ vil slette alle forekomster (f.eks i arbeidspakker). Er du sikker på at du vil slette den?" reorder_alphabetical: "Omorganiser verdier alfabetisk" - reorder_confirmation: "Advarsel: Gjeldende rekkefølge for tilgjengelige verdier vil gå tapt. Vil du fortsette?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Det er ingen egendefinerte felt for øyeblikket. no_results_content_text: Opprett nytt egendefinert felt @@ -4812,7 +4818,6 @@ text_length_between: "Lengde mellom %{min} og %{max} tegn." text_line_separated: "Flere verdier tillatt (en verdi pr. linje)." text_load_default_configuration: "Last inn standardoppsettet" - text_min_max_length_info: "0 betyr ingen restriksjoner" text_no_roles_defined: Det er ingen roller definert. text_no_access_tokens_configurable: "Det finnes ingen tilgangsnøkler som kan konfigureres." text_no_configuration_data: "Roller, typer, arbeidspakkestatuser og arbeidsflyt har ikke blitt satt opp ennå.\nDet er sterkt anbefalt å laste standardoppsettet. Du kan redigere disse når de er lastet inn." @@ -4827,7 +4832,6 @@ text_powered_by: "Driftes med %{link}" text_project_identifier_info: "Kun små bokstaver (a-z), tall, bindestrek og understrek er tillatt. Første tegn må være liten bokstav." text_reassign: "Tilordne til arbeidspakke:" - text_regexp_info: "f.eks. ^[A-Z0-9]+$" text_regexp_multiline: 'Denne regexen brukes i flerlinjemodus. f.eks ^---\s+' text_repository_usernames_mapping: "Velg eller oppdater OpenProject-brukeren knyttet til hvert enkelt brukernavn i pakkebrønn-loggen.\nBrukere med samme OpenProject- og pakkebrønn-brukernavn eller e-post tilknyttes automatisk." text_status_changed_by_changeset: "Benyttet i changeset %{value}." diff --git a/config/locales/crowdin/pl.yml b/config/locales/crowdin/pl.yml index 1666b360d91..110f4f82ad5 100644 --- a/config/locales/crowdin/pl.yml +++ b/config/locales/crowdin/pl.yml @@ -368,7 +368,7 @@ pl: contained_in_type: "Zawartość" confirm_destroy_option: "Usunięcie tej opcji spowoduje usunięcie wszystkich powiązanych wystąpień (np. w Work Package). Czy na pewno chcesz to usunąć?" reorder_alphabetical: "Ustaw kolejność wartości alfabetycznie" - reorder_confirmation: "Ostrzeżenie: Bieżąca kolejność dostępnych wartości zostanie utracona. Kontynuować?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "W pierwszej kolejności wymagany jest wybór pakietu roboczego lub projektu" calculated_field_not_editable: "Atrybut nieedytowalny. Ta wartość jest obliczana automatycznie." no_role_assigment: "Brak przypisanie roli" @@ -395,6 +395,12 @@ pl: Zezwól na użycie pola niestandardowego w filtrze w widokach pakietów roboczych. Pamiętaj, że pole niestandardowe będzie wyświetlane w widokach globalnych dopiero po zaznaczeniu opcji „Dla wszystkich projektów”. formula: project: "Dodaj wartości liczbowe lub wpisz „/”, aby wyszukać atrybut lub operator matematyczny." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Nie ma jeszcze żadnych pól użytkownika. no_results_content_text: Utwórz pole użytkownika @@ -4908,7 +4914,6 @@ pl: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Wiele wartości dopuszczalne (jeden wiersz dla każdej wartości)." text_load_default_configuration: "Załaduj domyślną konfigurację" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: Nie ma żadnych zdefiniowanych uprawnień. text_no_access_tokens_configurable: "Brak tokenów dostępu, które mogą być skonfigurowane." text_no_configuration_data: "Role, typy, statusy pakietów roboczych i przepływ pracy nie zostały jeszcze skonfigurowane.\nZalecane jest załadowanie domyślnej konfiguracji. Będzie można ją zmodyfikować po załadowaniu." @@ -4923,7 +4928,6 @@ pl: text_powered_by: "Napędzany przez %{link}" text_project_identifier_info: "Tylko małe litery (a-z), cyfry, myślniki i podkreślenia są dozwolone, oraz musi zaczynać się małą literą." text_reassign: "Ponowne przydzielenie do Zestawu zadań:" - text_regexp_info: "np. ^[A-Z0-9]+$" text_regexp_multiline: 'Wyrażenie regularne jest stosowane w trybie wielolinijkowym. np. ^---\s+' text_repository_usernames_mapping: "Wybierz lub zaktualizuj użytkownika OpenProject do każdego użytkownika w dzienniku repozytorium.\nUżytkownicy z tym samym loginem lub adresem e-mail w OpenProject i repozytorium są automatycznie mapowani." text_status_changed_by_changeset: "Zastosowane w zbiorze zmian %{value}." diff --git a/config/locales/crowdin/pt-BR.yml b/config/locales/crowdin/pt-BR.yml index 5e932333b38..d3639339c28 100644 --- a/config/locales/crowdin/pt-BR.yml +++ b/config/locales/crowdin/pt-BR.yml @@ -370,7 +370,7 @@ pt-BR: contained_in_type: "Contido no tipo" confirm_destroy_option: "Removendo uma opção removerá todas as suas ocorrências (ex. em pacotes de trabalho). Tem certeza que você quer removê-la?" reorder_alphabetical: "Reorganizar valores em ordem alfabética" - reorder_confirmation: "Aviso: A ordem atual dos valores disponíveis será perdida. Continuar?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Primeiro é necessária a seleção do pacote de trabalho ou projeto" calculated_field_not_editable: "Atributo não editável. O valor é calculado automaticamente." no_role_assigment: "Nenhuma atribuição de função" @@ -397,6 +397,12 @@ pt-BR: Permite que o campo personalizado seja usado como filtro nas visualizações de pacotes de trabalho. Observe que apenas com a opção "Para todos os projetos" selecionada o campo personalizado aparecerá nas visualizações globais. formula: project: "Adicione valores numéricos ou digite / para buscar um atributo ou um operador matemático." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Atualmente, não há campos personalizados. no_results_content_text: Criar um novo campo personalizado @@ -4809,7 +4815,6 @@ pt-BR: text_length_between: "Tamanho entre %{min} e %{max} caracteres." text_line_separated: "Vários valores permitidos (uma linha para cada valor)." text_load_default_configuration: "Carregar a configuração padrão" - text_min_max_length_info: "0 significa nenhuma restrição" text_no_roles_defined: Não há nenhuma função definida. text_no_access_tokens_configurable: "Não há nenhum token de acesso que pode ser configurado." text_no_configuration_data: "Papéis, tipos, situação do pacote de trabalho e fluxo de trabalho não foram configurados ainda. É altamente recomendável carregar a configuração padrão. Uma vez carregada, você será capaz de modificá-la." @@ -4824,7 +4829,6 @@ pt-BR: text_powered_by: "Desenvolvido por %{link}" text_project_identifier_info: "Apenas letras minúsculas (a-z), números, hífens e sublinhados são permitidos, deve-se começar com uma letra minúscula." text_reassign: "Reatribua ao pacote de trabalho:" - text_regexp_info: "ex. ^[A-Z0-9]+$" text_regexp_multiline: 'A expressão regular é aplicada no modo multilinha. Por exemplo: ^---\s+' text_repository_usernames_mapping: "Selecionar ou atualizar o usuário do OpenProject mapeado para cada nome de usuário encontrado no log do repositório. Os usuários com o mesmo nome de usuário OpenProject e repositório ou e-mail serão mapeados automaticamente." text_status_changed_by_changeset: "Aplicado no conjunto de alterações %{value}." diff --git a/config/locales/crowdin/pt-PT.yml b/config/locales/crowdin/pt-PT.yml index 204a3dfd898..da681fdcad7 100644 --- a/config/locales/crowdin/pt-PT.yml +++ b/config/locales/crowdin/pt-PT.yml @@ -369,7 +369,7 @@ pt-PT: contained_in_type: "Incluído no tipo" confirm_destroy_option: "Apagar uma opção irá apagar todas as suas ocorrências (ex. em pacotes de trabalho). Tem certeza que quer apagá-la?" reorder_alphabetical: "Reordenar valores em ordem alfabética" - reorder_confirmation: "Aviso: A ordem atual de valores disponíveis será perdida. Continuar?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "A seleção do pacote de trabalho ou do projeto é necessária em primeiro lugar" calculated_field_not_editable: "Atributo não editável. Este valor é calculado automaticamente." no_role_assigment: "Sem atribuição de funções" @@ -396,6 +396,12 @@ pt-PT: Permita que o campo personalizado seja utilizado num filtro nas vistas do pacote de trabalho. Note que apenas com a opção "Para todos os projetos" selecionada, o campo personalizado irá aparecer nas vistas globais. formula: project: "Adicione valores numéricos ou escreva / para procurar um atributo ou um operador matemático." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Atualmente, não existem campos personalizados. no_results_content_text: Criar um novo campo personalizado @@ -4807,7 +4813,6 @@ pt-PT: text_length_between: "Comprimento entre %{min} e %{max} caráteres." text_line_separated: "Múltiplos valores permitidos (uma linha para cada valor)." text_load_default_configuration: "Carregar a configuração por defeito" - text_min_max_length_info: "0 significa sem restrições" text_no_roles_defined: Não existem papéis definidos. text_no_access_tokens_configurable: "Não há tokens de acesso que possam ser configurados." text_no_configuration_data: "Os papéis, tipos, estados de pacotes de trabalho e o fluxo de trabalho ainda não foram configurados.\nÉ recomendado carregar as configurações padrão. Poderá modificá-las depois de estarem carregadas." @@ -4822,7 +4827,6 @@ pt-PT: text_powered_by: "Desenvolvido por %{link}" text_project_identifier_info: "Apenas letras minúsculas (a-z), números, traços e underscores são permitidos, deve começar com uma letra minúscula." text_reassign: "Reatribuir pacote de trabalho:" - text_regexp_info: "ex. ^[A-Z0-9]+$" text_regexp_multiline: 'Regex é aplicado num modo de várias linhas. por exemplo, ^---\s+' text_repository_usernames_mapping: "Selecionar ou atualizar o utilizador de OpenProjecto mapeado a cada nome de utilizador encontrado no repositório.\nUtilizadores com o mesmo nome de utilizador ou e-mail no OpenProject e no repositório são mapeados automaticamente." text_status_changed_by_changeset: "Aplicado no changeset %{value}." diff --git a/config/locales/crowdin/ro.yml b/config/locales/crowdin/ro.yml index b7b5e1d5ce2..4e3eb277c00 100644 --- a/config/locales/crowdin/ro.yml +++ b/config/locales/crowdin/ro.yml @@ -371,7 +371,7 @@ ro: contained_in_type: "Înclusă în tipul" confirm_destroy_option: "Ștergerea unei opțiuni va șterge toate aparițiile acesteia (de exemplu, în pachetele de lucru). Sunteți sigur că doriți să o ștergeți?" reorder_alphabetical: "Reordonează valorile alfabetic" - reorder_confirmation: "Avertisment: Ordinea curentă a valorilor disponibile se va pierde. Continuați?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ ro: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: În acest moment nu există câmpuri personalizate. no_results_content_text: Creează câmp personalizat nou @@ -4863,7 +4869,6 @@ ro: text_length_between: "Lungime între %{min} şi %{max} de caractere." text_line_separated: "Sunt permise valori multiple (un rând pentru fiecare valoare)." text_load_default_configuration: "Încărcare configurația implicită" - text_min_max_length_info: "0 înseamnă eliminarea restricției" text_no_roles_defined: Nu există roluri definite. text_no_access_tokens_configurable: "Nu există tichete de acces care pot fi configurate." text_no_configuration_data: "Rolurile, tipurile, stările pachetelor de lucru și fluxul de lucru nu au fost configurate încă.\nEste recomandat să încărcați configurația implicită. Veți putea să o modificați după ce e încărcată." @@ -4878,7 +4883,6 @@ ro: text_powered_by: "Propulsat de %{link}" text_project_identifier_info: "Doar litere mici (a-z), numere, cratime şi linii de subliniere sunt permise, trebuie să înceapă cu o literă mică." text_reassign: "Reatribuire la pachetul de lucru:" - text_regexp_info: "ex. ^[A-Z0-9]+$" text_regexp_multiline: 'Formula regex este aplicată într-un mod multilinie - de exemplu: ^---\s+' text_repository_usernames_mapping: "Selectați sau modificați contul OpenProject mapat la fiecare cont din istoricul repo-ului.\nUtilizatorii cu cont sau e-mail identic în OpenProject și repo sunt echivalați automat." text_status_changed_by_changeset: "Aplicat prin editarea %{value}." diff --git a/config/locales/crowdin/ru.yml b/config/locales/crowdin/ru.yml index 40a68ea5541..c781a70a291 100644 --- a/config/locales/crowdin/ru.yml +++ b/config/locales/crowdin/ru.yml @@ -370,7 +370,7 @@ ru: contained_in_type: "Содержится в типе" confirm_destroy_option: "Удаление опции приведет к удалению всех ее вхождений (например, в пакеты работ). Вы уверены, что вы хотите удалить ее?" reorder_alphabetical: "Переупорядочить значения по алфавиту" - reorder_confirmation: "Внимание: Текущий порядок доступных значений будет утерян. Продолжить?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Сначала необходимо выбрать пакет работ или проект" calculated_field_not_editable: "Не редактируемый атрибут. Это значение рассчитывается автоматически." no_role_assigment: "Роль не назначена" @@ -397,6 +397,12 @@ ru: Разрешить использовать пользовательское поле в качестве фильтра в представлениях пакетов работ. Обратите внимание, что только при выборе опции 'Для всех проектов' пользовательское поле будет отображаться в глобальных представлениях. formula: project: "Добавьте числовые значения или введите / для поиска атрибута или математического оператора." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Нет настраиваемых полей. no_results_content_text: Создать новое настраиваемое поле @@ -4910,7 +4916,6 @@ ru: text_length_between: "Количество символов между %{min} и %{max} ." text_line_separated: "Допускается несколько значений (одна строка для каждого значения)." text_load_default_configuration: "Загрузить конфигурацию по умолчанию" - text_min_max_length_info: "0 означает отсутствие ограничений" text_no_roles_defined: Роли не определены. text_no_access_tokens_configurable: "Маркеры доступа, которые могут быть настроены, отсутствуют." text_no_configuration_data: "Роли, типы, статусы пакетов работ и рабочие потоки еще не сконфигурированы. Настоятельно рекомендуем для правки загрузить конфигурацию по умолчанию." @@ -4925,7 +4930,6 @@ ru: text_powered_by: "С использованием %{link}" text_project_identifier_info: "Разрешены только строчные буквы (a-z), цифры, тире и знаки подчеркивания, начинать с буквы нижнего регистра." text_reassign: "Переназначить для пакета работ:" - text_regexp_info: "например: ^[A-Z0-9]+$" text_regexp_multiline: 'Регулярное выражение применяется в многострочном режиме. Например: ^---\s+' text_repository_usernames_mapping: "Выберете или обновите пользователя OpenProject сопоставленного с именами пользователей найдеными в журнале репозитория. Пользователи с одинаковыми именами в OpenProject и в репозитории или почте будут сопоставлены автоматически." text_status_changed_by_changeset: "Применены в наборе изменений %{value}." @@ -5026,7 +5030,7 @@ ru: reset_failed_logins: "Сброс неудачных попыток входа" status_user_and_brute_force: "%{user} и %{brute_force}" status_change: "Смена статуса" - text_change_disabled_for_provider_login: "The name and email is set by your login provider and can thus not be changed." + text_change_disabled_for_provider_login: "Это имя и адрес электронной почты заданы вашим поставщиком авторизации, поэтому их нельзя изменить." unlock: "Разблокировать" unlock_and_reset_failed_logins: "Разблокировать и сбросить неудачные попытки входа" error_cannot_delete_user: "User cannot be deleted" diff --git a/config/locales/crowdin/rw.yml b/config/locales/crowdin/rw.yml index 11f40e893d7..cff23b51616 100644 --- a/config/locales/crowdin/rw.yml +++ b/config/locales/crowdin/rw.yml @@ -371,7 +371,7 @@ rw: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ rw: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4813,7 +4819,6 @@ rw: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4828,7 +4833,6 @@ rw: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/si.yml b/config/locales/crowdin/si.yml index 183c50e6783..8fb5ca8e25d 100644 --- a/config/locales/crowdin/si.yml +++ b/config/locales/crowdin/si.yml @@ -371,7 +371,7 @@ si: contained_in_type: "වර්ගය අඩංගු" confirm_destroy_option: "විකල්පයක් මකා දැමීමෙන් එහි සියලු සිදුවීම් මකා දමනු ඇත (උදා: වැඩ පැකේජවල). ඔබට එය මකා දැමීමට අවශ්ය බව ඔබට විශ්වාසද?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ si: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: දැනට අභිරුචි ක්ෂේත්ර නොමැත. no_results_content_text: නව අභිරුචි ක්ෂේත්‍රයක් සාදන්න @@ -4813,7 +4819,6 @@ si: text_length_between: "%{min} සහ %{max} අක්ෂර අතර දිග." text_line_separated: "බහු අගයන් අවසර දී ඇත (එක් එක් අගය සඳහා එක් රේඛාවක්)." text_load_default_configuration: "පෙරනිමි වින්යාසය පටවන්න" - text_min_max_length_info: "0 යනු සීමා කිරීමක් නොවේ" text_no_roles_defined: අර්ථ දක්වා ඇති භූමිකාවන් නොමැත. text_no_access_tokens_configurable: "වින්යාසගත කළ හැකි ප්රවේශ ටෝකන නොමැත." text_no_configuration_data: "භූමිකාවන්, වර්ග, වැඩ පැකේජ තත්වයන් සහ කාර්ය ප්රවාහය තවමත් වින්යාස කර නොමැත.\nපෙරනිමි වින්යාසය පූරණය කිරීම අතිශයින් රෙකමදාරු කරනු ලැබේ. පටවන ලද පසු එය වෙනස් කිරීමට ඔබට හැකි වනු ඇත." @@ -4828,7 +4833,6 @@ si: text_powered_by: "%{link}විසින් තල්ලු" text_project_identifier_info: "අඩු සිද්ධි අකුරු (a-z), අංක, ඩෂ් සහ යටි ඉරි වලට පමණක් අවසර ඇත, අඩු සිද්ධි අකුරකින් ආරම්භ කළ යුතුය." text_reassign: "වැඩ පැකේජයට නැවත පැවරීම:" - text_regexp_info: "උදා: ^[A-Z0-9]+$" text_regexp_multiline: 'රීජෙක්ස් බහු රේඛා මාදිලියක යොදනු ලැබේ. උදා: ^ —\ s+' text_repository_usernames_mapping: "ගබඩාවේ ඇති එක් එක් පරිශීලක නාමයට සිතියම් ගත කර ඇති OpenProject පරිශීලකයා තෝරන්න හෝ යාවත්කාලීන කරන්න.\nඑකම OpenProject සහ ගබඩාව පරිශීලක නාමය හෝ විද්යුත් තැපෑල සහිත පරිශීලකයින් ස්වයංක්රීයව සිතියම් ගත කරනු ලැබේ." text_status_changed_by_changeset: "%{value}හි ව්යවහාරික වේ." diff --git a/config/locales/crowdin/sk.yml b/config/locales/crowdin/sk.yml index adb962291ff..89accc3a2e0 100644 --- a/config/locales/crowdin/sk.yml +++ b/config/locales/crowdin/sk.yml @@ -371,7 +371,7 @@ sk: contained_in_type: "Obsiahnuté v type" confirm_destroy_option: "Odstránením hodnoty sa odstránia všetky predtým nastavené hodnoty (napríklad pre pracovné balíčky). Naozaj chcete odstrániť hodnotu?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ sk: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Momentálne nie sú k dispozícií žiadne vlastné polia. no_results_content_text: Vytvoriť nové vlastné pole @@ -4914,7 +4920,6 @@ sk: text_length_between: "Dĺžka medzi %{min} až %{max} znakov." text_line_separated: "Je povolené zadať viaceré hodnoty (každú hodnotu na samostatný riadok)." text_load_default_configuration: "Nahrať predvolenú konfiguráciu" - text_min_max_length_info: "0 znamená bez obmedzení" text_no_roles_defined: Nie sú definované žiadne role. text_no_access_tokens_configurable: "Neexistujú žiadne Prístupové Tokeny, ktoré môžu byť konfigurované." text_no_configuration_data: "Roly, typy, pracovný balík stavy a pracovný postup zatiaľ neboli nakonfigurované. Doporučujeme načítať predvolenú konfiguráciu. Po je jnačítaní ju budete mať možnosť zmeniť podľa Vašich potrieb." @@ -4929,7 +4934,6 @@ sk: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Sú povolené len malé písmená (a-z), čísla, pomlčky a podčiarkovníky, hodnota musí začínať malým písmenom." text_reassign: "Zaradenie do pracovného balíčka:" - text_regexp_info: "napr. ^[A-Z0-9]+$" text_regexp_multiline: 'Regex je aplikovaný v režime s viacerými riadkami. napríklad ^---\s+' text_repository_usernames_mapping: "Vyberte alebo aktualizujte priradenie používateľov systému OpenProject k menám používateľov nájdených v zázname repozitára.\nPoužívatelia s rovnakým prihlasovacím menom alebo emailom v systéme OpenProject a repozitári sú priradení automaticky." text_status_changed_by_changeset: "Aplikované v súbore zmien %{value}." diff --git a/config/locales/crowdin/sl.yml b/config/locales/crowdin/sl.yml index f9da0cb5665..9045509cb70 100644 --- a/config/locales/crowdin/sl.yml +++ b/config/locales/crowdin/sl.yml @@ -370,7 +370,7 @@ sl: contained_in_type: "Vsebovano v vrstah" confirm_destroy_option: "Če izbrišete možnost, boste izbrisali vse njene primere (npr. v delovnih paketih). Ali ste prepričani, da ga želite izbrisati?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -397,6 +397,12 @@ sl: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Trenutno ni poslovnih procesov no_results_content_text: Ustvarite novo uporabniško lastnost @@ -4913,7 +4919,6 @@ sl: text_length_between: "Dolžina med %{min} in %{max} znakov." text_line_separated: "Dovoljenih več vrednosti (ena vrstica za vsako vrednost)." text_load_default_configuration: "Naloži privzeto konfiguracijo" - text_min_max_length_info: "0 pomeni brez omejitev" text_no_roles_defined: Nobene vloge ni definirane text_no_access_tokens_configurable: "Dostopnih žetonov ni mogoče konfigurirati." text_no_configuration_data: "Vloge, vrste, statusi delovnega paketa in potek dela še niso konfigurirani.\nZelo priporočljivo je, da naložite privzeto konfiguracijo. Ko ga boste naložili, ga boste lahko spremenili." @@ -4928,7 +4933,6 @@ sl: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Dovoljene so samo male črke (a-z), številke, črtice in podčrtaji, ki se začnejo z malo začetnico." text_reassign: "Ponovno dodelitev delovnemu paketu:" - text_regexp_info: "npr. ^[A-Z0-9]+$" text_regexp_multiline: 'Označba se uporablja v večvrstičnem načinu. npr. ^ --- \ s +' text_repository_usernames_mapping: "Izberite ali posodobite uporabnika OpenProject, vezanega na vsako uporabniško ime, ki ga najdete v dnevniku repozitorija.\nUporabniki z istim uporabniškim imenom ali e-poštnim imenom skladišča OpenProject se samodejno povežejo." text_status_changed_by_changeset: "Dodano v zapis sprememb %{value}." diff --git a/config/locales/crowdin/sr.yml b/config/locales/crowdin/sr.yml index 1b501fa19c9..c5a95f75504 100644 --- a/config/locales/crowdin/sr.yml +++ b/config/locales/crowdin/sr.yml @@ -371,7 +371,7 @@ sr: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ sr: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4864,7 +4870,6 @@ sr: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4879,7 +4884,6 @@ sr: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/sv.yml b/config/locales/crowdin/sv.yml index a34a5dd21b6..37c98ba2e2a 100644 --- a/config/locales/crowdin/sv.yml +++ b/config/locales/crowdin/sv.yml @@ -371,7 +371,7 @@ sv: contained_in_type: "Ingår i typ" confirm_destroy_option: "Radering av ett alternativ kommer radera alla förekomster (i arbetspaket). Är du säker på att du vill radera?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ sv: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Det finns för närvarande inga anpassade fält. no_results_content_text: Skapa ett nytt anpassat fält @@ -4811,7 +4817,6 @@ sv: text_length_between: "Längd mellan %{min} och %{max} tecken." text_line_separated: "Flera värden tillåtna (en rad för varje värde)." text_load_default_configuration: "Ladda standardkonfigurationen" - text_min_max_length_info: "0 innebär ingen begränsning" text_no_roles_defined: Det har inte definierats några roller. text_no_access_tokens_configurable: "Det finns inga åtkomstnycklar som kan konfigureras." text_no_configuration_data: "Roller, typer, status för arbetspaket och arbetsflöden har inte konfigurerats ännu. Det rekommenderas starkt att läsa in standardkonfigurationen. Du kommer att kunna ändra dem när de är initialiserade." @@ -4826,7 +4831,6 @@ sv: text_powered_by: "Drivs av %{link}" text_project_identifier_info: "Bara gemena bokstäver (a-z), siffror, bindestreck och understreck tillåts och måste dessutom börja med en gemen bokstav." text_reassign: "Tilldela till arbetspaket:" - text_regexp_info: "t.ex. ^[A-Z0-9]+$" text_regexp_multiline: 'Regex appliceras i flerradsläge. t.ex. ^---\s+' text_repository_usernames_mapping: "Välj eller uppdatera OpenProject användaren koppling till varje användarnamn i versionsarkivloggen. Användare med samma användarnamn eller E-post i OpenProject och versionsarkivet kopplas automatiskt." text_status_changed_by_changeset: "Tillämpad i uppdatering %{value}." diff --git a/config/locales/crowdin/th.yml b/config/locales/crowdin/th.yml index d9db65c8fde..342f9710148 100644 --- a/config/locales/crowdin/th.yml +++ b/config/locales/crowdin/th.yml @@ -371,7 +371,7 @@ th: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ th: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4762,7 +4768,6 @@ th: text_length_between: "ความยาวระหว่าง %{min} จนถึง %{max} ตัวอักษร" text_line_separated: "อนุญาตให้มีหลายค่าได้ (หนึ่งค่าต่อหนึ่งบรรทัด)" text_load_default_configuration: "โหลดการกำหนดค่าเริ่มต้น" - text_min_max_length_info: "0 หมายถึง ไม่มีข้อจำกัด" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4777,7 +4782,6 @@ th: text_powered_by: "ดำเนินการโดย %{link}" text_project_identifier_info: "ใช้ได้เฉพาะตัวพิมพ์เล็ก (a-z), ตัวเลข เส้นประ และขีดใต้ โดย ต้องเริ่มต้น ด้วยตัวอักษรพิมพ์เล็กเท่านั้น" text_reassign: "Reassign to work package:" - text_regexp_info: "ต.ย. เช่น ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "มีการนำไปใช้ในชุดการเปลี่ยนแปลง %{value}" diff --git a/config/locales/crowdin/tr.yml b/config/locales/crowdin/tr.yml index caec81b968d..64c1bc18901 100644 --- a/config/locales/crowdin/tr.yml +++ b/config/locales/crowdin/tr.yml @@ -372,7 +372,7 @@ tr: contained_in_type: "Kullanıldığı tipler" confirm_destroy_option: "Bir seçeneğin silinmesi, kullanıldığı her yerden (örneğin iş paketleri) silinmesine neden olur. Silmek istediğinizden emin misiniz?" reorder_alphabetical: "Değerleri alfabetik olarak yeniden sıralayın" - reorder_confirmation: "Uyarı: Mevcut değerlerin sırası kaybolacak. Devam et?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Öncelikle iş paketi veya proje seçimi gereklidir" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -399,6 +399,12 @@ tr: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Şu anda özel bir alan bulunmamakta. no_results_content_text: Yeni bir özel alan oluştur @@ -1049,7 +1055,6 @@ tr: title: "İş paketi paylaşımı için iş akışı eksik" message: "'İş paketi düzenleyicisi' rolü için hiçbir iş akışı yapılandırılmamıştır. Bir iş akışı olmadan, paylaşılan kullanıcı iş paketinin durumunu değiştiremez. İş akışları kopyalanabilir. Bir kaynak türü (örn. 'Görev') ve kaynak rolü (örn. 'Üye') seçin. Ardından hedef türleri seçin. Başlangıç olarak, tüm türleri hedef olarak seçebilirsiniz. Son olarak, hedef olarak 'İş paketi düzenleyicisi' rolünü seçin ve 'Kopyala' düğmesine basın. Varsayılanları bu şekilde oluşturduktan sonra, diğer tüm roller için yaptığınız gibi iş akışlarında ince ayar yapın." link_message: "İş akışlarını yönetim alanından yapılandırın." - templated_subject_hint: '%{type} türü aracılığıyla otomatik olarak oluşturulur' summary: reports: category: @@ -3237,7 +3242,6 @@ tr: label_duplicate: "kopya" label_duplicates: "kopyalayan" label_edit: "Düzenle" - label_edit_attribute: "Özellikleri düzenle" label_edit_x: "Düzenle: %{x}" label_enable_multi_select: "Çoklu seçimi etkinleştir" label_enabled_project_custom_fields: "Etkin özel alanlar" @@ -4813,7 +4817,6 @@ tr: text_length_between: "%{min} ve %{max} karakter arası uzunluk." text_line_separated: "Birden fazla değer kullanılabilir (her değer için bir satır)." text_load_default_configuration: "Varsayılan yapılandırmayı yükle" - text_min_max_length_info: "0 sınırlama yok demektir" text_no_roles_defined: Hiçbir rol tanımlanmadı. text_no_access_tokens_configurable: "Yapılandırılacak erişim belirteçleri yok." text_no_configuration_data: "Roller, türleri, çalışma paketi durumu ve iş akışı henüz yapılandırılmadı. Varsayılan yapılandırmayı yüklemeniz kesinlikle önerilir. Yüklendikten sonra onu değiştirebileceksiniz." @@ -4828,7 +4831,6 @@ tr: text_powered_by: "%{link} tarafından destekleniyor" text_project_identifier_info: "Yalnızca küçük harfler (a-z), sayılar, tire ve alt tire kullanılabilir ve mutlaka küçük harfle başlamalıdır." text_reassign: "İş paketine yeniden atama:" - text_regexp_info: "ör ^[A-Z0-9]+$" text_regexp_multiline: 'Regex (düzenli ifade) birden çok satırlı modda uygulanır. Örneğin, ^---\s+' text_repository_usernames_mapping: "Depo günlüklerinde bulunan her kullanıcı adına eşlenen OpenProject kullanıcısını seçin veya güncelleyin. Aynı OpenProject ve depo kullanıcı adına veya e-postasına sahip kullanıcılar otomatik olarak eşleştirilir." text_status_changed_by_changeset: "Uygulanan Değişiklik listesi %{value}." diff --git a/config/locales/crowdin/uk.yml b/config/locales/crowdin/uk.yml index 498683128a3..3bf4941d57d 100644 --- a/config/locales/crowdin/uk.yml +++ b/config/locales/crowdin/uk.yml @@ -368,7 +368,7 @@ uk: contained_in_type: "Міститься за типом" confirm_destroy_option: "Видалення опції призведе до видалення всіх його подій (наприклад, у робочих пакетах). Дійсно видалити його?" reorder_alphabetical: "Перевпорядкувати значення за алфавітом" - reorder_confirmation: "Увага! Поточний порядок доступних значень буде втрачено. Продовжити?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Спочатку потрібно вибрати пакет робіт або проєкт" calculated_field_not_editable: "Атрибут не підлягає редагуванню. Його значення обчислюється автоматично." no_role_assigment: "Роль не призначено" @@ -395,6 +395,12 @@ uk: Дозвольте використовувати користувацьке поле у фільтрі в поданнях пакетів робіт. Зверніть увагу: користувацьке поле відображатиметься в глобальних поданнях, лише якщо встановлено прапорець «Для всіх проєктів». formula: project: "Додайте числові значення або введіть «/» для пошуку атрибута чи математичного оператора." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Наразі немає спеціальних полів. no_results_content_text: Створіть нове спеціальне поле @@ -4907,7 +4913,6 @@ uk: text_length_between: "Довжина між %{min} і %{max} символів." text_line_separated: "Дозволено кілька значень (по одному значенню в рядок)." text_load_default_configuration: "Завантажити Конфігурацію по замовчуванню" - text_min_max_length_info: "0 означає відсутність заборон" text_no_roles_defined: Не визначено жодної ролі. text_no_access_tokens_configurable: "Немає маркерів доступу, які можна налаштувати." text_no_configuration_data: "Роль, типи, пакет робочі статуси і процес ще не налаштований.\nНастійно рекомендується завантажити налаштування за замовчуванням. Ви зможете змінити його тільки один раз." @@ -4922,7 +4927,6 @@ uk: text_powered_by: "Працює на %{link}" text_project_identifier_info: "Допускаються тільки рядкові малі букви (a-z), цифри, тире та нижнє підкреслення. Початок має бути з малої літери." text_reassign: "Перепризначити робочому пакету:" - text_regexp_info: "наприклад ^[A-Z0-9]+$" text_regexp_multiline: 'Реестр застосовується в багаторядковому режимі. наприклад, --- ---' text_repository_usernames_mapping: "Виберіть або оновіть користувача OpenProject, зіставленого з кожним ім'ям користувача в журналі репозиторію.\nКористувачі з таким самим ім'ям користувача або електронною поштою OpenProject автоматично відображаються." text_status_changed_by_changeset: " Застосовується в наборі змін %{value}" diff --git a/config/locales/crowdin/uz.yml b/config/locales/crowdin/uz.yml index 640417758ab..d05ed66c2a9 100644 --- a/config/locales/crowdin/uz.yml +++ b/config/locales/crowdin/uz.yml @@ -371,7 +371,7 @@ uz: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -398,6 +398,12 @@ uz: Allow the custom field to be used in a filter in work package views. Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. no_results_content_text: Create a new custom field @@ -4813,7 +4819,6 @@ uz: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -4828,7 +4833,6 @@ uz: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/locales/crowdin/vi.yml b/config/locales/crowdin/vi.yml index fc6e0760f26..b82944a4b7c 100644 --- a/config/locales/crowdin/vi.yml +++ b/config/locales/crowdin/vi.yml @@ -371,7 +371,7 @@ vi: contained_in_type: "Chứa trong loại" confirm_destroy_option: "Xóa một tùy chọn sẽ xóa tất cả các sự kiện của nó (ví dụ như trong các gói công việc). Bạn có chắc bạn muốn xóa bỏ nó?" reorder_alphabetical: "Sắp xếp lại các giá trị theo thứ tự bảng chữ cái" - reorder_confirmation: "Cảnh báo: Thứ tự hiện tại của các giá trị sẵn có sẽ bị mất. Tiếp tục?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Lựa chọn gói công việc hoặc dự án là bắt buộc trước tiên" calculated_field_not_editable: "Thuộc tính không thể chỉnh sửa. Giá trị này được tính toán tự động." no_role_assigment: "Không phân công vai trò" @@ -398,6 +398,12 @@ vi: Cho phép sử dụng trường tùy chỉnh trong bộ lọc trong chế độ xem gói công việc. Lưu ý rằng chỉ khi chọn 'Dành cho tất cả dự án', trường tùy chỉnh sẽ hiển thị ở chế độ xem chung. formula: project: "Thêm giá trị số hoặc nhập / để tìm kiếm thuộc tính hoặc toán tử toán học." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: Không có hiện tại không có trường tùy chỉnh. no_results_content_text: Tạo trường tùy chỉnh mới @@ -4760,7 +4766,6 @@ vi: text_length_between: "Độ dài từ %{min} đến %{max} ký tự." text_line_separated: "Cho phép nhiều giá trị (mỗi giá trị một dòng)." text_load_default_configuration: "Tải cấu hình mặc định" - text_min_max_length_info: "0 có nghĩa là không hạn chế" text_no_roles_defined: Không có vai trò nào được xác định. text_no_access_tokens_configurable: "Không có mã thông báo truy cập nào có thể được cấu hình." text_no_configuration_data: "Vai trò, loại, trạng thái gói công việc và quy trình làm việc chưa được định cấu hình.\nRất khuyến khích tải cấu hình mặc định. Bạn sẽ có thể sửa đổi nó sau khi tải." @@ -4775,7 +4780,6 @@ vi: text_powered_by: "Được cung cấp bởi %{link}" text_project_identifier_info: "Chỉ cho phép chữ cái viết thường (a-z), số, dấu gạch ngang và dấu gạch dưới, phải bắt đầu bằng chữ cái viết thường." text_reassign: "Phân công lại cho gói công việc:" - text_regexp_info: "ví dụ. ^[A-Z0-9]+$" text_regexp_multiline: 'Biểu thức được áp dụng ở chế độ đa dòng. Ví dụ: ^---\s+' text_repository_usernames_mapping: "Chọn hoặc cập nhật người dùng OpenProject được ánh xạ tới từng tên người dùng được tìm thấy trong nhật ký kho lưu trữ.\nNgười dùng có cùng tên người dùng hoặc email của OpenProject và kho lưu trữ sẽ được tự động ánh xạ." text_status_changed_by_changeset: "Được áp dụng trong bộ thay đổi %{value}." diff --git a/config/locales/crowdin/zh-CN.yml b/config/locales/crowdin/zh-CN.yml index 5ed139bb3e6..a0d82978d49 100644 --- a/config/locales/crowdin/zh-CN.yml +++ b/config/locales/crowdin/zh-CN.yml @@ -368,7 +368,7 @@ zh-CN: contained_in_type: "已包含在类型中" confirm_destroy_option: "删除某个选项将会删除其所有实例 (例如, 在工作包中)。确定要删除?" reorder_alphabetical: "按字母顺序重新排序" - reorder_confirmation: "警告:可用值的当前顺序将丢失。是否继续?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "需要先选择工作包或项目" calculated_field_not_editable: "不可编辑属性。此值是自动计算的。" no_role_assigment: "无角色指定" @@ -395,6 +395,12 @@ zh-CN: 允许在工作包视图的筛选器中使用自定义字段。请注意,仅当选中“适用于所有项目”时,自定义字段才会显示在全局视图中。 formula: project: "添加数值或输入 / 以搜索特性或数学运算符。" + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: 目前没有自定义字段。 no_results_content_text: 创建新的自定义字段 @@ -4753,7 +4759,6 @@ zh-CN: text_length_between: "长度介于 %{min} 和 %{max} 个字符。" text_line_separated: "允许有多个值 (每行一个值)。" text_load_default_configuration: "加载默认配置" - text_min_max_length_info: "0 表示没有限制" text_no_roles_defined: 没有定义的角色。 text_no_access_tokens_configurable: "没有可配置的访问令牌。" text_no_configuration_data: "角色、类型、工作包状态和工作流尚未配置。\n强烈建议加载默认配置。之后您可以再进行修改。" @@ -4768,7 +4773,6 @@ zh-CN: text_powered_by: "Powered by %{link}" text_project_identifier_info: "只有小写字母 (a-z)、 数字、 短划线和下划线被允许,必须以小写字母开头。" text_reassign: "重新分配工作包:" - text_regexp_info: "例如 ^ [A-Z0-9]+$" text_regexp_multiline: '以多行模式应用正则表达式,如 ^---\s+' text_repository_usernames_mapping: "选择或更新每个在存储库日志中发现的用户名所映射到的 OpenProject 用户。\n使用相同 OpenProject 和存储库用户名或电子邮件的用户已被自动映射。" text_status_changed_by_changeset: "被实施在更改集 %{value} 中。" diff --git a/config/locales/crowdin/zh-TW.yml b/config/locales/crowdin/zh-TW.yml index 4edf86f9037..6aeecef53fe 100644 --- a/config/locales/crowdin/zh-TW.yml +++ b/config/locales/crowdin/zh-TW.yml @@ -370,7 +370,7 @@ zh-TW: contained_in_type: "已包含在類型中" confirm_destroy_option: "刪除此選項將會刪除其所有出現位置(例如:工作套件中的該選項)。您確定要刪除嗎?" reorder_alphabetical: "按字母順序重新排列" - reorder_confirmation: "警告:目前可用值的順序將會遺失。繼續嗎?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "需要先選擇工作套件或專案" calculated_field_not_editable: "不可編輯屬性。此值會自動計算。" no_role_assigment: "無角色指派" @@ -397,6 +397,12 @@ zh-TW: 允許在工作套件檢視的篩選器中使用自訂欄位。請注意,只有選取「針對所有專案」時,自訂欄位才會顯示在全區域檢視中。 formula: project: "加入數值或輸入 / 搜尋屬性或數學運算符號。" + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: 目前沒有自定義的欄位 no_results_content_text: 建立一個新的自定義欄位 @@ -4756,7 +4762,6 @@ zh-TW: text_length_between: "長度須介於 %{min} 至 %{max} 個字元" text_line_separated: "可多行(每行一個值)。" text_load_default_configuration: "載入預設組態" - text_min_max_length_info: "0 代表沒有限制" text_no_roles_defined: 沒有定義的角色 text_no_access_tokens_configurable: "沒有存取令牌(Token)可以被設定。" text_no_configuration_data: "角色,類型,工作套件狀態與工作流程都尚未設定。\n強烈建議先載入預設值,然後再修改它們。" @@ -4771,7 +4776,6 @@ zh-TW: text_powered_by: "由 %{link} 提供" text_project_identifier_info: "僅允許小寫字母(a-z)、數字、破折號(-) 及底線(_),且必須以小寫字母開頭。" text_reassign: "重新指派到工作套件:" - text_regexp_info: "例如: ^[A-Z0-9]+$" text_regexp_multiline: '這個正規表達式套用在多行模式。 e.g., ^---\s+' text_repository_usernames_mapping: "選擇或更新與版本庫中的帳號所對應的OpenProject使用者。\n版本庫中與OpenProject的使用者具有相同名稱或email將被自動對應。" text_status_changed_by_changeset: "已套用至變更列表 %{value} 中。" diff --git a/config/locales/en.yml b/config/locales/en.yml index da27489b16a..f50449d83b6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -573,7 +573,7 @@ en: contained_in_type: "Contained in type" confirm_destroy_option: "Deleting an option will delete all of its occurrences (e.g. in work packages). Are you sure you want to delete it?" reorder_alphabetical: "Reorder values alphabetically" - reorder_confirmation: "Warning: The current order of available values will be lost. Continue?" + reorder_confirmation: "Warning: The current order of available values as well as all unsaved values will be lost. Are you sure you want to continue?" placeholder_version_select: "Work package or project selection is required first" calculated_field_not_editable: "Non-editable attribute. This value is calculated automatically." no_role_assigment: "No role assignment" @@ -601,6 +601,12 @@ en: Note that only with 'For all projects' selected, the custom field will show up in global views. formula: project: "Add numeric values or type / to search for an attribute or a mathematical operator." + regexp: + all: "eg. ^[A-Z0-9]+$" + project: "eg. ^[A-Z0-9]+$" + min_max: + all: "0 means no restriction" + project: "0 means no restriction" tab: no_results_title_text: There are currently no custom fields. @@ -5272,7 +5278,6 @@ en: text_length_between: "Length between %{min} and %{max} characters." text_line_separated: "Multiple values allowed (one line for each value)." text_load_default_configuration: "Load the default configuration" - text_min_max_length_info: "0 means no restriction" text_no_roles_defined: There are no roles defined. text_no_access_tokens_configurable: "There are no access tokens which can be configured." text_no_configuration_data: "Roles, types, work package statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded." @@ -5291,7 +5296,6 @@ en: text_powered_by: "Powered by %{link}" text_project_identifier_info: "Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter." text_reassign: "Reassign to work package:" - text_regexp_info: "eg. ^[A-Z0-9]+$" text_regexp_multiline: 'The regex is applied in a multi-line mode. e.g., ^---\s+' text_repository_usernames_mapping: "Select or update the OpenProject user mapped to each username found in the repository log.\nUsers with the same OpenProject and repository username or email are automatically mapped." text_status_changed_by_changeset: "Applied in changeset %{value}." diff --git a/config/routes.rb b/config/routes.rb index 7b06328b550..fa0494306fc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -218,6 +218,8 @@ Rails.application.routes.draw do get :attribute_help_text put :update_attribute_help_text + + get :list_items end scope module: :admin do @@ -694,6 +696,8 @@ Rails.application.routes.draw do get :attribute_help_text put :update_attribute_help_text + + get :list_items end resources :items, controller: "/admin/settings/project_custom_fields/hierarchy/items" do diff --git a/db/migrate/20260223142025_add_view_budgets_to_roles_with_edit_budgets.rb b/db/migrate/20260223142025_add_view_budgets_to_roles_with_edit_budgets.rb new file mode 100644 index 00000000000..dd8dd468349 --- /dev/null +++ b/db/migrate/20260223142025_add_view_budgets_to_roles_with_edit_budgets.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require Rails.root.join("db/migrate/migration_utils/permission_adder") + +class AddViewBudgetsToRolesWithEditBudgets < ActiveRecord::Migration[8.1] + def up + ::Migration::MigrationUtils::PermissionAdder.add(:edit_budgets, :view_budgets) + end + + def down + # No-op: removing view_budgets could break other functionality + end +end diff --git a/docs/use-cases/README.md b/docs/use-cases/README.md index 5fd3e7d98b0..8a824361259 100644 --- a/docs/use-cases/README.md +++ b/docs/use-cases/README.md @@ -12,7 +12,7 @@ keywords: use-cases | ------------------------------------------------------------ | ------------------------------------------------------------ | | [Meeting management](meeting-management) | With OpenProject's meeting management features, you can structure your meetings, define agendas, assign action items, and keep everything transparent and traceable in one place. Ensure your team stays aligned and decisions are well-documented throughout the project life cycle.
Learn how to plan, conduct, and follow up on meetings with OpenProject. | | [Objectives and Key Results (OKR) framework in OpenProject](okr-management) | Use OpenProject to set strategic goals, break them down into measurable key results, and track progress in a transparent and aligned way.
Learn how to manage OKRs, establish relations between goals and results, and embed goal-setting into your project management flow. | -| [Portfolio management](portfolio-management) | This guide provides detailed step-by-step instruction on how to set up an overview of your project portfolio and create custom reports using the Project Overview, Wiki and the Rich text (WYSIWYG) editor in OpenProject. | +| [Portfolio management](portfolio-management) | This guide provides detailed step-by-step instruction on how to set up an overview of your project portfolio using OpenProject Portfolios module. | | [PM² and PMflex project management with OpenProject](project-management-pm2-pmflex) | Learn how to use OpenProject to manage projects with the **PM² methodology**, developed by the European Commission, and its German extension **PMflex**, maintained by the Federal Office of Administration (BVA).
Get started with customizable workflows, templates, and collaboration tools to apply these frameworks effectively across all project phases. | | [Resource management](resource-management) | OpenProject does not have the automated functionality to provide detailed resource management or employee work capacity calculations. This guide with detailed step-by-step instructions introduces a workaround that can provide an avenue to accomplish this manually and visually beyond the features of the Team Planner module. | | [Implementing Scaled Agile Framework (SAFe) in OpenProject](safe-framework) | Learn how to set up and configure OpenProject to support the Scaled Agile Framework (SAFe) to successfully deliver value to customers using agile practices at scale. | diff --git a/docs/use-cases/portfolio-management/README.md b/docs/use-cases/portfolio-management/README.md index eed43032d7c..817ed3bbad3 100644 --- a/docs/use-cases/portfolio-management/README.md +++ b/docs/use-cases/portfolio-management/README.md @@ -1,83 +1,106 @@ --- sidebar_navigation: - title: Portfolio management and custom reporting - priority: 990 -description: Step-by-step instructions about portfolio management and custom reporting -keywords: use-case, portfolio management + title: Portfolio management + priority: 990 +description: Step-by-step instructions on portfolio management with OpenProject +keywords: use-case, portfolio management, portfolio --- -# Use Case: Portfolio management and custom reporting options +# Portfolio management with OpenProject -If you have a lot of projects running at the same time it can be helpful and even necessary to have a meta-level overview of your projects, keep track of the project status and due dates. With OpenProject you can do just that. +This use case explains how you can use **portfolio management in OpenProject** to get a **strategic overview across initiatives**, replace ad-hoc spreadsheets with structured reporting, and prepare meaningful insights for leadership. Portfolio management helps you monitor multiple **programs and projects** at a high level, identify risks early, and ensure alignment with organizational goals. + +This guide supports you in using: + +- the [**Portfolios module** (Enterprise add-on)](../../user-guide/portfolios/) for strategic grouping of workspaces, and +- complementary reporting options such as **project lists**, filters, and exports. + +If you have many projects running at the same time, it can be helpful and even necessary to maintain a meta-level overview, track overall status, and monitor due dates. With OpenProject, you can establish that strategic visibility in a structured and consistent way. + +> [!NOTE] +> This guide assumes that you are using **OpenProject with the Portfolios Enterprise add-on** enabled (Enterprise Cloud or Enterprise on-premises), and that you have permission to view and manage portfolios. + +## Overview + +When running many projects simultaneously, it becomes difficult to see the full picture: + +- You may not have a **single source of truth** for status and progress. +- Executive reporting can be **manual, inconsistent, and outdated**. +- You need to focus on **strategic signals** such as risks, timelines, and overall progress rather than operational details. + +Portfolio management in OpenProject addresses these challenges by grouping related **programs and projects** into a top-level workspace that provides a **high-level overview**. + +Portfolios in OpenProject are special workspaces that allow you to: + +- Combine multiple **programs and projects** into a strategic hierarchy +- Track **aggregated status and progress** across workspaces +- Use filters and customizable views for reporting +- Supplement this with project-level lists and exports when needed + +## Step-by-step guide + +### 1. Navigate to the portfolios overview + +Select **Portfolios** from the left hand or global modules menu. + +![Select the portfolios module from the left hand menu in OpenProject](openproject_use_case_select_portfolios_module.png) + +The overview page lists all portfolios you can access. Use filters, portfolio status, and aggregated status indicators across subitems to quickly assess portfolio health and identify initiatives that require attention. + +![Overview of portfolios in OpenProject](openproject_use_case_portfolios_filters.png) + +### 2. Structure your portfolio + +Within a portfolio, add: + +- **Programs** to group related strategic initiatives +- **Projects** for direct portfolio-level tracking + +A portfolio can contain programs, projects, or a mix of both. Define a structure that reflects your strategic priorities. Here is an example of a portfolio, which includes programs that in turn contain projects. + +![An example of a portfolio, which includes programs that in turn contain projects, shown in the "all projects" dropdown menu in OpenProject](openproject_use_case_portfolios_hierarchy_examples.png) -![Overview of all projects in OpenProject](openproject_use_case_portfolio_projects_overview.png) -## Create projects overview +Read more about [portfolio hierarchies](../../user-guide/portfolios/). -### Access project list +### 3. Manage portfolio subitems -To view all projects, first select the **Select a project** dropdown menu, then click on the **Project lists** button. -You can also get to projects overview by selecting **Projects** from the [global modules](../../user-guide/home/global-modules) menu in the top right corner. +Use the **Subitems widget** on the portfolio home page to maintain included programs and projects. -![Select a project dropdown menu and a button to open project list in OpenProject](openproject_use_case_portfolio_projects_list_button.png) +![Subitems widget in a portfolio module in OpenProject](openproject_use_case_portfolios_subitems_widget.png) -### Filter and sort projects +Review and adjust the structure regularly to keep your strategic overview aligned with organizational changes. -You will see a list of all projects within your organization. You can filter this list by various attributes, such as **Project owner** or **Created on**. Additionally, project custom fields can be used as filters (Enterprise add-on). If you haven't added custom fields yet, follow the instructions [here](../../system-admin-guide/custom-fields/). +### 4. Use filters and saved views -![Project list filters in OpenProject](openproject_use_case_portfolio_projects_overview_filters.png) +Configure views and apply filters to focus on relevant information, such as: -You can further adjust this view by adding or re-arranging columns and changing the sorting order. To sort the project list, click on a column heading, such as **Status**. Read more about [configuring project lists](../../user-guide/projects/project-lists/#configure-project-lists-view). +- Status +- Stakeholders +- Timeline indicators +- Custom attributes -After you have adjusted the projects overview to your liking, you can save it, -[export it](../../user-guide/projects/project-lists/#export-project-lists) or -[share it with key stakeholders](../../user-guide/projects/project-lists/#share-project-lists). +![Detailed view of filters for the portfolios module in OpenProject](openproject_use_case_portfolios_filters_detailed.png) -### Projects in Gantt view +## Complementary portfolio management features -You can add a visual component to the overview by clicking on the **Open as Gantt view** button. +In addition to the Portfolios module, OpenProject provides several features that can enhance your portfolio management and reporting setup. -![Button to open OpenProject projects overview in Gantt view](openproject_use_case_portfolio_projects_overview_gantt_button.png) +- [**Project lists**](../../user-guide/projects/project-lists/): Create a filterable overview of all projects across your organization. Adjust columns, sort by status or owner, save views, and share or export them for stakeholder reporting. -The **Gantt charts** module will open and the selected projects will be displayed in Gantt view. +- [**Gantt charts**](../../user-guide/gantt-chart/): Open multiple projects in a shared timeline to visualize milestones, overlaps, and dependencies. This supports cross-project timeline discussions. -![Multiple projects displayed in a Gantt charts view in OpenProject](openproject_use_case_portfolio_gantt_charts_view.png) +- **Export options**: Export work package tables or Gantt charts as PDF, XLS, or CSV for formal reporting. Learn more about [exporting work packages](../../user-guide/work-packages/exporting/) and [printing Gantt charts](../../user-guide/gantt-chart/#how-to-print-a-gantt-chart). -You can configure this view using the button with the three dots in the upper right corner and select **Configure view**. Find out more about [Gantt charts configuration](../../user-guide/gantt-chart/#gantt-chart-configuration). +- [**Wiki module**](../../user-guide/wiki/): Build structured portfolio reports with embedded work package tables, macros, and dynamic calculations. You can create reporting hubs or dashboards for different initiatives. -![Configure projects overview in Gantt charts module in OpenProject](openproject_use_case_portfolio_projects_overview_gantt_view_configure.png) +- [**Global work packages view**](../../user-guide/home/global-modules/): Analyze work packages across projects to identify overdue milestones, high-priority items, or cross-project risks affecting your portfolio. -## Create custom reports +## Outcome -### Export project reports +By combining the Portfolios module with structured reporting features, you establish a clear governance layer above your operational projects. -For creating custom project reports you can use the export function in the work packages table view or in the Gantt charts view. +Instead of consolidating information manually, you rely on live portfolio data, consistent status structures, and reusable reporting views. Strategic discussions become focused on priorities and risks rather than data collection. -![Icon to export work packages in OpenProject](openproject_use_case_portfolio_work_packages_export_icon.png) - -You can export the work packages in one of the following formats: PDF, XLS and CSV. Read more about [exporting work packages in OpenProject](../../user-guide/work-packages/exporting/#export-multiple-work-packages). - -![Work package export options in OpenProject](openproject_use_case_portfolio_projects_export_options.png) - -To export or print a Gantt chart, use the print function (**CTRL+P**) and then save it as a PDF. Only information displayed in the main screen area is included. Design elements, side menus, and top menus are excluded. Please see here [how to print a Gantt chart in OpenProject](../../user-guide/gantt-chart/#how-to-print-a-gantt-chart). - -### Project status reporting - -You can [display and configure the individual project status](../../user-guide/projects/project-status/) on the project overview page. - -For more advanced project reporting requirements, using the [Wiki module](../../user-guide/wiki/) is another powerful tool. The Wiki allows you to build complete custom reports using embedded work package tables, macros and even embedded calculations. - -Here is an example of how a project report wiki could look: - -![Custom status report in Wiki module in OpenProject](openproject_use_case_portfolio_wiki_status_report.png) - -And how the dynamic data, such as calculations, filters, macros and reference language work behind the scenes: - -![Editing a wiki page with dynamic data on project details in OpenProject](openproject_use_case_portfolio_wiki_status_report_edit_mode.png) - -For more information about the syntax and how the attributes work, please look [here](../../user-guide/wysiwyg/). - -If you want to work with multiple Wiki-based reports, you can create a parent Wiki page as a table of contents, for example, on which all the other reports are listed. - -See more info on Wiki and the use of Macros [here](../../user-guide/wiki/). +This allows you to manage growth, align initiatives with organizational goals, and maintain transparency across your entire project landscape. \ No newline at end of file diff --git a/docs/use-cases/portfolio-management/openproject_use_case_portfolios_filters.png b/docs/use-cases/portfolio-management/openproject_use_case_portfolios_filters.png new file mode 100644 index 00000000000..2e7941cb8f3 Binary files /dev/null and b/docs/use-cases/portfolio-management/openproject_use_case_portfolios_filters.png differ diff --git a/docs/use-cases/portfolio-management/openproject_use_case_portfolios_filters_detailed.png b/docs/use-cases/portfolio-management/openproject_use_case_portfolios_filters_detailed.png new file mode 100644 index 00000000000..774d0ed6c0d Binary files /dev/null and b/docs/use-cases/portfolio-management/openproject_use_case_portfolios_filters_detailed.png differ diff --git a/docs/use-cases/portfolio-management/openproject_use_case_portfolios_hierarchy_examples.png b/docs/use-cases/portfolio-management/openproject_use_case_portfolios_hierarchy_examples.png new file mode 100644 index 00000000000..d5f721d82aa Binary files /dev/null and b/docs/use-cases/portfolio-management/openproject_use_case_portfolios_hierarchy_examples.png differ diff --git a/docs/use-cases/portfolio-management/openproject_use_case_portfolios_subitems_widget.png b/docs/use-cases/portfolio-management/openproject_use_case_portfolios_subitems_widget.png new file mode 100644 index 00000000000..3ffb6d7eefd Binary files /dev/null and b/docs/use-cases/portfolio-management/openproject_use_case_portfolios_subitems_widget.png differ diff --git a/docs/use-cases/portfolio-management/openproject_use_case_select_portfolios_module.png b/docs/use-cases/portfolio-management/openproject_use_case_select_portfolios_module.png new file mode 100644 index 00000000000..a714c98ab90 Binary files /dev/null and b/docs/use-cases/portfolio-management/openproject_use_case_select_portfolios_module.png differ diff --git a/extensions/op-blocknote-hocuspocus/package-lock.json b/extensions/op-blocknote-hocuspocus/package-lock.json index ea6a539d1af..a5e1eb211ae 100644 --- a/extensions/op-blocknote-hocuspocus/package-lock.json +++ b/extensions/op-blocknote-hocuspocus/package-lock.json @@ -10,6 +10,7 @@ "license": "GPLv3", "dependencies": { "@blocknote/server-util": "^0.44.2", + "@hocuspocus/extension-logger": "^3.4.4", "@hocuspocus/server": "^3.4.0", "op-blocknote-extensions": "https://github.com/opf/op-blocknote-extensions/releases/download/v0.0.18/op-blocknote-extensions-0.0.18.tgz", "tsx": "^4.21.0" @@ -262,6 +263,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -284,6 +286,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -944,6 +947,7 @@ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", "license": "MIT", + "peer": true, "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" @@ -999,21 +1003,30 @@ } }, "node_modules/@hocuspocus/common": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@hocuspocus/common/-/common-3.4.0.tgz", - "integrity": "sha512-vN0kE/mGTjuwchq16naq+nEjFDmeSLNCkDSAB7DKvpZnuG4KQi5oC42VFdKbq/baQTbDMSe82rI7f9riOR8idQ==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@hocuspocus/common/-/common-3.4.4.tgz", + "integrity": "sha512-RykIJ0tsHHMP4Xk+4UCbc7SO5LgGxGUSTdbh6anJEsaALAyqinf1Nn5HYuMjLPolAmsar1v++m9zufR09NLpXA==", "license": "MIT", "dependencies": { "lib0": "^0.2.87" } }, - "node_modules/@hocuspocus/server": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@hocuspocus/server/-/server-3.4.0.tgz", - "integrity": "sha512-ludVWFkos7FgOmdGxn5UxhV7H0K4mMly8mq+wXAK8fOuW+9vrjQDfd23tOOcQPbZ0aGyPC0FMmEZ1GsFpVArCg==", + "node_modules/@hocuspocus/extension-logger": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@hocuspocus/extension-logger/-/extension-logger-3.4.4.tgz", + "integrity": "sha512-GEnjmvQlrDlr5hoTQ8NHStZkzpL42wkwZ5XOBMkEX/TgqcnEtKCK1SOK0xj+px9tEHaflDLtCq6f5oy4gOCkPQ==", "license": "MIT", "dependencies": { - "@hocuspocus/common": "^3.4.0", + "@hocuspocus/server": "^3.4.4" + } + }, + "node_modules/@hocuspocus/server": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@hocuspocus/server/-/server-3.4.4.tgz", + "integrity": "sha512-UV+oaONAejOzeYgUygNcgsc8RdZvSokVvAxluZJIisLACpRO/VsseQ5lWKDRwLd7Fn6+rHWDH3hGuQ1fdX1Ycg==", + "license": "MIT", + "dependencies": { + "@hocuspocus/common": "^3.4.4", "async-lock": "^1.3.1", "async-mutex": "^0.5.0", "kleur": "^4.1.4", @@ -1596,6 +1609,7 @@ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.13.0.tgz", "integrity": "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw==", "license": "MIT", + "peer": true, "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" @@ -1668,6 +1682,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.13.0.tgz", "integrity": "sha512-iUelgiTMgPVMpY5ZqASUpk8mC8HuR9FWKaDzK27w9oWip9tuB54Z8mePTxNcQaSPb6ErzEaC8x8egrRt7OsdGQ==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -1865,6 +1880,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.13.0.tgz", "integrity": "sha512-WKR4ucALq+lwx0WJZW17CspeTpXorbIOpvKv5mulZica6QxqfMhn8n1IXCkDws/mCoLRx4Drk5d377tIjFNsvQ==", "license": "MIT", + "peer": true, "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", @@ -2010,6 +2026,7 @@ "integrity": "sha512-gWEkeiyYE4vqjON/+Obqcoeffmk0NF15WSBwSs7zwVA2bAbTaE0SJ7P0WNGoJn8uE7fiaV5a7dKYIJriEqOrmA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2105,6 +2122,7 @@ "integrity": "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.48.1", "@typescript-eslint/types": "8.48.1", @@ -2439,6 +2457,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2720,7 +2739,6 @@ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -2852,8 +2870,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/data-urls": { "version": "5.0.0", @@ -2933,8 +2950,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/devlop": { "version": "1.1.0", @@ -3110,6 +3126,7 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3451,7 +3468,6 @@ "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -3945,6 +3961,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.28.4" }, @@ -5671,6 +5688,7 @@ "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz", "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==", "license": "MIT", + "peer": true, "dependencies": { "orderedmap": "^2.0.0" } @@ -5700,6 +5718,7 @@ "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz", "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==", "license": "MIT", + "peer": true, "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-transform": "^1.0.0", @@ -5748,6 +5767,7 @@ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.3.tgz", "integrity": "sha512-SqMiYMUQNNBP9kfPhLO8WXEk/fon47vc52FQsUiJzTBuyjKgEcoAwMyF04eQ4WZ2ArMn7+ReypYL60aKngbACQ==", "license": "MIT", + "peer": true, "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", @@ -5836,7 +5856,6 @@ "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.4.4.tgz", "integrity": "sha512-wOmoNZoOpvMminhifQYiYSTCLUDOiUbBunrMrMjA+dV52sY+vck1S4UhR6PkgnoCquvvMSeJjErXZ4qSaWCliA==", "license": "MIT", - "peer": true, "peerDependencies": { "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -5847,7 +5866,6 @@ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", "license": "MIT", - "peer": true, "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", @@ -5873,7 +5891,6 @@ "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", "license": "MIT", - "peer": true, "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" @@ -5896,7 +5913,6 @@ "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", "license": "MIT", - "peer": true, "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" @@ -5919,7 +5935,6 @@ "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.20.13", "use-composed-ref": "^1.3.0", @@ -6185,8 +6200,7 @@ "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/semver": { "version": "7.7.3", @@ -7068,7 +7082,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "license": "(MIT OR CC0-1.0)", - "peer": true, "engines": { "node": ">=16" }, @@ -7254,7 +7267,6 @@ "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.0.0" }, @@ -7276,7 +7288,6 @@ "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz", "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==", "license": "MIT", - "peer": true, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -7291,7 +7302,6 @@ "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", "license": "MIT", - "peer": true, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -7306,7 +7316,6 @@ "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz", "integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==", "license": "MIT", - "peer": true, "dependencies": { "use-isomorphic-layout-effect": "^1.1.1" }, @@ -7324,7 +7333,6 @@ "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", "license": "MIT", - "peer": true, "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" @@ -7408,6 +7416,7 @@ "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -7787,6 +7796,7 @@ "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz", "integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==", "license": "MIT", + "peer": true, "dependencies": { "lib0": "^0.2.85" }, @@ -7846,6 +7856,7 @@ "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.27.tgz", "integrity": "sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw==", "license": "MIT", + "peer": true, "dependencies": { "lib0": "^0.2.99" }, diff --git a/extensions/op-blocknote-hocuspocus/package.json b/extensions/op-blocknote-hocuspocus/package.json index 9ffc23cc3f3..53c994eb0e5 100644 --- a/extensions/op-blocknote-hocuspocus/package.json +++ b/extensions/op-blocknote-hocuspocus/package.json @@ -24,6 +24,7 @@ }, "dependencies": { "@blocknote/server-util": "^0.44.2", + "@hocuspocus/extension-logger": "^3.4.4", "@hocuspocus/server": "^3.4.0", "op-blocknote-extensions": "https://github.com/opf/op-blocknote-extensions/releases/download/v0.0.18/op-blocknote-extensions-0.0.18.tgz", "tsx": "^4.21.0" diff --git a/extensions/op-blocknote-hocuspocus/src/extensions/openProjectApi.ts b/extensions/op-blocknote-hocuspocus/src/extensions/openProjectApi.ts index 134cc767653..ef107e22f82 100644 --- a/extensions/op-blocknote-hocuspocus/src/extensions/openProjectApi.ts +++ b/extensions/op-blocknote-hocuspocus/src/extensions/openProjectApi.ts @@ -77,9 +77,6 @@ export class OpenProjectApi implements Extension { */ async onLoadDocument(data: onLoadDocumentPayload) { const { resourceUrl } = data.context; - - printLog(`[onLoadDocument] GET ${resourceUrl}`); - const response = await fetchResource(resourceUrl, data.context.token); if (response.status != 200) { @@ -111,8 +108,6 @@ export class OpenProjectApi implements Extension { return; } - printLog(`[onStoreDocument] PATCH ${resourceUrl}`); - const base64Data = Buffer.from(Y.encodeStateAsUpdate(data.document)).toString("base64"); // Create a copy of the document to avoid side effects diff --git a/extensions/op-blocknote-hocuspocus/src/index.ts b/extensions/op-blocknote-hocuspocus/src/index.ts index 10f2af827f8..f972be60ad8 100644 --- a/extensions/op-blocknote-hocuspocus/src/index.ts +++ b/extensions/op-blocknote-hocuspocus/src/index.ts @@ -1,12 +1,15 @@ +import { Logger } from "@hocuspocus/extension-logger"; import { Server } from "@hocuspocus/server"; import { OpenProjectApi } from "./extensions/openProjectApi"; - const server = new Server({ port: 1234, quiet: false, extensions: [ new OpenProjectApi(), + new Logger({ + onChange: false, + }), ], }); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ab2ef48df52..b94ddb03e83 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,18 +9,18 @@ "version": "0.1.0", "license": "GPLv3", "dependencies": { - "@angular/animations": "^21.1.3", - "@angular/cdk": "^21.1.3", - "@angular/cli": "^21.1.3", - "@angular/common": "^21.1.3", - "@angular/compiler": "^21.1.3", - "@angular/compiler-cli": "^21.1.3", - "@angular/core": "^21.1.3", - "@angular/elements": "^21.1.3", - "@angular/forms": "^21.1.3", - "@angular/platform-browser": "^21.1.3", - "@angular/platform-browser-dynamic": "^21.1.3", - "@angular/router": "^21.1.3", + "@angular/animations": "^21.1.4", + "@angular/cdk": "^21.1.4", + "@angular/cli": "^21.1.4", + "@angular/common": "^21.1.4", + "@angular/compiler": "^21.1.4", + "@angular/compiler-cli": "^21.1.4", + "@angular/core": "^21.1.4", + "@angular/elements": "^21.1.4", + "@angular/forms": "^21.1.4", + "@angular/platform-browser": "^21.1.4", + "@angular/platform-browser-dynamic": "^21.1.4", + "@angular/router": "^21.1.4", "@appsignal/javascript": "^1.6.1", "@appsignal/plugin-breadcrumbs-console": "^1.1.37", "@appsignal/plugin-breadcrumbs-network": "^1.1.24", @@ -130,13 +130,13 @@ }, "devDependencies": { "@angular-builders/custom-esbuild": "^21.0.3", - "@angular-devkit/build-angular": "^21.1.3", + "@angular-devkit/build-angular": "^21.1.4", "@angular-eslint/builder": "20.7.0", "@angular-eslint/eslint-plugin": "20.7.0", "@angular-eslint/eslint-plugin-template": "20.7.0", "@angular-eslint/schematics": "20.7.0", "@angular-eslint/template-parser": "20.7.0", - "@angular/language-service": "21.1.3", + "@angular/language-service": "21.1.4", "@eslint/js": "^9.39.2", "@html-eslint/eslint-plugin": "^0.54.2", "@html-eslint/parser": "^0.54.0", @@ -648,16 +648,16 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-21.1.3.tgz", - "integrity": "sha512-02mA04tz9UshwPTv8lBkLcMPpMFh7YnAMXM6u0fL558rU7UrBxsm3XfMmDao3f+jT8umA1mDHBx9OW9LIF4Ewg==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-21.1.4.tgz", + "integrity": "sha512-2HPCo6vEu5EIwxxFYhnmdfbktRBoOVQD3q7lG9PMQPf/jRCnyIZ70qSbXbAV96IMDLFl8mLRfY4scoaFMIYGMw==", "dev": true, "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2101.3", - "@angular-devkit/build-webpack": "0.2101.3", - "@angular-devkit/core": "21.1.3", - "@angular/build": "21.1.3", + "@angular-devkit/architect": "0.2101.4", + "@angular-devkit/build-webpack": "0.2101.4", + "@angular-devkit/core": "21.1.4", + "@angular/build": "21.1.4", "@babel/core": "7.28.5", "@babel/generator": "7.28.5", "@babel/helper-annotate-as-pure": "7.27.3", @@ -668,7 +668,7 @@ "@babel/preset-env": "7.28.5", "@babel/runtime": "7.28.4", "@discoveryjs/json-ext": "0.6.3", - "@ngtools/webpack": "21.1.3", + "@ngtools/webpack": "21.1.4", "ansi-colors": "4.1.3", "autoprefixer": "10.4.23", "babel-loader": "10.0.0", @@ -723,7 +723,7 @@ "@angular/platform-browser": "^21.0.0", "@angular/platform-server": "^21.0.0", "@angular/service-worker": "^21.0.0", - "@angular/ssr": "^21.1.3", + "@angular/ssr": "^21.1.4", "@web/test-runner": "^0.20.0", "browser-sync": "^3.0.2", "jest": "^30.2.0", @@ -780,12 +780,12 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": { - "version": "0.2101.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.3.tgz", - "integrity": "sha512-vKz8aPA62W+e9+pF6ct4CRDG/MjlIH7sWFGYkxPPRst2g46ZQsRkrzfMZAWv/wnt6OZ1OwyRuO3RW83EMhag8g==", + "version": "0.2101.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", + "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "21.1.3", + "@angular-devkit/core": "21.1.4", "rxjs": "7.8.2" }, "bin": { @@ -798,9 +798,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.3.tgz", - "integrity": "sha512-huEXd1tWQHwwN+0VGRT+vSVplV0KNrGFUGJzkIW6iJE1SQElxn6etMai+pSd5DJcePkx6+SuscVsxbfwf70hnA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", + "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", "dev": true, "dependencies": { "ajv": "8.17.1", @@ -1046,12 +1046,12 @@ } }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.2101.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.2101.3.tgz", - "integrity": "sha512-M2o79NbnrjKC78DBdPcJ/ZDSvTi1rpvWBhAa0TN/HZhW33xf9pkYCBOfHIowv+m/tPA1KqL7Ww3qNhRmzId6yg==", + "version": "0.2101.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.2101.4.tgz", + "integrity": "sha512-lPjPxeEzUha4bnlGzD3KFFf3yxcQjOfV9wwZIa4XLsqjCZsUk95TzHQH7i64OCTw9uKTEQkJBAuO6v2WXHxopw==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.2101.3", + "@angular-devkit/architect": "0.2101.4", "rxjs": "7.8.2" }, "engines": { @@ -1065,12 +1065,12 @@ } }, "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": { - "version": "0.2101.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.3.tgz", - "integrity": "sha512-vKz8aPA62W+e9+pF6ct4CRDG/MjlIH7sWFGYkxPPRst2g46ZQsRkrzfMZAWv/wnt6OZ1OwyRuO3RW83EMhag8g==", + "version": "0.2101.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", + "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "21.1.3", + "@angular-devkit/core": "21.1.4", "rxjs": "7.8.2" }, "bin": { @@ -1083,9 +1083,9 @@ } }, "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/core": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.3.tgz", - "integrity": "sha512-huEXd1tWQHwwN+0VGRT+vSVplV0KNrGFUGJzkIW6iJE1SQElxn6etMai+pSd5DJcePkx6+SuscVsxbfwf70hnA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", + "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", "dev": true, "dependencies": { "ajv": "8.17.1", @@ -1373,9 +1373,9 @@ "license": "MIT" }, "node_modules/@angular/animations": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-21.1.3.tgz", - "integrity": "sha512-UADMncDd9lkmIT1NPVFcufyP5gJHMPzxNaQpojiGrxT1aT8Du30mao0KSrB4aTwcicv6/cdD5bZbIyg+FL6LkQ==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-21.1.4.tgz", + "integrity": "sha512-8xQ0Ylw7qqVyw4ZJ/Tyw/z5Mtqtp8AMj+R+Z1sCWcyxBgDU4+qfxteVYdiipWC3tX77A0FTsXqwvNP9WVY2/WA==", "dependencies": { "tslib": "^2.3.0" }, @@ -1383,17 +1383,17 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/core": "21.1.3" + "@angular/core": "21.1.4" } }, "node_modules/@angular/build": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-21.1.3.tgz", - "integrity": "sha512-RXVRuamfrSPwsFCLJgsO2ucp+dwWDbGbhXrQnMrGXm0qdgYpI8bAsBMd8wOeUA6vn4fRmjaRFQZbL/rcIVrkzw==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-21.1.4.tgz", + "integrity": "sha512-7CAAQPWFMMqod40ox5MOVB/CnoBXFDehyQhs0hls6lu7bOy/M0EDy0v6bERkyNGRz1mihWWBiCV8XzEinrlq1A==", "dev": true, "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2101.3", + "@angular-devkit/architect": "0.2101.4", "@babel/core": "7.28.5", "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-split-export-declaration": "7.24.7", @@ -1436,7 +1436,7 @@ "@angular/platform-browser": "^21.0.0", "@angular/platform-server": "^21.0.0", "@angular/service-worker": "^21.0.0", - "@angular/ssr": "^21.1.3", + "@angular/ssr": "^21.1.4", "karma": "^6.4.0", "less": "^4.2.0", "ng-packagr": "^21.0.0", @@ -1486,12 +1486,12 @@ } }, "node_modules/@angular/build/node_modules/@angular-devkit/architect": { - "version": "0.2101.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.3.tgz", - "integrity": "sha512-vKz8aPA62W+e9+pF6ct4CRDG/MjlIH7sWFGYkxPPRst2g46ZQsRkrzfMZAWv/wnt6OZ1OwyRuO3RW83EMhag8g==", + "version": "0.2101.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", + "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "21.1.3", + "@angular-devkit/core": "21.1.4", "rxjs": "7.8.2" }, "bin": { @@ -1504,9 +1504,9 @@ } }, "node_modules/@angular/build/node_modules/@angular-devkit/core": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.3.tgz", - "integrity": "sha512-huEXd1tWQHwwN+0VGRT+vSVplV0KNrGFUGJzkIW6iJE1SQElxn6etMai+pSd5DJcePkx6+SuscVsxbfwf70hnA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", + "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", "dev": true, "dependencies": { "ajv": "8.17.1", @@ -1587,9 +1587,9 @@ } }, "node_modules/@angular/cdk": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-21.1.3.tgz", - "integrity": "sha512-jMiEKCcZMIAnyx2jxrJHmw5c7JXAiN56ErZ4X+OuQ5yFvYRocRVEs25I0OMxntcXNdPTJQvpGwGlhWhS0yDorg==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-21.1.4.tgz", + "integrity": "sha512-PElA4Ww4TIa3+B/ND+fm8ZPDKONTIqc9a/s0qNxhcAD9IpDqjaBVi/fyg+ZWBtS+x0DQgJtKeCsSZ6sr2aFQaQ==", "dependencies": { "parse5": "^8.0.0", "tslib": "^2.3.0" @@ -1626,17 +1626,17 @@ } }, "node_modules/@angular/cli": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.1.3.tgz", - "integrity": "sha512-UPtDcpKyrKZRPfym9gTovcibPzl2O/Woy7B8sm45sAnjDH+jDUCcCvuIak7GpH47shQkC2J4yvnHZbD4c6XxcQ==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.1.4.tgz", + "integrity": "sha512-XsMHgxTvHGiXXrhYZz3zMZYhYU0gHdpoHKGiEKXwcx+S1KoYbIssyg6oF2Kq49ZaE0OYCTKjnvgDce6ZqdkJ/A==", "dependencies": { - "@angular-devkit/architect": "0.2101.3", - "@angular-devkit/core": "21.1.3", - "@angular-devkit/schematics": "21.1.3", + "@angular-devkit/architect": "0.2101.4", + "@angular-devkit/core": "21.1.4", + "@angular-devkit/schematics": "21.1.4", "@inquirer/prompts": "7.10.1", "@listr2/prompt-adapter-inquirer": "3.0.5", "@modelcontextprotocol/sdk": "1.26.0", - "@schematics/angular": "21.1.3", + "@schematics/angular": "21.1.4", "@yarnpkg/lockfile": "1.1.0", "algoliasearch": "5.46.2", "ini": "6.0.0", @@ -1660,11 +1660,11 @@ } }, "node_modules/@angular/cli/node_modules/@angular-devkit/architect": { - "version": "0.2101.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.3.tgz", - "integrity": "sha512-vKz8aPA62W+e9+pF6ct4CRDG/MjlIH7sWFGYkxPPRst2g46ZQsRkrzfMZAWv/wnt6OZ1OwyRuO3RW83EMhag8g==", + "version": "0.2101.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", + "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", "dependencies": { - "@angular-devkit/core": "21.1.3", + "@angular-devkit/core": "21.1.4", "rxjs": "7.8.2" }, "bin": { @@ -1677,9 +1677,9 @@ } }, "node_modules/@angular/cli/node_modules/@angular-devkit/core": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.3.tgz", - "integrity": "sha512-huEXd1tWQHwwN+0VGRT+vSVplV0KNrGFUGJzkIW6iJE1SQElxn6etMai+pSd5DJcePkx6+SuscVsxbfwf70hnA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", + "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", "dependencies": { "ajv": "8.17.1", "ajv-formats": "3.0.1", @@ -1703,11 +1703,11 @@ } }, "node_modules/@angular/cli/node_modules/@angular-devkit/schematics": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.3.tgz", - "integrity": "sha512-Ps7bRl5uOcM7WpNJHbSls/jz5/wAI0ldkTlKyiBFA7RtNeQIABAV+hvlw5DJuEb1Lo5hnK0hXj90AyZdOxzY+w==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.4.tgz", + "integrity": "sha512-Nqq0ioCUxrbEX+L4KOarETcZZJNnJ1mAJ0ubO4VM91qnn8RBBM9SnQ91590TfC34Szk/wh+3+Uj6KUvTJNuegQ==", "dependencies": { - "@angular-devkit/core": "21.1.3", + "@angular-devkit/core": "21.1.4", "jsonc-parser": "3.3.1", "magic-string": "0.30.21", "ora": "9.0.0", @@ -1847,12 +1847,12 @@ } }, "node_modules/@angular/cli/node_modules/ora/node_modules/string-width": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.1.tgz", - "integrity": "sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz", + "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", "dependencies": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" }, "engines": { "node": ">=20" @@ -1948,9 +1948,9 @@ } }, "node_modules/@angular/common": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.1.3.tgz", - "integrity": "sha512-Wdbln/UqZM5oVnpfIydRdhhL8A9x3bKZ9Zy1/mM0q+qFSftPvmFZIXhEpFqbDwNYbGUhGzx7t8iULC4sVVp/zA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.1.4.tgz", + "integrity": "sha512-1uOxPrHO9PFZBU/JavzYzjxAm+5x7vD2z6AeUndqdT4LjqOBIePswxFDRqM9dlfF8FIwnnfmNFipiC/yQjJSnA==", "dependencies": { "tslib": "^2.3.0" }, @@ -1958,14 +1958,14 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/core": "21.1.3", + "@angular/core": "21.1.4", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.1.3.tgz", - "integrity": "sha512-gDNLh7MEf7Qf88ktZzS4LJQXCA5U8aQTfK9ak+0mi2ruZ0x4XSjQCro4H6OPKrrbq94+6GcnlSX5+oVIajEY3w==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.1.4.tgz", + "integrity": "sha512-H0qtASeqOTaS44ioF4DYE/yNqwzUmEJpMYrcNEUFEwA20/DkLzyoaEx4y1CjIxtXxuhtge95PNymDBOLWSjIdg==", "dependencies": { "tslib": "^2.3.0" }, @@ -1974,9 +1974,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.1.3.tgz", - "integrity": "sha512-nKxoQ89W2B1WdonNQ9kgRnvLNS6DAxDrRHBslsKTlV+kbdv7h59M9PjT4ZZ2sp1M/M8LiofnUfa/s2jd/xYj5w==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.1.4.tgz", + "integrity": "sha512-Uw8KmpVCo58/f5wf6pY8ZS5fodv65hn5jxms8Nv/K7/LVe3i1nNFrHyneBx5+a7qkz93nSV4rdwBVnMvjIyr+g==", "dependencies": { "@babel/core": "7.28.5", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -1995,7 +1995,7 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/compiler": "21.1.3", + "@angular/compiler": "21.1.4", "typescript": ">=5.9 <6.0" }, "peerDependenciesMeta": { @@ -2152,9 +2152,9 @@ } }, "node_modules/@angular/core": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.1.3.tgz", - "integrity": "sha512-TbhQxRC7Lb/3WBdm1n8KRsktmVEuGBBp0WRF5mq0Ze4s1YewIM6cULrSw9ACtcL5jdcq7c74ms+uKQsaP/gdcQ==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.1.4.tgz", + "integrity": "sha512-QBDO5SaVYTVQ0fIN9Qd7US9cUCgs2vM9x6K18PTUKmygIkHVHTFdzwm4MO5gpCOFzJseGbS+dNzqn+v0PaKf9g==", "dependencies": { "tslib": "^2.3.0" }, @@ -2162,7 +2162,7 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/compiler": "21.1.3", + "@angular/compiler": "21.1.4", "rxjs": "^6.5.3 || ^7.4.0", "zone.js": "~0.15.0 || ~0.16.0" }, @@ -2176,9 +2176,9 @@ } }, "node_modules/@angular/elements": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/elements/-/elements-21.1.3.tgz", - "integrity": "sha512-nuXv4Nzmfl/m7d8shDCpSt7v1uKqWBj9QMNLpR8pzqa6I9cVyvT5fXVA0OF74b+3n8tzVActxcqtH+I8avt08A==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/elements/-/elements-21.1.4.tgz", + "integrity": "sha512-OJTxfzdh77LeoFiAbW/s38Kks4HTEyeJTxMzZqA3aWF8ZCa5DNai6aB2F20QbhJdrr/NAITjlDf8gM4c2XJgxw==", "dependencies": { "tslib": "^2.3.0" }, @@ -2186,14 +2186,14 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/core": "21.1.3", + "@angular/core": "21.1.4", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/forms": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.1.3.tgz", - "integrity": "sha512-YW/YdjM9suZUeJam9agHFXIEE3qQIhGYXMjnnX7xGjOe+CuR2R0qsWn1AR0yrKrNmFspb0lKgM7kTTJyzt8gZg==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.1.4.tgz", + "integrity": "sha512-duVT/eOncmFNBYRlK/F7WDg6GD1vL1mxUrDdnp7M9J8JvNrKH0PvdfzuOAmjbB8/bsvUNTLFXCV4+43Mc2Hqsw==", "dependencies": { "@standard-schema/spec": "^1.0.0", "tslib": "^2.3.0" @@ -2202,25 +2202,25 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "21.1.3", - "@angular/core": "21.1.3", - "@angular/platform-browser": "21.1.3", + "@angular/common": "21.1.4", + "@angular/core": "21.1.4", + "@angular/platform-browser": "21.1.4", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/language-service": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-21.1.3.tgz", - "integrity": "sha512-i7iMIMt2rbCDXRuVULbi0I5v4a7ldBgoGdPvHQ17poohTjU4NJ2Jm7p7mUYCGcDlYmWOvgxMGaoiqUs6S5lFPA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-21.1.4.tgz", + "integrity": "sha512-E0OKcbYMJPaWlDsz4clPoFJRCgpWBSmMZtgzd4Py3C6yxTyPCZYgA43UyzUDiQI7rHHjD5V6d5EvocgSq6uBfQ==", "dev": true, "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/@angular/platform-browser": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.1.3.tgz", - "integrity": "sha512-W+ZMXAioaP7CsACafBCHsIxiiKrRTPOlQ+hcC7XNBwy+bn5mjGONoCgLreQs76M8HNWLtr/OAUAr6h26OguOuA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.1.4.tgz", + "integrity": "sha512-S6Iw5CkORih5omh+MQY35w0bUBxdSFAPLDg386S6/9fEUjDClo61hvXNKxaNh9g7tnh1LD7zmTmKrqufnhnFDQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -2228,9 +2228,9 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/animations": "21.1.3", - "@angular/common": "21.1.3", - "@angular/core": "21.1.3" + "@angular/animations": "21.1.4", + "@angular/common": "21.1.4", + "@angular/core": "21.1.4" }, "peerDependenciesMeta": { "@angular/animations": { @@ -2239,9 +2239,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-21.1.3.tgz", - "integrity": "sha512-wWEjrNtJfxzZmbDWdJSyRau7NWpQ6IFM9QAyn7xH3cQDGCj+Gy9lTU5sUIYQc+7sx3nKWztolc7h/M5meYCTAg==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-21.1.4.tgz", + "integrity": "sha512-lThgNDFHPQyrx0liNX3x8wHcgp1sd/Dym19zm1PSQ67k6J4snwxZFhNlwFHVr1K86XvX/vilyeR2edPLe9lF3Q==", "dependencies": { "tslib": "^2.3.0" }, @@ -2249,16 +2249,16 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "21.1.3", - "@angular/compiler": "21.1.3", - "@angular/core": "21.1.3", - "@angular/platform-browser": "21.1.3" + "@angular/common": "21.1.4", + "@angular/compiler": "21.1.4", + "@angular/core": "21.1.4", + "@angular/platform-browser": "21.1.4" } }, "node_modules/@angular/router": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.1.3.tgz", - "integrity": "sha512-uAw4LAMHXAPCe4SywhlUEWjMYVbbLHwTxLyduSp1b+9aVwep0juy5O/Xttlxd/oigVe0NMnOyJG9y1Br/ubnrg==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.1.4.tgz", + "integrity": "sha512-nPYuRJ8ub/X8GK55U2KqYy/ducVRn6HSoDmZz0yiXtI6haFsZlv9R1j5zi0EDIqrrN0HGARMs6jNDXZC1Ded3w==", "dependencies": { "tslib": "^2.3.0" }, @@ -2266,9 +2266,9 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "21.1.3", - "@angular/core": "21.1.3", - "@angular/platform-browser": "21.1.3", + "@angular/common": "21.1.4", + "@angular/core": "21.1.4", + "@angular/platform-browser": "21.1.4", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -5679,25 +5679,6 @@ } } }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", - "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -7065,9 +7046,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-21.1.3.tgz", - "integrity": "sha512-Un4dHHELxuFwlSfjsHlmw73col+t0NID2hhx1JPRmKXBXAd4nDRJKX2LPouQLL0FFF+gOtA4mxabf5NruDTQNg==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-21.1.4.tgz", + "integrity": "sha512-CgKnMofIVGTwNPqFNZmkmr2aLOFUG/AKm8lauXU+juwSaY7Z28eguFd+J42uVUOnasLxINQY9y7kr9f6deTrcg==", "dev": true, "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0", @@ -8364,12 +8345,12 @@ "dev": true }, "node_modules/@schematics/angular": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.1.3.tgz", - "integrity": "sha512-obJvWBhzRdsYL2msM4+8bQD21vFl3VxaVsuiq6iIfYsxhU5i2Iar2wM9NaRaIIqAYhZ8ehQQ/moB9BEbWvDCTw==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.1.4.tgz", + "integrity": "sha512-I1zdSNzdbrVCWpeE2NsZQmIoa9m0nlw4INgdGIkqUH6FgwvoGKC0RoOxKAmm6HHVJ48FE/sPI13dwAeK89ow5A==", "dependencies": { - "@angular-devkit/core": "21.1.3", - "@angular-devkit/schematics": "21.1.3", + "@angular-devkit/core": "21.1.4", + "@angular-devkit/schematics": "21.1.4", "jsonc-parser": "3.3.1" }, "engines": { @@ -8379,9 +8360,9 @@ } }, "node_modules/@schematics/angular/node_modules/@angular-devkit/core": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.3.tgz", - "integrity": "sha512-huEXd1tWQHwwN+0VGRT+vSVplV0KNrGFUGJzkIW6iJE1SQElxn6etMai+pSd5DJcePkx6+SuscVsxbfwf70hnA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", + "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", "dependencies": { "ajv": "8.17.1", "ajv-formats": "3.0.1", @@ -8405,11 +8386,11 @@ } }, "node_modules/@schematics/angular/node_modules/@angular-devkit/schematics": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.3.tgz", - "integrity": "sha512-Ps7bRl5uOcM7WpNJHbSls/jz5/wAI0ldkTlKyiBFA7RtNeQIABAV+hvlw5DJuEb1Lo5hnK0hXj90AyZdOxzY+w==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.4.tgz", + "integrity": "sha512-Nqq0ioCUxrbEX+L4KOarETcZZJNnJ1mAJ0ubO4VM91qnn8RBBM9SnQ91590TfC34Szk/wh+3+Uj6KUvTJNuegQ==", "dependencies": { - "@angular-devkit/core": "21.1.3", + "@angular-devkit/core": "21.1.4", "jsonc-parser": "3.3.1", "magic-string": "0.30.21", "ora": "9.0.0", @@ -8527,12 +8508,12 @@ } }, "node_modules/@schematics/angular/node_modules/string-width": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.1.tgz", - "integrity": "sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz", + "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", "dependencies": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" }, "engines": { "node": ">=20" @@ -9050,16 +9031,34 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@tufjs/models/node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", - "license": "BlueOak-1.0.0", + "node_modules/@tufjs/models/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "balanced-match": "^4.0.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", + "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -9706,13 +9705,25 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { @@ -9754,12 +9765,12 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", + "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -10038,13 +10049,25 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/type-utils/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/type-utils/node_modules/debug": { @@ -10077,12 +10100,12 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", + "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -10131,13 +10154,25 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { @@ -10158,12 +10193,12 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", + "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -10306,13 +10341,25 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/utils/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/utils/node_modules/debug": { @@ -10345,12 +10392,12 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", + "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -14712,9 +14759,9 @@ } }, "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.7.tgz", + "integrity": "sha512-FjiwU9HaHW6YB3H4a1sFudnv93lvydNjz2lmyUXR6IwKhGI+bgL3SOZrBGn6kvvX2pJvhEkGSGjyTHN47O4rqA==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -15002,10 +15049,9 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", - "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", - "license": "MIT", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", "engines": { "node": ">=18" }, @@ -15128,6 +15174,25 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/glob/node_modules/lru-cache": { "version": "11.2.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.1.tgz", @@ -15137,14 +15202,14 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", + "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "brace-expansion": "^5.0.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -15614,9 +15679,9 @@ } }, "node_modules/hono": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.9.tgz", - "integrity": "sha512-Eaw2YTGM6WOxA6CXbckaEvslr2Ne4NFsKrvc0v97JD5awbmeBLO5w9Ho9L9kmKonrwF9RJlW6BxT1PVv/agBHQ==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.0.tgz", + "integrity": "sha512-NekXntS5M94pUfiVZ8oXXK/kkri+5WpX2/Ik+LVsl+uvw+soj4roXIsPqO+XsWrAw20mOzaXOZf3Q7PfB9A/IA==", "engines": { "node": ">=16.9.0" } @@ -16020,16 +16085,34 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/ignore-walk/node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", - "license": "BlueOak-1.0.0", + "node_modules/ignore-walk/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "balanced-match": "^4.0.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", + "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -19192,9 +19275,9 @@ "dev": true }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", + "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -21162,10 +21245,9 @@ "integrity": "sha512-C0cqfbS1P5hfqN4NhsYsUXePlk9BO+a45bAQ3xLYjBL3bOIFzoVEjs79Fado9u9BPBD3buHi3+vY+C8tHh4qMQ==" }, "node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", - "license": "BSD-3-Clause", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "dependencies": { "side-channel": "^1.1.0" }, @@ -24222,13 +24304,25 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/typescript-eslint/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/typescript-eslint/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/typescript-eslint/node_modules/debug": { @@ -24270,12 +24364,12 @@ } }, "node_modules/typescript-eslint/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", + "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -26040,16 +26134,16 @@ } }, "@angular-devkit/build-angular": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-21.1.3.tgz", - "integrity": "sha512-02mA04tz9UshwPTv8lBkLcMPpMFh7YnAMXM6u0fL558rU7UrBxsm3XfMmDao3f+jT8umA1mDHBx9OW9LIF4Ewg==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-21.1.4.tgz", + "integrity": "sha512-2HPCo6vEu5EIwxxFYhnmdfbktRBoOVQD3q7lG9PMQPf/jRCnyIZ70qSbXbAV96IMDLFl8mLRfY4scoaFMIYGMw==", "dev": true, "requires": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2101.3", - "@angular-devkit/build-webpack": "0.2101.3", - "@angular-devkit/core": "21.1.3", - "@angular/build": "21.1.3", + "@angular-devkit/architect": "0.2101.4", + "@angular-devkit/build-webpack": "0.2101.4", + "@angular-devkit/core": "21.1.4", + "@angular/build": "21.1.4", "@babel/core": "7.28.5", "@babel/generator": "7.28.5", "@babel/helper-annotate-as-pure": "7.27.3", @@ -26060,7 +26154,7 @@ "@babel/preset-env": "7.28.5", "@babel/runtime": "7.28.4", "@discoveryjs/json-ext": "0.6.3", - "@ngtools/webpack": "21.1.3", + "@ngtools/webpack": "21.1.4", "ansi-colors": "4.1.3", "autoprefixer": "10.4.23", "babel-loader": "10.0.0", @@ -26103,19 +26197,19 @@ }, "dependencies": { "@angular-devkit/architect": { - "version": "0.2101.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.3.tgz", - "integrity": "sha512-vKz8aPA62W+e9+pF6ct4CRDG/MjlIH7sWFGYkxPPRst2g46ZQsRkrzfMZAWv/wnt6OZ1OwyRuO3RW83EMhag8g==", + "version": "0.2101.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", + "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", "dev": true, "requires": { - "@angular-devkit/core": "21.1.3", + "@angular-devkit/core": "21.1.4", "rxjs": "7.8.2" } }, "@angular-devkit/core": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.3.tgz", - "integrity": "sha512-huEXd1tWQHwwN+0VGRT+vSVplV0KNrGFUGJzkIW6iJE1SQElxn6etMai+pSd5DJcePkx6+SuscVsxbfwf70hnA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", + "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", "dev": true, "requires": { "ajv": "8.17.1", @@ -26260,29 +26354,29 @@ } }, "@angular-devkit/build-webpack": { - "version": "0.2101.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.2101.3.tgz", - "integrity": "sha512-M2o79NbnrjKC78DBdPcJ/ZDSvTi1rpvWBhAa0TN/HZhW33xf9pkYCBOfHIowv+m/tPA1KqL7Ww3qNhRmzId6yg==", + "version": "0.2101.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.2101.4.tgz", + "integrity": "sha512-lPjPxeEzUha4bnlGzD3KFFf3yxcQjOfV9wwZIa4XLsqjCZsUk95TzHQH7i64OCTw9uKTEQkJBAuO6v2WXHxopw==", "dev": true, "requires": { - "@angular-devkit/architect": "0.2101.3", + "@angular-devkit/architect": "0.2101.4", "rxjs": "7.8.2" }, "dependencies": { "@angular-devkit/architect": { - "version": "0.2101.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.3.tgz", - "integrity": "sha512-vKz8aPA62W+e9+pF6ct4CRDG/MjlIH7sWFGYkxPPRst2g46ZQsRkrzfMZAWv/wnt6OZ1OwyRuO3RW83EMhag8g==", + "version": "0.2101.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", + "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", "dev": true, "requires": { - "@angular-devkit/core": "21.1.3", + "@angular-devkit/core": "21.1.4", "rxjs": "7.8.2" } }, "@angular-devkit/core": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.3.tgz", - "integrity": "sha512-huEXd1tWQHwwN+0VGRT+vSVplV0KNrGFUGJzkIW6iJE1SQElxn6etMai+pSd5DJcePkx6+SuscVsxbfwf70hnA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", + "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", "dev": true, "requires": { "ajv": "8.17.1", @@ -26478,21 +26572,21 @@ } }, "@angular/animations": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-21.1.3.tgz", - "integrity": "sha512-UADMncDd9lkmIT1NPVFcufyP5gJHMPzxNaQpojiGrxT1aT8Du30mao0KSrB4aTwcicv6/cdD5bZbIyg+FL6LkQ==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-21.1.4.tgz", + "integrity": "sha512-8xQ0Ylw7qqVyw4ZJ/Tyw/z5Mtqtp8AMj+R+Z1sCWcyxBgDU4+qfxteVYdiipWC3tX77A0FTsXqwvNP9WVY2/WA==", "requires": { "tslib": "^2.3.0" } }, "@angular/build": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-21.1.3.tgz", - "integrity": "sha512-RXVRuamfrSPwsFCLJgsO2ucp+dwWDbGbhXrQnMrGXm0qdgYpI8bAsBMd8wOeUA6vn4fRmjaRFQZbL/rcIVrkzw==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-21.1.4.tgz", + "integrity": "sha512-7CAAQPWFMMqod40ox5MOVB/CnoBXFDehyQhs0hls6lu7bOy/M0EDy0v6bERkyNGRz1mihWWBiCV8XzEinrlq1A==", "dev": true, "requires": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2101.3", + "@angular-devkit/architect": "0.2101.4", "@babel/core": "7.28.5", "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-split-export-declaration": "7.24.7", @@ -26522,19 +26616,19 @@ }, "dependencies": { "@angular-devkit/architect": { - "version": "0.2101.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.3.tgz", - "integrity": "sha512-vKz8aPA62W+e9+pF6ct4CRDG/MjlIH7sWFGYkxPPRst2g46ZQsRkrzfMZAWv/wnt6OZ1OwyRuO3RW83EMhag8g==", + "version": "0.2101.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", + "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", "dev": true, "requires": { - "@angular-devkit/core": "21.1.3", + "@angular-devkit/core": "21.1.4", "rxjs": "7.8.2" } }, "@angular-devkit/core": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.3.tgz", - "integrity": "sha512-huEXd1tWQHwwN+0VGRT+vSVplV0KNrGFUGJzkIW6iJE1SQElxn6etMai+pSd5DJcePkx6+SuscVsxbfwf70hnA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", + "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", "dev": true, "requires": { "ajv": "8.17.1", @@ -26585,9 +26679,9 @@ } }, "@angular/cdk": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-21.1.3.tgz", - "integrity": "sha512-jMiEKCcZMIAnyx2jxrJHmw5c7JXAiN56ErZ4X+OuQ5yFvYRocRVEs25I0OMxntcXNdPTJQvpGwGlhWhS0yDorg==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-21.1.4.tgz", + "integrity": "sha512-PElA4Ww4TIa3+B/ND+fm8ZPDKONTIqc9a/s0qNxhcAD9IpDqjaBVi/fyg+ZWBtS+x0DQgJtKeCsSZ6sr2aFQaQ==", "requires": { "parse5": "^8.0.0", "tslib": "^2.3.0" @@ -26609,17 +26703,17 @@ } }, "@angular/cli": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.1.3.tgz", - "integrity": "sha512-UPtDcpKyrKZRPfym9gTovcibPzl2O/Woy7B8sm45sAnjDH+jDUCcCvuIak7GpH47shQkC2J4yvnHZbD4c6XxcQ==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.1.4.tgz", + "integrity": "sha512-XsMHgxTvHGiXXrhYZz3zMZYhYU0gHdpoHKGiEKXwcx+S1KoYbIssyg6oF2Kq49ZaE0OYCTKjnvgDce6ZqdkJ/A==", "requires": { - "@angular-devkit/architect": "0.2101.3", - "@angular-devkit/core": "21.1.3", - "@angular-devkit/schematics": "21.1.3", + "@angular-devkit/architect": "0.2101.4", + "@angular-devkit/core": "21.1.4", + "@angular-devkit/schematics": "21.1.4", "@inquirer/prompts": "7.10.1", "@listr2/prompt-adapter-inquirer": "3.0.5", "@modelcontextprotocol/sdk": "1.26.0", - "@schematics/angular": "21.1.3", + "@schematics/angular": "21.1.4", "@yarnpkg/lockfile": "1.1.0", "algoliasearch": "5.46.2", "ini": "6.0.0", @@ -26635,18 +26729,18 @@ }, "dependencies": { "@angular-devkit/architect": { - "version": "0.2101.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.3.tgz", - "integrity": "sha512-vKz8aPA62W+e9+pF6ct4CRDG/MjlIH7sWFGYkxPPRst2g46ZQsRkrzfMZAWv/wnt6OZ1OwyRuO3RW83EMhag8g==", + "version": "0.2101.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", + "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", "requires": { - "@angular-devkit/core": "21.1.3", + "@angular-devkit/core": "21.1.4", "rxjs": "7.8.2" } }, "@angular-devkit/core": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.3.tgz", - "integrity": "sha512-huEXd1tWQHwwN+0VGRT+vSVplV0KNrGFUGJzkIW6iJE1SQElxn6etMai+pSd5DJcePkx6+SuscVsxbfwf70hnA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", + "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", "requires": { "ajv": "8.17.1", "ajv-formats": "3.0.1", @@ -26657,11 +26751,11 @@ } }, "@angular-devkit/schematics": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.3.tgz", - "integrity": "sha512-Ps7bRl5uOcM7WpNJHbSls/jz5/wAI0ldkTlKyiBFA7RtNeQIABAV+hvlw5DJuEb1Lo5hnK0hXj90AyZdOxzY+w==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.4.tgz", + "integrity": "sha512-Nqq0ioCUxrbEX+L4KOarETcZZJNnJ1mAJ0ubO4VM91qnn8RBBM9SnQ91590TfC34Szk/wh+3+Uj6KUvTJNuegQ==", "requires": { - "@angular-devkit/core": "21.1.3", + "@angular-devkit/core": "21.1.4", "jsonc-parser": "3.3.1", "magic-string": "0.30.21", "ora": "9.0.0", @@ -26745,12 +26839,12 @@ }, "dependencies": { "string-width": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.1.tgz", - "integrity": "sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz", + "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", "requires": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" } } } @@ -26809,25 +26903,25 @@ } }, "@angular/common": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.1.3.tgz", - "integrity": "sha512-Wdbln/UqZM5oVnpfIydRdhhL8A9x3bKZ9Zy1/mM0q+qFSftPvmFZIXhEpFqbDwNYbGUhGzx7t8iULC4sVVp/zA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.1.4.tgz", + "integrity": "sha512-1uOxPrHO9PFZBU/JavzYzjxAm+5x7vD2z6AeUndqdT4LjqOBIePswxFDRqM9dlfF8FIwnnfmNFipiC/yQjJSnA==", "requires": { "tslib": "^2.3.0" } }, "@angular/compiler": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.1.3.tgz", - "integrity": "sha512-gDNLh7MEf7Qf88ktZzS4LJQXCA5U8aQTfK9ak+0mi2ruZ0x4XSjQCro4H6OPKrrbq94+6GcnlSX5+oVIajEY3w==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.1.4.tgz", + "integrity": "sha512-H0qtASeqOTaS44ioF4DYE/yNqwzUmEJpMYrcNEUFEwA20/DkLzyoaEx4y1CjIxtXxuhtge95PNymDBOLWSjIdg==", "requires": { "tslib": "^2.3.0" } }, "@angular/compiler-cli": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.1.3.tgz", - "integrity": "sha512-nKxoQ89W2B1WdonNQ9kgRnvLNS6DAxDrRHBslsKTlV+kbdv7h59M9PjT4ZZ2sp1M/M8LiofnUfa/s2jd/xYj5w==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.1.4.tgz", + "integrity": "sha512-Uw8KmpVCo58/f5wf6pY8ZS5fodv65hn5jxms8Nv/K7/LVe3i1nNFrHyneBx5+a7qkz93nSV4rdwBVnMvjIyr+g==", "requires": { "@babel/core": "7.28.5", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -26926,56 +27020,56 @@ } }, "@angular/core": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.1.3.tgz", - "integrity": "sha512-TbhQxRC7Lb/3WBdm1n8KRsktmVEuGBBp0WRF5mq0Ze4s1YewIM6cULrSw9ACtcL5jdcq7c74ms+uKQsaP/gdcQ==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.1.4.tgz", + "integrity": "sha512-QBDO5SaVYTVQ0fIN9Qd7US9cUCgs2vM9x6K18PTUKmygIkHVHTFdzwm4MO5gpCOFzJseGbS+dNzqn+v0PaKf9g==", "requires": { "tslib": "^2.3.0" } }, "@angular/elements": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/elements/-/elements-21.1.3.tgz", - "integrity": "sha512-nuXv4Nzmfl/m7d8shDCpSt7v1uKqWBj9QMNLpR8pzqa6I9cVyvT5fXVA0OF74b+3n8tzVActxcqtH+I8avt08A==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/elements/-/elements-21.1.4.tgz", + "integrity": "sha512-OJTxfzdh77LeoFiAbW/s38Kks4HTEyeJTxMzZqA3aWF8ZCa5DNai6aB2F20QbhJdrr/NAITjlDf8gM4c2XJgxw==", "requires": { "tslib": "^2.3.0" } }, "@angular/forms": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.1.3.tgz", - "integrity": "sha512-YW/YdjM9suZUeJam9agHFXIEE3qQIhGYXMjnnX7xGjOe+CuR2R0qsWn1AR0yrKrNmFspb0lKgM7kTTJyzt8gZg==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.1.4.tgz", + "integrity": "sha512-duVT/eOncmFNBYRlK/F7WDg6GD1vL1mxUrDdnp7M9J8JvNrKH0PvdfzuOAmjbB8/bsvUNTLFXCV4+43Mc2Hqsw==", "requires": { "@standard-schema/spec": "^1.0.0", "tslib": "^2.3.0" } }, "@angular/language-service": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-21.1.3.tgz", - "integrity": "sha512-i7iMIMt2rbCDXRuVULbi0I5v4a7ldBgoGdPvHQ17poohTjU4NJ2Jm7p7mUYCGcDlYmWOvgxMGaoiqUs6S5lFPA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-21.1.4.tgz", + "integrity": "sha512-E0OKcbYMJPaWlDsz4clPoFJRCgpWBSmMZtgzd4Py3C6yxTyPCZYgA43UyzUDiQI7rHHjD5V6d5EvocgSq6uBfQ==", "dev": true }, "@angular/platform-browser": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.1.3.tgz", - "integrity": "sha512-W+ZMXAioaP7CsACafBCHsIxiiKrRTPOlQ+hcC7XNBwy+bn5mjGONoCgLreQs76M8HNWLtr/OAUAr6h26OguOuA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.1.4.tgz", + "integrity": "sha512-S6Iw5CkORih5omh+MQY35w0bUBxdSFAPLDg386S6/9fEUjDClo61hvXNKxaNh9g7tnh1LD7zmTmKrqufnhnFDQ==", "requires": { "tslib": "^2.3.0" } }, "@angular/platform-browser-dynamic": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-21.1.3.tgz", - "integrity": "sha512-wWEjrNtJfxzZmbDWdJSyRau7NWpQ6IFM9QAyn7xH3cQDGCj+Gy9lTU5sUIYQc+7sx3nKWztolc7h/M5meYCTAg==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-21.1.4.tgz", + "integrity": "sha512-lThgNDFHPQyrx0liNX3x8wHcgp1sd/Dym19zm1PSQ67k6J4snwxZFhNlwFHVr1K86XvX/vilyeR2edPLe9lF3Q==", "requires": { "tslib": "^2.3.0" } }, "@angular/router": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.1.3.tgz", - "integrity": "sha512-uAw4LAMHXAPCe4SywhlUEWjMYVbbLHwTxLyduSp1b+9aVwep0juy5O/Xttlxd/oigVe0NMnOyJG9y1Br/ubnrg==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.1.4.tgz", + "integrity": "sha512-nPYuRJ8ub/X8GK55U2KqYy/ducVRn6HSoDmZz0yiXtI6haFsZlv9R1j5zi0EDIqrrN0HGARMs6jNDXZC1Ded3w==", "requires": { "tslib": "^2.3.0" } @@ -29113,19 +29207,6 @@ "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==" }, - "@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==" - }, - "@isaacs/brace-expansion": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", - "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", - "requires": { - "@isaacs/balanced-match": "^4.0.1" - } - }, "@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -29960,9 +30041,9 @@ } }, "@ngtools/webpack": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-21.1.3.tgz", - "integrity": "sha512-Un4dHHELxuFwlSfjsHlmw73col+t0NID2hhx1JPRmKXBXAd4nDRJKX2LPouQLL0FFF+gOtA4mxabf5NruDTQNg==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-21.1.4.tgz", + "integrity": "sha512-CgKnMofIVGTwNPqFNZmkmr2aLOFUG/AKm8lauXU+juwSaY7Z28eguFd+J42uVUOnasLxINQY9y7kr9f6deTrcg==", "dev": true }, "@npmcli/agent": { @@ -30654,19 +30735,19 @@ "dev": true }, "@schematics/angular": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.1.3.tgz", - "integrity": "sha512-obJvWBhzRdsYL2msM4+8bQD21vFl3VxaVsuiq6iIfYsxhU5i2Iar2wM9NaRaIIqAYhZ8ehQQ/moB9BEbWvDCTw==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.1.4.tgz", + "integrity": "sha512-I1zdSNzdbrVCWpeE2NsZQmIoa9m0nlw4INgdGIkqUH6FgwvoGKC0RoOxKAmm6HHVJ48FE/sPI13dwAeK89ow5A==", "requires": { - "@angular-devkit/core": "21.1.3", - "@angular-devkit/schematics": "21.1.3", + "@angular-devkit/core": "21.1.4", + "@angular-devkit/schematics": "21.1.4", "jsonc-parser": "3.3.1" }, "dependencies": { "@angular-devkit/core": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.3.tgz", - "integrity": "sha512-huEXd1tWQHwwN+0VGRT+vSVplV0KNrGFUGJzkIW6iJE1SQElxn6etMai+pSd5DJcePkx6+SuscVsxbfwf70hnA==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", + "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", "requires": { "ajv": "8.17.1", "ajv-formats": "3.0.1", @@ -30677,11 +30758,11 @@ } }, "@angular-devkit/schematics": { - "version": "21.1.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.3.tgz", - "integrity": "sha512-Ps7bRl5uOcM7WpNJHbSls/jz5/wAI0ldkTlKyiBFA7RtNeQIABAV+hvlw5DJuEb1Lo5hnK0hXj90AyZdOxzY+w==", + "version": "21.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.4.tgz", + "integrity": "sha512-Nqq0ioCUxrbEX+L4KOarETcZZJNnJ1mAJ0ubO4VM91qnn8RBBM9SnQ91590TfC34Szk/wh+3+Uj6KUvTJNuegQ==", "requires": { - "@angular-devkit/core": "21.1.3", + "@angular-devkit/core": "21.1.4", "jsonc-parser": "3.3.1", "magic-string": "0.30.21", "ora": "9.0.0", @@ -30750,12 +30831,12 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==" }, "string-width": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.1.tgz", - "integrity": "sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz", + "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", "requires": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" } }, "strip-ansi": { @@ -31048,12 +31129,25 @@ "minimatch": "^10.1.1" }, "dependencies": { - "minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==" + }, + "brace-expansion": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "requires": { - "@isaacs/brace-expansion": "^5.0.0" + "balanced-match": "^4.0.2" + } + }, + "minimatch": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", + "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "requires": { + "brace-expansion": "^5.0.2" } } } @@ -31605,13 +31699,19 @@ "eslint-visitor-keys": "^4.2.1" } }, + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, "brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "requires": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" } }, "debug": { @@ -31636,12 +31736,12 @@ "dev": true }, "minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", + "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", "dev": true, "requires": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" } } } @@ -31793,13 +31893,19 @@ "eslint-visitor-keys": "^4.2.1" } }, + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, "brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "requires": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" } }, "debug": { @@ -31818,12 +31924,12 @@ "dev": true }, "minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", + "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", "dev": true, "requires": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" } } } @@ -31851,13 +31957,19 @@ "ts-api-utils": "^2.4.0" }, "dependencies": { + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, "brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "requires": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" } }, "debug": { @@ -31870,12 +31982,12 @@ } }, "minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", + "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", "dev": true, "requires": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" } } } @@ -31952,13 +32064,19 @@ "eslint-visitor-keys": "^4.2.1" } }, + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, "brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "requires": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" } }, "debug": { @@ -31977,12 +32095,12 @@ "dev": true }, "minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", + "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", "dev": true, "requires": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" } } } @@ -35087,9 +35205,9 @@ } }, "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.7.tgz", + "integrity": "sha512-FjiwU9HaHW6YB3H4a1sFudnv93lvydNjz2lmyUXR6IwKhGI+bgL3SOZrBGn6kvvX2pJvhEkGSGjyTHN47O4rqA==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -35287,9 +35405,9 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-east-asian-width": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", - "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==" }, "get-intrinsic": { "version": "1.3.0", @@ -35343,17 +35461,30 @@ "path-scurry": "^2.0.0" }, "dependencies": { + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==" + }, + "brace-expansion": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "requires": { + "balanced-match": "^4.0.2" + } + }, "lru-cache": { "version": "11.2.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.1.tgz", "integrity": "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==" }, "minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", + "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", "requires": { - "@isaacs/brace-expansion": "^5.0.0" + "brace-expansion": "^5.0.2" } }, "path-scurry": { @@ -35693,9 +35824,9 @@ } }, "hono": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.9.tgz", - "integrity": "sha512-Eaw2YTGM6WOxA6CXbckaEvslr2Ne4NFsKrvc0v97JD5awbmeBLO5w9Ho9L9kmKonrwF9RJlW6BxT1PVv/agBHQ==" + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.0.tgz", + "integrity": "sha512-NekXntS5M94pUfiVZ8oXXK/kkri+5WpX2/Ik+LVsl+uvw+soj4roXIsPqO+XsWrAw20mOzaXOZf3Q7PfB9A/IA==" }, "hosted-git-info": { "version": "9.0.2", @@ -35976,12 +36107,25 @@ "minimatch": "^10.0.3" }, "dependencies": { - "minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==" + }, + "brace-expansion": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "requires": { - "@isaacs/brace-expansion": "^5.0.0" + "balanced-match": "^4.0.2" + } + }, + "minimatch": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", + "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "requires": { + "brace-expansion": "^5.0.2" } } } @@ -38097,9 +38241,9 @@ "dev": true }, "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", + "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -39468,9 +39612,9 @@ "integrity": "sha512-C0cqfbS1P5hfqN4NhsYsUXePlk9BO+a45bAQ3xLYjBL3bOIFzoVEjs79Fado9u9BPBD3buHi3+vY+C8tHh4qMQ==" }, "qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "requires": { "side-channel": "^1.1.0" } @@ -41542,13 +41686,19 @@ "eslint-visitor-keys": "^4.2.1" } }, + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, "brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "requires": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" } }, "debug": { @@ -41573,12 +41723,12 @@ "dev": true }, "minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", + "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", "dev": true, "requires": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" } } } diff --git a/frontend/package.json b/frontend/package.json index 20c3c97e6df..d402501607d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,13 +6,13 @@ "private": true, "devDependencies": { "@angular-builders/custom-esbuild": "^21.0.3", - "@angular-devkit/build-angular": "^21.1.3", + "@angular-devkit/build-angular": "^21.1.4", "@angular-eslint/builder": "20.7.0", "@angular-eslint/eslint-plugin": "20.7.0", "@angular-eslint/eslint-plugin-template": "20.7.0", "@angular-eslint/schematics": "20.7.0", "@angular-eslint/template-parser": "20.7.0", - "@angular/language-service": "21.1.3", + "@angular/language-service": "21.1.4", "@eslint/js": "^9.39.2", "@html-eslint/eslint-plugin": "^0.54.2", "@html-eslint/parser": "^0.54.0", @@ -64,18 +64,18 @@ "wscat": "^6.1.0" }, "dependencies": { - "@angular/animations": "^21.1.3", - "@angular/cdk": "^21.1.3", - "@angular/cli": "^21.1.3", - "@angular/common": "^21.1.3", - "@angular/compiler": "^21.1.3", - "@angular/compiler-cli": "^21.1.3", - "@angular/core": "^21.1.3", - "@angular/elements": "^21.1.3", - "@angular/forms": "^21.1.3", - "@angular/platform-browser": "^21.1.3", - "@angular/platform-browser-dynamic": "^21.1.3", - "@angular/router": "^21.1.3", + "@angular/animations": "^21.1.4", + "@angular/cdk": "^21.1.4", + "@angular/cli": "^21.1.4", + "@angular/common": "^21.1.4", + "@angular/compiler": "^21.1.4", + "@angular/compiler-cli": "^21.1.4", + "@angular/core": "^21.1.4", + "@angular/elements": "^21.1.4", + "@angular/forms": "^21.1.4", + "@angular/platform-browser": "^21.1.4", + "@angular/platform-browser-dynamic": "^21.1.4", + "@angular/router": "^21.1.4", "@appsignal/javascript": "^1.6.1", "@appsignal/plugin-breadcrumbs-console": "^1.1.37", "@appsignal/plugin-breadcrumbs-network": "^1.1.24", 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 805893d881a..8e47165da89 100644 --- a/frontend/src/app/shared/components/grids/grid/grid.component.html +++ b/frontend/src/app/shared/components/grids/grid/grid.component.html @@ -3,7 +3,7 @@ [style.grid-template-rows]="gridRowStyle"> - @for (area of layout.widgetAreas; track identifyGridArea($index, area)) { + @for (area of widgetAreasForDisplay; track identifyGridArea($index, area)) {
+ (a.startRow - 1) * this.layout.numColumns + a.startColumn; + + const key = (a:GridWidgetArea) => + (a.widget?.id ?? a.guid).toString(); + + return [...(this.layout.widgetAreas || [])].sort((a, b) => { + const diff = index(a) - index(b); + return diff !== 0 ? diff : key(a).localeCompare(key(b)); + }); + } + public identifyGridArea(index:number, area:GridArea) { return area.guid; } diff --git a/frontend/src/app/shared/components/grids/widgets/header/header.component.html b/frontend/src/app/shared/components/grids/widgets/header/header.component.html index 1d0db85e286..913865975d8 100644 --- a/frontend/src/app/shared/components/grids/widgets/header/header.component.html +++ b/frontend/src/app/shared/components/grids/widgets/header/header.component.html @@ -1,20 +1,22 @@ -

- @if (drag.isDraggable) { - - } +
+

+ @if (drag.isDraggable) { + + } - + - + +

-

+
diff --git a/frontend/src/app/shared/components/grids/widgets/header/header.component.sass b/frontend/src/app/shared/components/grids/widgets/header/header.component.sass index 3d1c86389cd..238fa39d9f8 100644 --- a/frontend/src/app/shared/components/grids/widgets/header/header.component.sass +++ b/frontend/src/app/shared/components/grids/widgets/header/header.component.sass @@ -1,3 +1,8 @@ .op-widget-box--header &.-editable margin-top: 0 + + &-title-wrapper + font-size: var(--h4-size, 16px) + display: flex + align-items: center diff --git a/frontend/src/app/shared/components/grids/widgets/menu/widget-abstract-menu.component.ts b/frontend/src/app/shared/components/grids/widgets/menu/widget-abstract-menu.component.ts index 4eb563f5336..d0c2ce5a6a3 100644 --- a/frontend/src/app/shared/components/grids/widgets/menu/widget-abstract-menu.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/menu/widget-abstract-menu.component.ts @@ -64,4 +64,8 @@ export abstract class WidgetAbstractMenuComponent { public get hasMenu() { return this.layout.isEditable; } + + public widgetMenuLabel():string { + return this.i18n.t('js.grid.widget_menu_label', { widgetName: this.resource.options.name }); + } } diff --git a/frontend/src/app/shared/components/grids/widgets/menu/widget-menu.component.html b/frontend/src/app/shared/components/grids/widgets/menu/widget-menu.component.html index 412f988d60b..e0f49b8419d 100644 --- a/frontend/src/app/shared/components/grids/widgets/menu/widget-menu.component.html +++ b/frontend/src/app/shared/components/grids/widgets/menu/widget-menu.component.html @@ -1,3 +1,3 @@ @if (hasMenu) { - + } diff --git a/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.html b/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.html index 7f862a6d9ea..2d2917cff4a 100644 --- a/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.html +++ b/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.html @@ -2,7 +2,8 @@ [name]="widgetName" (onRenamed)="renameWidget($event)"> - diff --git a/frontend/src/app/shared/components/op-context-menu/icon-triggered-context-menu/icon-triggered-context-menu.component.html b/frontend/src/app/shared/components/op-context-menu/icon-triggered-context-menu/icon-triggered-context-menu.component.html index 125cae30673..5c0c6ef87d6 100644 --- a/frontend/src/app/shared/components/op-context-menu/icon-triggered-context-menu/icon-triggered-context-menu.component.html +++ b/frontend/src/app/shared/components/op-context-menu/icon-triggered-context-menu/icon-triggered-context-menu.component.html @@ -1,3 +1,3 @@ - + 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 dd5a4cf30a4..726d030a091 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 @@ -56,6 +56,7 @@ export class IconTriggeredContextMenuComponent extends OpContextMenuTrigger { } @Input() menuItemsFactory:() => Promise; + @Input() customAriaLabel:string = this.I18n.t('js.label_open_menu'); protected async open(evt:Event) { this.items = await this.buildItems(); diff --git a/frontend/src/app/shared/components/op-context-menu/op-context-menu-handler.ts b/frontend/src/app/shared/components/op-context-menu/op-context-menu-handler.ts index d02ac296ccb..429c90b2503 100644 --- a/frontend/src/app/shared/components/op-context-menu/op-context-menu-handler.ts +++ b/frontend/src/app/shared/components/op-context-menu/op-context-menu-handler.ts @@ -23,7 +23,7 @@ export abstract class OpContextMenuHandler extends UntilDestroyedMixin { * * @param focus Focus on the trigger again */ - public onClose(focus = false) { + public onClose(focus = true) { if (focus) { this.afterFocusOn.focus(); } @@ -66,6 +66,12 @@ export abstract class OpContextMenuHandler extends UntilDestroyedMixin { } protected get afterFocusOn():HTMLElement { - return this.element; + const focusableSelector = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'; + + if (this.element.matches(focusableSelector)) { + return this.element; + } + + return this.element.querySelector(focusableSelector) ?? this.element; } } diff --git a/frontend/src/app/shared/components/principal/principal-renderer.service.ts b/frontend/src/app/shared/components/principal/principal-renderer.service.ts index b14532bd98c..cef60201733 100644 --- a/frontend/src/app/shared/components/principal/principal-renderer.service.ts +++ b/frontend/src/app/shared/components/principal/principal-renderer.service.ts @@ -111,7 +111,7 @@ export class PrincipalRendererService { const type = typeFromHref(hrefFromPrincipal(principal))!; if (!avatar.hide) { - const el = this.renderAvatar(principal, avatar, hoverCard, type); + const el = this.renderAvatar(principal, avatar, hoverCard, type, !name.hide); container.appendChild(el); } @@ -127,6 +127,7 @@ export class PrincipalRendererService { options:AvatarOptions, hoverCard:HoverCardOptions, type:PrincipalType, + ariaHidden:boolean, ) { const userInitials = this.getInitials(principal.name); const colorMode = this.colors.colorMode(); @@ -144,6 +145,13 @@ export class PrincipalRendererService { this.setHoverCardAttributes(fallback, hoverCard, principal, type); + if (ariaHidden) { + fallback.setAttribute('aria-hidden', 'true'); + } else { + fallback.setAttribute('role', 'img'); + fallback.setAttribute('aria-label', options.imageAltText ?? principal.name); + } + if (type === 'placeholder_user' && colorMode !== colorModes.lightHighContrast) { fallback.style.color = colorCode; fallback.style.borderColor = colorCode; diff --git a/frontend/src/app/spot/components/drop-modal/drop-modal.component.html b/frontend/src/app/spot/components/drop-modal/drop-modal.component.html index 83fcb83fc35..79b907ef2b0 100644 --- a/frontend/src/app/spot/components/drop-modal/drop-modal.component.html +++ b/frontend/src/app/spot/components/drop-modal/drop-modal.component.html @@ -1,6 +1,7 @@ diff --git a/frontend/src/elements/block-note-element.ts b/frontend/src/elements/block-note-element.ts index e06ef6d448a..b66b8a37af3 100644 --- a/frontend/src/elements/block-note-element.ts +++ b/frontend/src/elements/block-note-element.ts @@ -73,7 +73,10 @@ class BlockNoteElement extends HTMLElement { this.errorContainer.dataset.flashAutohideValue = 'true'; this.editorMount = document.createElement('div'); + + // Copy over definition for external-links handling this.editorMount.dataset.controller = 'external-links'; + this.editorMount.dataset.externalLinksEnabledValue = document.body.dataset.externalLinksEnabledValue; this.stimulusRoot.appendChild(this.errorContainer); this.stimulusRoot.appendChild(this.editorMount); diff --git a/frontend/src/global_styles/content/_grid.sass b/frontend/src/global_styles/content/_grid.sass index 8c150a8e2af..a680629903d 100644 --- a/frontend/src/global_styles/content/_grid.sass +++ b/frontend/src/global_styles/content/_grid.sass @@ -64,6 +64,8 @@ $grid--widget-padding: 20px 20px 20px 20px .op-widget-box--header:has(.grid--area-drag-handle) display: flex + justify-content: space-between + align-items: center padding-left: 0 &.-drop-target, diff --git a/frontend/src/global_styles/content/_pagination.sass b/frontend/src/global_styles/content/_pagination.sass index 8b9874a0ca4..ca6e25fd12f 100644 --- a/frontend/src/global_styles/content/_pagination.sass +++ b/frontend/src/global_styles/content/_pagination.sass @@ -44,6 +44,9 @@ $pagination--font-size: 0.8125rem flex-shrink: 1 margin: 10px 0 0 5px + @media screen and (max-width: $breakpoint-sm) + display: none + &--items list-style-type: none display: flex diff --git a/frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts b/frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts index b810e97c39a..e9039f7336f 100644 --- a/frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts +++ b/frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts @@ -33,65 +33,28 @@ import dragula from 'dragula'; export default class CustomFieldsController extends Controller { static targets = [ - 'format', 'dragContainer', - 'submitButton', 'customOptionDefaults', 'customOptionRow', - - 'allowNonOpenVersions', - 'defaultBool', - 'defaultLongText', - 'defaultText', - 'length', - 'multiSelect', - 'possibleValues', - 'regexp', - 'searchable', - 'textOrientation', - - 'enterpriseBanner', ]; static values = { - formatConfig: Array, - hierarchyEnabled: Boolean, - format: String, + multiSelect: Boolean, }; - declare readonly formatConfigValue:[string, string, string[]][]; - declare readonly formatValue:string; - declare readonly hierarchyEnabledValue:boolean; + declare readonly multiSelectValue:boolean; - declare readonly formatTarget:HTMLInputElement; declare readonly dragContainerTarget:HTMLElement; declare readonly hasDragContainerTarget:boolean; - declare readonly submitButtonTarget:HTMLButtonElement; - declare readonly hasSubmitButtonTarget:boolean; declare readonly customOptionDefaultsTargets:HTMLInputElement[]; declare readonly customOptionRowTargets:HTMLTableRowElement[]; - declare readonly allowNonOpenVersionsTargets:HTMLElement[]; - declare readonly defaultBoolTargets:HTMLElement[]; - declare readonly defaultLongTextTargets:HTMLElement[]; - declare readonly defaultTextTargets:HTMLElement[]; - declare readonly lengthTargets:HTMLElement[]; - declare readonly multiSelectTargets:HTMLElement[]; - declare readonly possibleValuesTargets:HTMLElement[]; - declare readonly regexpTargets:HTMLElement[]; - declare readonly searchableTargets:HTMLInputElement[]; - declare readonly textOrientationTargets:HTMLElement[]; - - declare readonly enterpriseBannerTarget:HTMLElement; - connect() { if (this.hasDragContainerTarget) { this.setupDragAndDrop(); } - - this.adaptInputsToFormat(this.formatValue); } moveRowUp(event:{ target:HTMLElement }) { @@ -183,24 +146,9 @@ export default class CustomFieldsController extends Controller { uncheckOtherDefaults(event:{ target:HTMLElement }) { const cb = event.target as HTMLInputElement; - if (cb.checked) { - const multi = this.multiSelectTargets[0] as HTMLInputElement|undefined; - - if (multi?.checked === false) { - this.customOptionDefaultsTargets.forEach((el) => (el.checked = false)); - cb.checked = true; - } - } - } - - checkOnlyOne(event:{ target:HTMLElement }) { - const cb = event.target as HTMLInputElement; - - if (!cb.checked) { - this.customOptionDefaultsTargets - .filter((el) => el.checked) - .slice(1) - .forEach((el) => (el.checked = false)); + if (cb.checked && !this.multiSelectValue) { + this.customOptionDefaultsTargets.forEach((el) => (el.checked = false)); + cb.checked = true; } } @@ -235,30 +183,4 @@ export default class CustomFieldsController extends Controller { ); }); } - - private setActive(elements:HTMLElement[], active:boolean) { - elements.forEach((element) => { - element.hidden = !active; - element - .querySelectorAll('input, textarea') - .forEach((input) => { - input.disabled = !active; - }); - }); - } - - private adaptInputsToFormat(format:string) { - if (this.hasSubmitButtonTarget) { - this.submitButtonTarget.disabled = format === 'hierarchy' && !this.hierarchyEnabledValue; - } - - this.formatConfigValue.forEach(([targetsName, operator, formats]) => { - const active = operator === 'only' ? formats.includes(format) : !formats.includes(format); - - const targets = this[`${targetsName}Targets` as keyof typeof this] as HTMLElement[]; - if (targets) { - this.setActive(targets, active); - } - }); - } } diff --git a/frontend/src/stimulus/controllers/external-links.controller.ts b/frontend/src/stimulus/controllers/external-links.controller.ts index e7a213ce04d..648b15bd70b 100644 --- a/frontend/src/stimulus/controllers/external-links.controller.ts +++ b/frontend/src/stimulus/controllers/external-links.controller.ts @@ -70,6 +70,12 @@ const shouldProcessLink = (link:HTMLAnchorElement) => { * dynamically loaded content. */ export default class ExternalLinksController extends ApplicationController { + static values = { + enabled: Boolean + }; + + declare readonly enabledValue:boolean; + connect() { useMutation(this, { attributes: true, childList: true, subtree: true, attributeFilter: ['target', 'href'] }); @@ -77,9 +83,9 @@ export default class ExternalLinksController extends ApplicationController { this.element.querySelectorAll(LINK_QUERY).forEach((link)=>{ if (!shouldProcessLink(link)) return; - if (isLinkBlank(link)) updateBlankLink(link); + if (isLinkBlank(link)) this.updateBlankLink(link); - if (isLinkExternal(link)) updateExternalLink(link); + if (isLinkExternal(link)) this.updateExternalLink(link); }); } @@ -89,16 +95,16 @@ export default class ExternalLinksController extends ApplicationController { if (isElement(node)) { // Added element itself is an external link if (isLink(node) && shouldProcessLink(node)) { - if (isLinkBlank(node)) updateBlankLink(node); - if (isLinkExternal(node)) updateExternalLink(node); + if (isLinkBlank(node)) this.updateBlankLink(node); + if (isLinkExternal(node)) this.updateExternalLink(node); } node.querySelectorAll(LINK_QUERY).forEach((link)=>{ if (!shouldProcessLink(link)) return; - if (isLinkBlank(link)) updateBlankLink(link); + if (isLinkBlank(link)) this.updateBlankLink(link); - if (isLinkExternal(link)) updateExternalLink(link); + if (isLinkExternal(link)) this.updateExternalLink(link); }); } }); @@ -110,28 +116,29 @@ export default class ExternalLinksController extends ApplicationController { isLink(mutation.target) && shouldProcessLink(mutation.target) ) { - if (mutation.attributeName === 'target' && isLinkBlank(mutation.target)) updateBlankLink(mutation.target); - if (mutation.attributeName === 'href' && isLinkExternal(mutation.target)) updateExternalLink(mutation.target); + if (mutation.attributeName === 'target' && isLinkBlank(mutation.target)) this.updateBlankLink(mutation.target); + if (mutation.attributeName === 'href' && isLinkExternal(mutation.target)) this.updateExternalLink(mutation.target); } }); } -} -function updateBlankLink(link:HTMLAnchorElement) { - // Ensure accessibility description - attributeTokenList(link, 'aria-describedby').add(BLANK_LINK_DESCRIPTION_ID); -} - -function updateExternalLink(link:HTMLAnchorElement) { - // Ensure external link behavior - link.target = '_blank'; - attributeTokenList(link, 'rel').add('noopener', 'noreferrer'); - - // Capture external links through redirect page - // The backend controller will redirect directly if the feature is disabled - if (!link.dataset.allowExternalLink) { - const originalHref = link.href; - const basePath = window.appBasePath ?? ''; - link.href = `${basePath}/external_redirect?url=${encodeURIComponent(originalHref)}`; + private updateBlankLink(link:HTMLAnchorElement) { + // Ensure accessibility description + attributeTokenList(link, 'aria-describedby').add(BLANK_LINK_DESCRIPTION_ID); } + + private updateExternalLink(link:HTMLAnchorElement) { + // Ensure external link behavior + link.target = '_blank'; + attributeTokenList(link, 'rel').add('noopener', 'noreferrer'); + + // Capture external links through redirect page + // The backend controller will redirect directly if the feature is disabled + if (this.enabledValue && !link.dataset.allowExternalLink) { + const originalHref = link.href; + const basePath = window.appBasePath ?? ''; + link.href = `${basePath}/external_redirect?url=${encodeURIComponent(originalHref)}`; + } + } + } diff --git a/lib/open_project/text_formatting/filters/sanitization_filter.rb b/lib/open_project/text_formatting/filters/sanitization_filter.rb index 26408b49a5e..a84c2017632 100644 --- a/lib/open_project/text_formatting/filters/sanitization_filter.rb +++ b/lib/open_project/text_formatting/filters/sanitization_filter.rb @@ -31,14 +31,24 @@ module OpenProject::TextFormatting module Filters class SanitizationFilter < HTML::Pipeline::SanitizationFilter - def allowlist + # Prefix for all id and name attributes so they cannot clobber document/window + # (e.g. id="constructor" becomes id="op-frag-constructor"). Anchors still work + # because we rewrite fragment links to use the same prefix. Used by + # TableOfContentsFilter when it assigns heading ids. + FRAGMENT_ID_PREFIX = "op-frag-" + + def allowlist # rubocop:disable Metrics/AbcSize base = super + # Ensure id is allowed (for anchors); we make it safe by prefixing in the transformer. + base_attrs = base[:attributes].deep_dup + all_attrs = Array(base_attrs[:all]) + base_attrs[:all] = all_attrs.include?("id") ? all_attrs : all_attrs + ["id"] Sanitize::Config.merge( base, elements: base[:elements] + %w[macro mention], - attributes: base[:attributes].deep_merge( + attributes: base_attrs.deep_merge( # Whitelist class and data-* attributes on all macros "macro" => ["class", :data], # mentions @@ -75,11 +85,52 @@ module OpenProject::TextFormatting def transformers [ + fragment_id_prefix_transformer, + fragment_link_rewrite_transformer, todo_list_transformer, code_block_transformer ] end + # Prefix all id and name attributes so they cannot clobber document/window. + # e.g. id="constructor" -> id="op-frag-constructor"; anchors still work. + def fragment_id_prefix_transformer + prefix = FRAGMENT_ID_PREFIX + lambda { |env| + node = env[:node] + next unless node.element? + + %w[id name].each do |attr| + val = node[attr] + next if val.blank? + next if val.start_with?(prefix) + + node[attr] = "#{prefix}#{val}" + end + } + end + + # Rewrite same-document fragment links to use the same prefix so anchors match. + # e.g. -> href="#op-frag-section" + def fragment_link_rewrite_transformer + prefix = FRAGMENT_ID_PREFIX + lambda { |env| + node = env[:node] + next unless node.name == "a" + + href = node["href"] + return if href.blank? + + # Only rewrite fragment-only links (#foo), not full URLs with fragment + next unless href.start_with?("#") && href.length > 1 + + fragment = href.slice(1..) + next if fragment.empty? || fragment.start_with?(prefix) + + node["href"] = "##{prefix}#{fragment}" + } + end + # Transformer to fix task lists in sanitization # Replace to do lists in tables with their markdown equivalent def todo_list_transformer # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity diff --git a/lib/open_project/text_formatting/filters/table_of_contents_filter.rb b/lib/open_project/text_formatting/filters/table_of_contents_filter.rb index 0516d492b51..01f926b5831 100644 --- a/lib/open_project/text_formatting/filters/table_of_contents_filter.rb +++ b/lib/open_project/text_formatting/filters/table_of_contents_filter.rb @@ -43,8 +43,13 @@ module OpenProject::TextFormatting end def add_header_link_class_and_id(node, id) - node.css("a").first["class"] = "op-uc-link_permalink icon-link" - node["id"] = id + anchor = node.css("a").first + if anchor + anchor["class"] = "op-uc-link_permalink icon-link" + anchor["href"] = "##{fragment_id_prefix}#{id}" + anchor.remove_attribute("id") # avoid duplicate id with heading; only heading keeps the id + end + node["id"] = "#{fragment_id_prefix}#{id}" end ## @@ -134,7 +139,11 @@ module OpenProject::TextFormatting number = parsed_text[1] || number number_span = content_tag(:span, number, class: "op-uc-toc--list-item-number") content_span = content_tag(:span, parsed_text[2].strip, class: "op-uc-toc--list-item-title") - content_tag(:a, number_span + content_span, href: "##{id}", class: "op-uc-toc--item-link") + content_tag(:a, number_span + content_span, href: "##{fragment_id_prefix}#{id}", class: "op-uc-toc--item-link") + end + + def fragment_id_prefix + SanitizationFilter::FRAGMENT_ID_PREFIX end end end diff --git a/modules/backlogs/app/helpers/version_settings_helper.rb b/modules/backlogs/app/helpers/version_settings_helper.rb deleted file mode 100644 index e50c00fb597..00000000000 --- a/modules/backlogs/app/helpers/version_settings_helper.rb +++ /dev/null @@ -1,84 +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. -#++ - -module VersionSettingsHelper - def version_settings_fields(version, project) - setting = version_setting_for_project(version, project) - - content_tag :div, class: "form--field" do - [ - styled_label_tag(name_for_setting_attributes("display"), t(:label_column_in_backlog)), - content_tag(:div, - styled_select_tag(name_for_setting_attributes("display"), - options_for_select(position_display_options, setting.display), container_class: "-xslim"), - class: "form--field-container"), - version_hidden_id_field(setting), - hidden_field_tag("project_id", project.id) - ].join.html_safe - end - end - - private - - def version_hidden_id_field(setting) - return "" unless setting.id - - hidden_field_tag(name_for_setting_attributes("id"), setting.id) - end - - def version_setting_for_project(version, project) - setting = version.version_settings.detect { |vs| vs.project_id == project.id || vs.project_id.nil? } - - # nil? because some settings in the active codebase do have that right now - setting ||= version.version_settings.new(display: VersionSetting::DISPLAY_LEFT, project:) - - setting - end - - def name_for_setting_attributes(attribute) - "version[version_settings_attributes][][#{attribute}]" - end - - def position_display_options - options = [::VersionSetting::DISPLAY_NONE, - ::VersionSetting::DISPLAY_LEFT, - ::VersionSetting::DISPLAY_RIGHT] - options.map { |s| [humanize_display_option(s), s] } - end - - def humanize_display_option(option) - case option - when ::VersionSetting::DISPLAY_NONE - t("version_settings_display_option_none") - when ::VersionSetting::DISPLAY_LEFT - t("version_settings_display_option_left") - when ::VersionSetting::DISPLAY_RIGHT - t("version_settings_display_option_right") - end - end -end diff --git a/modules/backlogs/app/models/agile/sprint.rb b/modules/backlogs/app/models/agile/sprint.rb index dda13f3b4bb..d236ca0bc74 100644 --- a/modules/backlogs/app/models/agile/sprint.rb +++ b/modules/backlogs/app/models/agile/sprint.rb @@ -38,17 +38,27 @@ module Agile belongs_to :project has_many :work_packages, dependent: :nullify - enum :status, { - in_planning: "in_planning", - active: "active", - completed: "completed" - }, default: "in_planning", validate: true + enum :status, + { + in_planning: "in_planning", + active: "active", + completed: "completed" + }, + default: "in_planning", + validate: true - SPRINT_SHARINGS = %w(none descendants system).freeze + enum :sharing, + { + none: "none", + descendants: "descendants", + system: "system" + }, + default: "none", + prefix: :sharing_with, + validate: true validates :name, presence: true validates :project, presence: true - validates :sharing, presence: true, inclusion: { in: SPRINT_SHARINGS } validates :start_date, presence: true validates :finish_date, presence: true, diff --git a/modules/backlogs/config/locales/crowdin/fr.yml b/modules/backlogs/config/locales/crowdin/fr.yml index 1f4fbddd1ca..9d132253119 100644 --- a/modules/backlogs/config/locales/crowdin/fr.yml +++ b/modules/backlogs/config/locales/crowdin/fr.yml @@ -26,7 +26,7 @@ fr: activerecord: attributes: sprint: - duration: "Sprint duration" + duration: "Durée du sprint" work_package: position: "Position" story_points: "Points d'histoire" @@ -46,7 +46,7 @@ fr: task_type: "Type de tâche" backlogs: any: "tout" - column_width: "Column width" + column_width: "Largeur de la colonne" definition_of_done: "Définition de Fait" impediment: "Obstacle" label_versions_default_fold_state: "Afficher les versions de manière repliée" @@ -64,8 +64,8 @@ fr: show_burndown_chart: "Graphique d'avancement" story: "Histoire" story_points: - one: "%{count} story point" - other: "%{count} story points" + one: "%{count} point d'histoire" + other: "%{count} points de l'histoire" task: "Tâche" task_color: "Couleur des tâches" unassigned: "Non assigné" @@ -73,28 +73,28 @@ fr: header_backlogs: "Module des backlogs" button_update_backlogs: "Mettre à jour le module des backlogs" backlog_component: - blankslate_title: "%{name} is empty" - blankslate_description: "No items planned yet. Drag items here to add them." + blankslate_title: "%{name} est vide" + blankslate_description: "Aucun élément n'a encore été planifié. Faites glisser les éléments ici pour les ajouter." backlog_header_component: - label_toggle_backlog: "Collapse/Expand %{name}" + label_toggle_backlog: "Réduire/développer %{name}" label_story_count: - zero: "No stories in backlog" - one: "%{count} story in backlog" - other: "%{count} stories in backlog" + zero: "Pas d'histoires dans le carnet de commandes" + one: "%{count} article en attente" + other: "%{count} histoires en attente" backlog_menu_component: - label_actions: "Backlog actions" + label_actions: "Actions en attente" action_menu: - edit_sprint: "Edit sprint" - new_story: "New story" - stories_tasks: "Stories/Tasks" - task_board: "Task board" - burndown_chart: "Burndown chart" + edit_sprint: "Editer le sprint" + new_story: "Nouvelle story" + stories_tasks: "Histoires/tâches" + task_board: "Tableau des tâches" + burndown_chart: "Graphique d'avancement" wiki: "Wiki" - properties: "Properties" + properties: "Propriétés" story_component: - label_drag_story: "Move %{name}" + label_drag_story: "Déplacer %{name}" story_menu_component: - label_actions: "Story actions" + label_actions: "Actions de l'histoire" backlogs_points_burn_direction: "Les points évoluent vers le haut/bas" backlogs_product_backlog: "Backlog de produit" backlogs_story: "Histoire" @@ -102,14 +102,14 @@ fr: backlogs_task: "Tâche" backlogs_task_type: "Type de tâche" backlogs_wiki_template: "Modèle pour page wiki de sprint" - backlogs_empty_title: "No versions are defined yet" - backlogs_empty_action_text: "To start using backlogs, please create a version first" - backlogs_not_configured_title: "Backlogs not configured" - backlogs_not_configured_description: "Story and task types need to be set before using this module." - backlogs_not_configured_action_text: "Configure Backlogs" + backlogs_empty_title: "Aucune version n'est encore définie" + backlogs_empty_action_text: "Pour commencer à utiliser les backlogs, veuillez d'abord créer une version" + backlogs_not_configured_title: "Les carnets de commandes ne sont pas configurés" + backlogs_not_configured_description: "Les types d'histoires et de tâches doivent être définis avant d'utiliser ce module." + backlogs_not_configured_action_text: "Configurer les carnets de commandes" burndown: - story_points: "Story points" - story_points_ideal: "Story points (ideal)" + story_points: "Points d'histoire" + story_points_ideal: "Points d'histoire (idéal)" errors: attributes: task_type: @@ -129,8 +129,8 @@ fr: project_module_backlogs: "Backlogs" rb_burndown_charts: show: - blankslate_title: "No burndown data available" - blankslate_description: "Set start and end date for the sprint to generate a burndown chart." + blankslate_title: "Pas de données disponibles sur le brûlage" + blankslate_description: "Définissez les dates de début et de fin du sprint afin de générer un tableau d'avancement." remaining_hours: "travail restant" version_settings_display_label: "Colonne dans le backlog" version_settings_display_option_left: "gauche" diff --git a/modules/backlogs/config/locales/crowdin/js-fr.yml b/modules/backlogs/config/locales/crowdin/js-fr.yml index ead159b3f7a..7c1e4c80f3d 100644 --- a/modules/backlogs/config/locales/crowdin/js-fr.yml +++ b/modules/backlogs/config/locales/crowdin/js-fr.yml @@ -25,5 +25,5 @@ fr: properties: storyPoints: "Points d'histoire" burndown: - day: "Day" + day: "Jour" points: "Points" diff --git a/modules/backlogs/config/locales/crowdin/js-he.yml b/modules/backlogs/config/locales/crowdin/js-he.yml index 137d66c0d31..39ba3d85850 100644 --- a/modules/backlogs/config/locales/crowdin/js-he.yml +++ b/modules/backlogs/config/locales/crowdin/js-he.yml @@ -25,5 +25,5 @@ he: properties: storyPoints: "Story Points" burndown: - day: "Day" - points: "Points" + day: "יום" + points: "נקודות" diff --git a/modules/backlogs/config/locales/crowdin/sk.yml b/modules/backlogs/config/locales/crowdin/sk.yml index 8cca23a7bcf..2e9db70d37f 100644 --- a/modules/backlogs/config/locales/crowdin/sk.yml +++ b/modules/backlogs/config/locales/crowdin/sk.yml @@ -26,7 +26,7 @@ sk: activerecord: attributes: sprint: - duration: "Sprint duration" + duration: "Trvanie šprintu" work_package: position: "Pozícia" story_points: "História bodov" @@ -43,10 +43,10 @@ sk: sprint: cannot_end_before_it_starts: "Šprint nemôže byť ukončený ešte pred jeho spustením." attributes: - task_type: "Task type" + task_type: "Typ úlohy" backlogs: any: "akékoľvek" - column_width: "Column width" + column_width: "Šírka stĺpca" definition_of_done: "Definícia pojmu Hotovo" impediment: "Prekážka" label_versions_default_fold_state: "Zobrazenie zložených verzií" @@ -54,10 +54,10 @@ sk: work_package_is_closed: "Pracovný balík je hotový, keď" label_is_done_status: "Status %{status_name} znamená dokončené" points_label: - one: "point" - few: "points" - many: "points" - other: "points" + one: "bod" + few: "bodov" + many: "bodov" + other: "body" positions_could_not_be_rebuilt: "Pozície nemohli byť prestavané." positions_rebuilt_successfully: "Pozície úspešne prestavané." rebuild: "Obnoviť" @@ -77,10 +77,10 @@ sk: header_backlogs: "Modul nevyriešených úloh" button_update_backlogs: "Aktualizácia modulu nevybavených úloh" backlog_component: - blankslate_title: "%{name} is empty" - blankslate_description: "No items planned yet. Drag items here to add them." + blankslate_title: "%{name} je prázdne" + blankslate_description: "Zatiaľ nie sú plánované žiadne položky. Ak chcete pridať položky, potiahnite ich sem." backlog_header_component: - label_toggle_backlog: "Collapse/Expand %{name}" + label_toggle_backlog: "Zbaliť/rozšíriť %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" @@ -91,7 +91,7 @@ sk: edit_sprint: "Edit sprint" new_story: "New story" stories_tasks: "Stories/Tasks" - task_board: "Task board" + task_board: "Tabuľa úloh" burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" @@ -127,8 +127,8 @@ sk: label_sprint_impediments: "Sprint Impediments" label_task_board: "Task board" permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" - permission_select_done_statuses: "Select done statuses" + permission_view_taskboards: "Zobrazenie tabúľ úloh" + permission_select_done_statuses: "Výber stavov hotovo" permission_update_sprints: "Aktualizácia šprinty" project_module_backlogs: "Backlogs" rb_burndown_charts: diff --git a/modules/backlogs/lib/open_project/backlogs/patches/versions_controller_patch.rb b/modules/backlogs/lib/open_project/backlogs/patches/versions_controller_patch.rb index a3cc88e924d..53e29ab9403 100644 --- a/modules/backlogs/lib/open_project/backlogs/patches/versions_controller_patch.rb +++ b/modules/backlogs/lib/open_project/backlogs/patches/versions_controller_patch.rb @@ -29,10 +29,6 @@ module OpenProject::Backlogs::Patches::VersionsControllerPatch def self.included(base) # rubocop:disable Metrics/AbcSize base.class_eval do - include VersionSettingsHelper - - helper :version_settings - before_action :override_project_from_id, only: %i[edit update] append_before_action :add_project_to_version_settings_attributes, only: %i[update create] diff --git a/modules/backlogs/spec/controllers/rb_sprints_controller_permissions_spec.rb b/modules/backlogs/spec/controllers/rb_sprints_controller_permissions_spec.rb new file mode 100644 index 00000000000..e0a372b0f3c --- /dev/null +++ b/modules/backlogs/spec/controllers/rb_sprints_controller_permissions_spec.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. +#++ +require "spec_helper" + +RSpec.describe RbSprintsController, "permissions" do + let(:sprint_project) do + create(:project, enabled_module_names: %w[work_package_tracking backlogs]) + end + let(:sprint) { create(:sprint, project: sprint_project) } + + let(:other_project) do + create(:project, enabled_module_names: %w[work_package_tracking backlogs]).tap do |p| + create(:member, + user: current_user, + roles: [create(:project_role, permissions: [:update_sprints])], + project: p) + end + end + + let(:current_user) { create(:user) } + + before do + allow(Setting).to receive(:plugin_openproject_backlogs) + .and_return({ "story_types" => ["1"], "task_type" => "2" }) + login_as current_user + end + + describe "#update" do + let(:original_name) { sprint.name } + let(:new_name) { "a better name!" } + + context "when the user has access to a different project but not the sprint's project" do + it "does not allow updating the sprint via a foreign project_id" do + original_name # memoize before request + + patch :update, + params: { + project_id: other_project.id, + id: sprint.id, + sprint: { + name: new_name + } + }, + format: :turbo_stream + sprint.reload + + expect(response).to have_http_status(:not_found) + expect(sprint.name).to eq(original_name) + end + end + + context "when the user has access to the sprint's own project" do + before do + create(:member, + user: current_user, + roles: [create(:project_role, permissions: %i[view_work_packages view_versions update_sprints])], + project: sprint_project) + end + + it "allows updating the sprint" do + skip "Incorrect permissions for updating Sprint" + + patch :update, + params: { + project_id: sprint_project.id, + id: sprint.id, + sprint: { + name: new_name + } + }, + format: :turbo_stream + sprint.reload + + expect(sprint.name).to eq(new_name) + end + end + end +end diff --git a/modules/backlogs/spec/models/agile/sprint_spec.rb b/modules/backlogs/spec/models/agile/sprint_spec.rb index bf00b256ce3..ed1fc3b5628 100644 --- a/modules/backlogs/spec/models/agile/sprint_spec.rb +++ b/modules/backlogs/spec/models/agile/sprint_spec.rb @@ -46,7 +46,7 @@ RSpec.describe Agile::Sprint do it { is_expected.to validate_presence_of(:finish_date) } it { is_expected.to validate_presence_of(:project) } it { is_expected.to validate_inclusion_of(:status).in_array(described_class.statuses.keys) } - it { is_expected.to validate_inclusion_of(:sharing).in_array(described_class::SPRINT_SHARINGS) } + it { is_expected.to validate_inclusion_of(:sharing).in_array(described_class.sharings.keys) } it "validates finish_date is after or equal to start_date" do sprint.finish_date = sprint.start_date - 1.day @@ -96,16 +96,15 @@ RSpec.describe Agile::Sprint do end it "status defaults to in_planning" do - expect(sprint.status).to eq("in_planning") + expect(sprint).to be_in_planning end - it "allows sharing settings" do - expect(sprint).to allow_values(*%w[none descendants system]).for(:sharing) - expect(sprint).not_to allow_value(*%w[invalid_value hierarchy tree]).for(:sharing) + it "has sharing enum with correct values" do + expect(described_class.sharings.keys).to contain_exactly("none", "descendants", "system") end it "sharing defaults to none" do - expect(sprint.sharing).to eq("none") + expect(sprint).to be_sharing_with_none end end diff --git a/modules/boards/config/locales/crowdin/ru.yml b/modules/boards/config/locales/crowdin/ru.yml index cdc9073ca42..d88ebaf0e55 100644 --- a/modules/boards/config/locales/crowdin/ru.yml +++ b/modules/boards/config/locales/crowdin/ru.yml @@ -20,7 +20,7 @@ ru: action: "Доска действий (%{attribute})" board_type_attributes: assignee: Исполнитель - status: Kanban + status: Канбан version: Версия subproject: Подпроект subtasks: Родитель-Потомок diff --git a/modules/budgets/config/locales/crowdin/fr.yml b/modules/budgets/config/locales/crowdin/fr.yml index 57e525b8187..bfe73ea2cb1 100644 --- a/modules/budgets/config/locales/crowdin/fr.yml +++ b/modules/budgets/config/locales/crowdin/fr.yml @@ -59,36 +59,36 @@ fr: budgets: widgets: budget_totals: - title: "Budget totals" - remaining_budget: "Remaining budget" - spent_budget: "Spent budget" - total_actual_costs: "Total actual costs" - total_planned_budget: "Total planned budget" + title: "Totaux du budget" + remaining_budget: "Budget restant" + spent_budget: "Budget dépensé" + total_actual_costs: "Total des coûts réels" + total_planned_budget: "Budget total prévu" budget_by_cost_type: - title: "Budget by cost type" + title: "Budget par type de coût" blankslate: - heading: "Start project controlling" - description: "Get an overview of your budgets and costs to efficiently track the health status of your project" + heading: "Démarrer le contrôle du projet" + description: "Obtenez une vue d'ensemble de vos budgets et de vos coûts pour suivre efficacement l'état de santé de votre projet" caption: - zero: "No budget data." - one: "Data aggregated from %{count} budget included in %{portfolios}, %{subprograms} and %{subprojects}." - other: "Data aggregated from %{count} budgets included in %{portfolios}, %{subprograms} and %{subprojects}." + zero: "Pas de données budgétaires." + one: "Données agrégées à partir de %{count} budget inclus dans %{portfolios}, %{subprograms} et %{subprojects}." + other: "Les données agrégées à partir des budgets de %{count} sont incluses dans %{portfolios}, %{subprograms} et %{subprojects}." caption_simple: - one: "Data aggregated from %{count} budget." - other: "Data aggregated from %{count} budgets." + one: "Données agrégées à partir du budget %{count}." + other: "Données agrégées à partir des budgets %{count}." portfolio: - zero: "no portfolios" - one: "1 portfolio" - other: "%{count} portfolios" + zero: "pas de portefeuilles" + one: "1 portefeuille" + other: "%{count} portefeuilles" subprogram: - zero: "no subprograms" - one: "1 subprogram" - other: "%{count} subprograms" + zero: "pas de sous-programmes" + one: "1 sous-programme" + other: "%{count} sous-programmes" subproject: - zero: "no subprojects" - one: "1 subproject" - other: "%{count} subprojects" - view_details: "View budget details" + zero: "pas de sous-projets" + one: "1 sous-projet" + other: "%{count} sous-projets" + view_details: "Voir les détails du budget" events: budget: "Budget modifié" help_click_to_edit: "Cliquez ici pour modifier." diff --git a/modules/budgets/config/locales/crowdin/js-fr.yml b/modules/budgets/config/locales/crowdin/js-fr.yml index 1b801a18326..47c2cf93460 100644 --- a/modules/budgets/config/locales/crowdin/js-fr.yml +++ b/modules/budgets/config/locales/crowdin/js-fr.yml @@ -25,8 +25,8 @@ fr: widgets: budget_by_cost_type: blankslate: - title: "No budget data" - description: "Add planned unit and labor costs to this project to start tracking the budget" + title: "Pas de données budgétaires" + description: "Ajoutez les coûts unitaires et de main-d'œuvre prévus à ce projet pour commencer à suivre le budget" work_packages: properties: costObject: "Budget" diff --git a/modules/budgets/config/locales/crowdin/js-ru.yml b/modules/budgets/config/locales/crowdin/js-ru.yml index 2d48bb8b5e4..a1194b514f8 100644 --- a/modules/budgets/config/locales/crowdin/js-ru.yml +++ b/modules/budgets/config/locales/crowdin/js-ru.yml @@ -25,7 +25,7 @@ ru: widgets: budget_by_cost_type: blankslate: - title: "No budget data" + title: "Нет данных о бюджете" description: "Add planned unit and labor costs to this project to start tracking the budget" work_packages: properties: diff --git a/modules/budgets/config/locales/crowdin/ru.yml b/modules/budgets/config/locales/crowdin/ru.yml index 3c2dee9b485..72cf156d95a 100644 --- a/modules/budgets/config/locales/crowdin/ru.yml +++ b/modules/budgets/config/locales/crowdin/ru.yml @@ -59,18 +59,18 @@ ru: budgets: widgets: budget_totals: - title: "Budget totals" - remaining_budget: "Remaining budget" - spent_budget: "Spent budget" - total_actual_costs: "Total actual costs" - total_planned_budget: "Total planned budget" + title: "Общая сумма бюджета" + remaining_budget: "Оставшийся бюджет" + spent_budget: "Израсходованный бюджет" + total_actual_costs: "Общие фактические затраты" + total_planned_budget: "Общий запланированный бюджет" budget_by_cost_type: - title: "Budget by cost type" + title: "Бюджет по видам затрат" blankslate: heading: "Start project controlling" description: "Get an overview of your budgets and costs to efficiently track the health status of your project" caption: - zero: "No budget data." + zero: "Нет данных о бюджете." one: "Data aggregated from %{count} budget included in %{portfolios}, %{subprograms} and %{subprojects}." other: "Data aggregated from %{count} budgets included in %{portfolios}, %{subprograms} and %{subprojects}." caption_simple: diff --git a/modules/budgets/lib/budgets/engine.rb b/modules/budgets/lib/budgets/engine.rb index 922290cfd34..cae7248270f 100644 --- a/modules/budgets/lib/budgets/engine.rb +++ b/modules/budgets/lib/budgets/engine.rb @@ -43,7 +43,8 @@ module Budgets { budgets: %i[index show edit update destroy destroy_info new create copy] }, - permissible_on: :project + permissible_on: :project, + dependencies: :view_budgets end menu :project_menu, diff --git a/modules/backlogs/spec/helpers/version_settings_helper_spec.rb b/modules/budgets/spec/lib/budgets/permissions_spec.rb similarity index 74% rename from modules/backlogs/spec/helpers/version_settings_helper_spec.rb rename to modules/budgets/spec/lib/budgets/permissions_spec.rb index 33724b427af..b8983f5ccb3 100644 --- a/modules/backlogs/spec/helpers/version_settings_helper_spec.rb +++ b/modules/budgets/spec/lib/budgets/permissions_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + #-- copyright # OpenProject is an open source project management software. # Copyright (C) the OpenProject GmbH @@ -28,14 +30,12 @@ require "spec_helper" -RSpec.describe VersionSettingsHelper do - describe "#position_display_options" do - before do - @expected_options = [[I18n.t("version_settings_display_option_none"), 1], - [I18n.t("version_settings_display_option_left"), 2], - [I18n.t("version_settings_display_option_right"), 3]] - end +RSpec.describe OpenProject::AccessControl, "Budgets module permissions" do # rubocop:disable RSpec/SpecFilePathFormat + describe "edit_budgets" do + subject { described_class.permission(:edit_budgets) } - it { expect(helper.send(:position_display_options)).to eql @expected_options } + it "depends on view_budgets" do + expect(subject.dependencies).to contain_exactly(:view_budgets) + end end end diff --git a/modules/calendar/spec/features/calendar_widget_spec.rb b/modules/calendar/spec/features/calendar_widget_spec.rb index 6c7e8192bbb..30d55346011 100644 --- a/modules/calendar/spec/features/calendar_widget_spec.rb +++ b/modules/calendar/spec/features/calendar_widget_spec.rb @@ -30,7 +30,7 @@ require "spec_helper" require_relative "../../../overviews/spec/support/pages/dashboard" require_relative "../support/pages/calendar" -RSpec.describe "Calendar Widget", :js, with_flag: { new_project_overview: true }, with_settings: { start_of_week: 1 } do +RSpec.describe "Calendar Widget", :js, with_settings: { start_of_week: 1 } do shared_let(:project) do create(:project, enabled_module_names: %w[work_package_tracking calendar_view meetings]) end diff --git a/modules/costs/config/locales/crowdin/fr.yml b/modules/costs/config/locales/crowdin/fr.yml index 19f3a736900..42fd773736e 100644 --- a/modules/costs/config/locales/crowdin/fr.yml +++ b/modules/costs/config/locales/crowdin/fr.yml @@ -229,12 +229,12 @@ fr: costs: widgets: actual_costs: - title: "Actual costs by month" + title: "Coûts réels par mois" blankslate: - heading: "Start tracking your time and costs" - description: "Get an overview of your costs and logged time to monitor progress of your project" - action: "Log time" - view_details: "View actual costs details" + heading: "Commencez à suivre votre temps et vos coûts" + description: "Obtenez une vue d'ensemble de vos coûts et du temps enregistré pour suivre l'avancement de votre projet" + action: "Durée du journal" + view_details: "Voir les détails des coûts réels" ee: features: time_entry_time_restrictions: Exiger un suivi du temps exact diff --git a/modules/costs/config/locales/crowdin/js-fr.yml b/modules/costs/config/locales/crowdin/js-fr.yml index 9fd45917e67..4fd550640cd 100644 --- a/modules/costs/config/locales/crowdin/js-fr.yml +++ b/modules/costs/config/locales/crowdin/js-fr.yml @@ -25,8 +25,8 @@ fr: widgets: actual_costs: blankslate: - title: "No data for current year" - description: "Log spent time and costs for work packages to start tracking actual costs" + title: "Pas de données pour l'année en cours" + description: "Enregistrez le temps passé et les coûts pour les lots de travail afin de commencer à suivre les coûts réels" text_are_you_sure: "Êtes-vous sûr ?" myTimeTracking: noSpecificTime: "Aucune heure spécifique" diff --git a/modules/costs/config/locales/crowdin/js-ru.yml b/modules/costs/config/locales/crowdin/js-ru.yml index 0dc1c7af23f..b96bf4018a2 100644 --- a/modules/costs/config/locales/crowdin/js-ru.yml +++ b/modules/costs/config/locales/crowdin/js-ru.yml @@ -25,7 +25,7 @@ ru: widgets: actual_costs: blankslate: - title: "No data for current year" + title: "Нет данных за текущий год" description: "Log spent time and costs for work packages to start tracking actual costs" text_are_you_sure: "Уверены?" myTimeTracking: diff --git a/modules/costs/config/locales/crowdin/ru.yml b/modules/costs/config/locales/crowdin/ru.yml index 0932947a99d..c54f741649b 100644 --- a/modules/costs/config/locales/crowdin/ru.yml +++ b/modules/costs/config/locales/crowdin/ru.yml @@ -237,11 +237,11 @@ ru: costs: widgets: actual_costs: - title: "Actual costs by month" + title: "Фактические затраты по месяцам" blankslate: heading: "Start tracking your time and costs" description: "Get an overview of your costs and logged time to monitor progress of your project" - action: "Log time" + action: "Добавить трудозатраты" view_details: "View actual costs details" ee: features: diff --git a/modules/documents/config/locales/crowdin/af.yml b/modules/documents/config/locales/crowdin/af.yml index 3aca99e3a26..9299436f15a 100644 --- a/modules/documents/config/locales/crowdin/af.yml +++ b/modules/documents/config/locales/crowdin/af.yml @@ -23,6 +23,8 @@ af: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ af: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/ar.yml b/modules/documents/config/locales/crowdin/ar.yml index ce98c95b070..e850bd18be3 100644 --- a/modules/documents/config/locales/crowdin/ar.yml +++ b/modules/documents/config/locales/crowdin/ar.yml @@ -23,6 +23,8 @@ ar: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -129,7 +131,8 @@ ar: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/az.yml b/modules/documents/config/locales/crowdin/az.yml index c5d3c53ab63..1cb0a9e3452 100644 --- a/modules/documents/config/locales/crowdin/az.yml +++ b/modules/documents/config/locales/crowdin/az.yml @@ -23,6 +23,8 @@ az: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ az: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/be.yml b/modules/documents/config/locales/crowdin/be.yml index b0254b3f2d9..3ced961fd71 100644 --- a/modules/documents/config/locales/crowdin/be.yml +++ b/modules/documents/config/locales/crowdin/be.yml @@ -23,6 +23,8 @@ be: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -125,7 +127,8 @@ be: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/bg.yml b/modules/documents/config/locales/crowdin/bg.yml index d0f3bc4143f..913f0c7282d 100644 --- a/modules/documents/config/locales/crowdin/bg.yml +++ b/modules/documents/config/locales/crowdin/bg.yml @@ -23,6 +23,8 @@ bg: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ bg: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/ca.yml b/modules/documents/config/locales/crowdin/ca.yml index f9f16fa8dcc..3a130c30dce 100644 --- a/modules/documents/config/locales/crowdin/ca.yml +++ b/modules/documents/config/locales/crowdin/ca.yml @@ -23,6 +23,8 @@ ca: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ ca: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/ckb-IR.yml b/modules/documents/config/locales/crowdin/ckb-IR.yml index b83409c8193..dde2c1d1cc3 100644 --- a/modules/documents/config/locales/crowdin/ckb-IR.yml +++ b/modules/documents/config/locales/crowdin/ckb-IR.yml @@ -23,6 +23,8 @@ ckb-IR: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ ckb-IR: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/cs.yml b/modules/documents/config/locales/crowdin/cs.yml index aad9eb9147e..3fe467eb53a 100644 --- a/modules/documents/config/locales/crowdin/cs.yml +++ b/modules/documents/config/locales/crowdin/cs.yml @@ -23,6 +23,8 @@ cs: plugin_openproject_documents: name: "Dokumenty OpenProject" description: "OpenProject plugin umožňující vytváření dokumentů v projektech." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -125,7 +127,8 @@ cs: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/da.yml b/modules/documents/config/locales/crowdin/da.yml index 29876799ff9..10927343abd 100644 --- a/modules/documents/config/locales/crowdin/da.yml +++ b/modules/documents/config/locales/crowdin/da.yml @@ -23,6 +23,8 @@ da: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ da: some_unwritable: Nogle værdier konfigureres via miljøvariabler og kan ikke redigeres her. hocuspocus_server_url: label: "URL til Hocuspocus-server" - caption: "Adressen på en fungerende Hocuspocus-server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Klienthemmelighed" caption: "Indsæt den hemmelighed, der er leveret af Hocuspocus-serveren." diff --git a/modules/documents/config/locales/crowdin/de.yml b/modules/documents/config/locales/crowdin/de.yml index 533c0aab6fc..0a813bec137 100644 --- a/modules/documents/config/locales/crowdin/de.yml +++ b/modules/documents/config/locales/crowdin/de.yml @@ -23,6 +23,8 @@ de: plugin_openproject_documents: name: "OpenProject Dokumente" description: "Ein OpenProject Plugin, um das Erstellen von Dokumenten in Projekten zu ermöglichen." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -120,7 +122,8 @@ de: some_unwritable: Einige Werte werden über Umgebungsvariablen konfiguriert und können hier nicht bearbeitet werden. hocuspocus_server_url: label: "Hokuspokus-Server-URL" - caption: "Die Adresse des aktiven Hocuspocus-Servers." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client-Secret" caption: "Fügen Sie das vom Hocuspocus-Server bereitgestellte Secret ein." diff --git a/modules/documents/config/locales/crowdin/el.yml b/modules/documents/config/locales/crowdin/el.yml index 1f9e601e3e7..3d1e705fa7f 100644 --- a/modules/documents/config/locales/crowdin/el.yml +++ b/modules/documents/config/locales/crowdin/el.yml @@ -23,6 +23,8 @@ el: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ el: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/eo.yml b/modules/documents/config/locales/crowdin/eo.yml index b00a08b6560..02b8e55baf1 100644 --- a/modules/documents/config/locales/crowdin/eo.yml +++ b/modules/documents/config/locales/crowdin/eo.yml @@ -23,6 +23,8 @@ eo: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ eo: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/es.yml b/modules/documents/config/locales/crowdin/es.yml index 93207b0b3fe..94bd1179c66 100644 --- a/modules/documents/config/locales/crowdin/es.yml +++ b/modules/documents/config/locales/crowdin/es.yml @@ -23,6 +23,8 @@ es: plugin_openproject_documents: name: "Documentos de OpenProject" description: "Un plug-in OpenProject para permitir la creación de documentos en proyectos." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -119,7 +121,8 @@ es: some_unwritable: Algunos valores están configurados en las variables de entorno y no pueden ser configurados aquí. hocuspocus_server_url: label: "URL del servidor Hocuspocus" - caption: "La dirección de un servidor Hocuspocus operativo." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Cliente Secreto" caption: "Pega el secreto proporcionado por el servidor Hocuspocus." diff --git a/modules/documents/config/locales/crowdin/et.yml b/modules/documents/config/locales/crowdin/et.yml index cc1528131bb..58fa87819b9 100644 --- a/modules/documents/config/locales/crowdin/et.yml +++ b/modules/documents/config/locales/crowdin/et.yml @@ -23,6 +23,8 @@ et: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ et: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/eu.yml b/modules/documents/config/locales/crowdin/eu.yml index aef3ceba07f..0c1f85d4f70 100644 --- a/modules/documents/config/locales/crowdin/eu.yml +++ b/modules/documents/config/locales/crowdin/eu.yml @@ -23,6 +23,8 @@ eu: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ eu: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/fa.yml b/modules/documents/config/locales/crowdin/fa.yml index dfdbacd70e6..405130fa421 100644 --- a/modules/documents/config/locales/crowdin/fa.yml +++ b/modules/documents/config/locales/crowdin/fa.yml @@ -23,6 +23,8 @@ fa: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ fa: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/fi.yml b/modules/documents/config/locales/crowdin/fi.yml index c06f6f5ffef..61a91e02cce 100644 --- a/modules/documents/config/locales/crowdin/fi.yml +++ b/modules/documents/config/locales/crowdin/fi.yml @@ -23,6 +23,8 @@ fi: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ fi: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/fil.yml b/modules/documents/config/locales/crowdin/fil.yml index 570f939a63a..097f78e623b 100644 --- a/modules/documents/config/locales/crowdin/fil.yml +++ b/modules/documents/config/locales/crowdin/fil.yml @@ -23,6 +23,8 @@ fil: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ fil: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/fr.yml b/modules/documents/config/locales/crowdin/fr.yml index 0d22673922d..1d73a3112c4 100644 --- a/modules/documents/config/locales/crowdin/fr.yml +++ b/modules/documents/config/locales/crowdin/fr.yml @@ -23,6 +23,8 @@ fr: plugin_openproject_documents: name: "Documents OpenProject" description: "Un plugin OpenProject qui permet la création de documents dans les projets." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ fr: some_unwritable: Certaines valeurs sont configurées via des variables d'environnement et ne peuvent pas être modifiées ici. hocuspocus_server_url: label: "URL du serveur Hocuspocus" - caption: "L'adresse d'un serveur Hocuspocus opérationnel." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Secret du client" caption: "Collez le secret fourni par le serveur Hocuspocus." diff --git a/modules/documents/config/locales/crowdin/he.seeders.yml b/modules/documents/config/locales/crowdin/he.seeders.yml index 28f97c19f03..0f310209ed9 100644 --- a/modules/documents/config/locales/crowdin/he.seeders.yml +++ b/modules/documents/config/locales/crowdin/he.seeders.yml @@ -7,14 +7,14 @@ he: common: document_types: item_0: - name: Note + name: הערה item_1: - name: Idea + name: רעיון item_2: - name: Proposal + name: הצעה item_3: - name: Specification + name: מפרט item_4: - name: Report + name: דיווח item_5: - name: Documentation + name: תיעוד diff --git a/modules/documents/config/locales/crowdin/he.yml b/modules/documents/config/locales/crowdin/he.yml index 993205b8e85..0e7d2d57473 100644 --- a/modules/documents/config/locales/crowdin/he.yml +++ b/modules/documents/config/locales/crowdin/he.yml @@ -23,6 +23,8 @@ he: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -125,7 +127,8 @@ he: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/hi.yml b/modules/documents/config/locales/crowdin/hi.yml index 09e7d0a765b..3c509b0950b 100644 --- a/modules/documents/config/locales/crowdin/hi.yml +++ b/modules/documents/config/locales/crowdin/hi.yml @@ -23,6 +23,8 @@ hi: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ hi: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/hr.yml b/modules/documents/config/locales/crowdin/hr.yml index 01e54cb1c05..2e31d3e0ece 100644 --- a/modules/documents/config/locales/crowdin/hr.yml +++ b/modules/documents/config/locales/crowdin/hr.yml @@ -23,6 +23,8 @@ hr: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -123,7 +125,8 @@ hr: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/hu.yml b/modules/documents/config/locales/crowdin/hu.yml index 353ab1ee019..d2c996c8b3d 100644 --- a/modules/documents/config/locales/crowdin/hu.yml +++ b/modules/documents/config/locales/crowdin/hu.yml @@ -23,6 +23,8 @@ hu: plugin_openproject_documents: name: "OpenProject Dokumentumok" description: "Egy OpenProjekt plugin projekten belüli dokumentumok létrehozásához." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ hu: some_unwritable: Egyes értékek környezeti változókon keresztül vannak beállítva, és itt nem szerkeszthetők. hocuspocus_server_url: label: "Hocuspocus szerver URL címe" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Kliens szerver" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/id.yml b/modules/documents/config/locales/crowdin/id.yml index 2f317b0b2fd..35d378a631d 100644 --- a/modules/documents/config/locales/crowdin/id.yml +++ b/modules/documents/config/locales/crowdin/id.yml @@ -23,6 +23,8 @@ id: plugin_openproject_documents: name: "Dokumen OpenProject" description: "Plugin OpenProject untuk memungkinkan pembuatan dokumen dalam proyek." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -119,7 +121,8 @@ id: some_unwritable: Beberapa nilai dikonfigurasi melalui variabel lingkungan dan tidak dapat disunting di sini. hocuspocus_server_url: label: "URL server Hocuspocus" - caption: "Alamat server Hocuspocus yang sedang beroperasi." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Klien rahasia" caption: "Salin kode rahasia yang disediakan oleh server Hocuspocus." diff --git a/modules/documents/config/locales/crowdin/it.yml b/modules/documents/config/locales/crowdin/it.yml index 59927bb626d..a982b822a5a 100644 --- a/modules/documents/config/locales/crowdin/it.yml +++ b/modules/documents/config/locales/crowdin/it.yml @@ -23,6 +23,8 @@ it: plugin_openproject_documents: name: "Documenti OpenProject" description: "Un plugin OpenProject per consentire la creazione di documenti nei progetti." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ it: some_unwritable: Alcuni valori sono configurati tramite variabili di ambiente e non possono essere modificati qui. hocuspocus_server_url: label: "URL del server Hocuspocus" - caption: "L'indirizzo di un server Hocuspocus funzionante." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Segreto client" caption: "Incolla il segreto fornito dal server Hocuspocus." diff --git a/modules/documents/config/locales/crowdin/ja.yml b/modules/documents/config/locales/crowdin/ja.yml index 192d1833de4..0e636797391 100644 --- a/modules/documents/config/locales/crowdin/ja.yml +++ b/modules/documents/config/locales/crowdin/ja.yml @@ -23,6 +23,8 @@ ja: plugin_openproject_documents: name: "OpenProjectドキュメント" description: "プロジェクト内のドキュメントの作成を可能にするOpenProjectプラグイン。" + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -119,7 +121,8 @@ ja: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/ka.yml b/modules/documents/config/locales/crowdin/ka.yml index 5ad9d4ebf60..0abb242f621 100644 --- a/modules/documents/config/locales/crowdin/ka.yml +++ b/modules/documents/config/locales/crowdin/ka.yml @@ -23,6 +23,8 @@ ka: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ ka: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/kk.yml b/modules/documents/config/locales/crowdin/kk.yml index e67a9a5a012..bc0e849eebe 100644 --- a/modules/documents/config/locales/crowdin/kk.yml +++ b/modules/documents/config/locales/crowdin/kk.yml @@ -23,6 +23,8 @@ kk: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ kk: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/ko.yml b/modules/documents/config/locales/crowdin/ko.yml index 5f602721b00..7442a3181e8 100644 --- a/modules/documents/config/locales/crowdin/ko.yml +++ b/modules/documents/config/locales/crowdin/ko.yml @@ -23,6 +23,8 @@ ko: plugin_openproject_documents: name: "OpenProject 문서" description: "프로젝트에서 문서 생성을 허용하는 OpenProject 플러그인입니다." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -119,7 +121,8 @@ ko: some_unwritable: 일부 값은 환경 변수를 통해 구성되며 여기에서 편집할 수 없습니다. hocuspocus_server_url: label: "Hocuspocus 서버 URL" - caption: "작동 중인 Hocuspocus 서버의 주소입니다." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "클라이언트 비밀번호" caption: "Hocuspocus 서버에서 제공한 비밀번호를 붙여넣으세요." diff --git a/modules/documents/config/locales/crowdin/lt.yml b/modules/documents/config/locales/crowdin/lt.yml index 6e7d449cbf5..5fadfe1cfb9 100644 --- a/modules/documents/config/locales/crowdin/lt.yml +++ b/modules/documents/config/locales/crowdin/lt.yml @@ -23,6 +23,8 @@ lt: plugin_openproject_documents: name: "OpenProject dokumentai" description: "OpenProject priedas, leidžiantis kurti dokumentus projektuose." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -125,7 +127,8 @@ lt: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/lv.yml b/modules/documents/config/locales/crowdin/lv.yml index 0bd4ac2db3b..85b45098d30 100644 --- a/modules/documents/config/locales/crowdin/lv.yml +++ b/modules/documents/config/locales/crowdin/lv.yml @@ -23,6 +23,8 @@ lv: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -123,7 +125,8 @@ lv: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/mn.yml b/modules/documents/config/locales/crowdin/mn.yml index 5a78f625502..9d0e5c18fa2 100644 --- a/modules/documents/config/locales/crowdin/mn.yml +++ b/modules/documents/config/locales/crowdin/mn.yml @@ -23,6 +23,8 @@ mn: plugin_openproject_documents: name: "OpenProject баримт бичиг" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ mn: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/ms.yml b/modules/documents/config/locales/crowdin/ms.yml index aa717185d4d..67418f0b962 100644 --- a/modules/documents/config/locales/crowdin/ms.yml +++ b/modules/documents/config/locales/crowdin/ms.yml @@ -23,6 +23,8 @@ ms: plugin_openproject_documents: name: "Dokumen OpenProject" description: "Plugin OpenProject membolehkan penciptaan dokumen dalam projek." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -119,7 +121,8 @@ ms: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/ne.yml b/modules/documents/config/locales/crowdin/ne.yml index b0bf1d14441..18f9df873d3 100644 --- a/modules/documents/config/locales/crowdin/ne.yml +++ b/modules/documents/config/locales/crowdin/ne.yml @@ -23,6 +23,8 @@ ne: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ ne: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/nl.yml b/modules/documents/config/locales/crowdin/nl.yml index ff998c42528..77c2fd5b28b 100644 --- a/modules/documents/config/locales/crowdin/nl.yml +++ b/modules/documents/config/locales/crowdin/nl.yml @@ -23,6 +23,8 @@ nl: plugin_openproject_documents: name: "OpenProject Documenten" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ nl: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/no.yml b/modules/documents/config/locales/crowdin/no.yml index d90636a3eb6..27d6be2500e 100644 --- a/modules/documents/config/locales/crowdin/no.yml +++ b/modules/documents/config/locales/crowdin/no.yml @@ -23,6 +23,8 @@ plugin_openproject_documents: name: "OpenProject Dokumenter" description: "En OpenProject utvidelse for å tillate oppretting av dokumenter i prosjekter." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/pl.yml b/modules/documents/config/locales/crowdin/pl.yml index a2abc34be2f..ff767719afb 100644 --- a/modules/documents/config/locales/crowdin/pl.yml +++ b/modules/documents/config/locales/crowdin/pl.yml @@ -23,6 +23,8 @@ pl: plugin_openproject_documents: name: "Dokumenty OpenProject" description: "Wtyczka OpenProject umożliwiająca tworzenie dokumentów w projektach." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -125,7 +127,8 @@ pl: some_unwritable: Niektóre wartości są konfigurowane za pomocą zmiennych środowiskowych i nie można ich edytować tutaj. hocuspocus_server_url: label: "Adres URL serwera Hocuspocus" - caption: "Adres działającego serwera Hocuspocus." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Klucz tajny klienta" caption: "Wklej klucz tajny dostarczony przez serwer Hocuspocus." diff --git a/modules/documents/config/locales/crowdin/pt-BR.yml b/modules/documents/config/locales/crowdin/pt-BR.yml index 66c193ff7f0..abeb92255d8 100644 --- a/modules/documents/config/locales/crowdin/pt-BR.yml +++ b/modules/documents/config/locales/crowdin/pt-BR.yml @@ -23,6 +23,8 @@ pt-BR: plugin_openproject_documents: name: "Documentos do OpenProject" description: "Um plugin OpenProject para permitir a criação de documentos em projetos." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ pt-BR: some_unwritable: Alguns valores são configurados por meio de variáveis de ambiente e não podem ser editados aqui. hocuspocus_server_url: label: "URL do servidor Hocuspocus" - caption: "O endereço de um servidor Hocuspocus em funcionamento." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Segredo do cliente" caption: "Cole o segredo fornecido pelo servidor Hocuspocus." diff --git a/modules/documents/config/locales/crowdin/pt-PT.yml b/modules/documents/config/locales/crowdin/pt-PT.yml index 8d45826e887..4dfd1bd241f 100644 --- a/modules/documents/config/locales/crowdin/pt-PT.yml +++ b/modules/documents/config/locales/crowdin/pt-PT.yml @@ -23,6 +23,8 @@ pt-PT: plugin_openproject_documents: name: "Documentos do OpenProject" description: "Um plugin OpenProject para permitir a criação de documentos em projetos." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ pt-PT: some_unwritable: Alguns valores são configurados através de variáveis de ambiente e não podem ser editados aqui. hocuspocus_server_url: label: "URL do servidor Hocuspocus" - caption: "O endereço de um servidor Hocuspocus em funcionamento." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Segredo do cliente" caption: "Cole o segredo fornecido pelo servidor Hocuspocus." diff --git a/modules/documents/config/locales/crowdin/ro.yml b/modules/documents/config/locales/crowdin/ro.yml index 1d0169a029f..cd7b73153ff 100644 --- a/modules/documents/config/locales/crowdin/ro.yml +++ b/modules/documents/config/locales/crowdin/ro.yml @@ -23,6 +23,8 @@ ro: plugin_openproject_documents: name: "Documente OpenProject" description: "Un plugin OpenProject pentru a permite crearea de documente în proiecte." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -123,7 +125,8 @@ ro: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Secret Client" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/ru.yml b/modules/documents/config/locales/crowdin/ru.yml index cc752d4c9f6..eb3fbbb3e73 100644 --- a/modules/documents/config/locales/crowdin/ru.yml +++ b/modules/documents/config/locales/crowdin/ru.yml @@ -23,6 +23,8 @@ ru: plugin_openproject_documents: name: "Документы OpenProject" description: "Плагин OpenProject позволяет создавать документы в проектах." + attributes: + collaborative_editing_hocuspocus_url: "URL-адрес сервера Hocuspocus" activerecord: errors: models: @@ -125,7 +127,8 @@ ru: some_unwritable: Некоторые значения настраиваются через переменные окружения и не могут быть отредактированы здесь. hocuspocus_server_url: label: "URL-адрес сервера Hocuspocus" - caption: "Адрес рабочего сервера Hocuspocus." + caption: "Адрес WebSocket работающего сервера Hocuspocus." + invalid_scheme: "Должен использоваться протокол WebSocket (ws:// или wss://)." hocuspocus_server_secret: label: "Закрытый ключ клиента" caption: "Вставьте ключ, предоставленный сервером Hocuspocus." diff --git a/modules/documents/config/locales/crowdin/rw.yml b/modules/documents/config/locales/crowdin/rw.yml index 5509aad7b4b..ee6241b78b0 100644 --- a/modules/documents/config/locales/crowdin/rw.yml +++ b/modules/documents/config/locales/crowdin/rw.yml @@ -23,6 +23,8 @@ rw: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ rw: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/si.yml b/modules/documents/config/locales/crowdin/si.yml index f1bb8ff672f..e392c927f17 100644 --- a/modules/documents/config/locales/crowdin/si.yml +++ b/modules/documents/config/locales/crowdin/si.yml @@ -23,6 +23,8 @@ si: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ si: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/sk.yml b/modules/documents/config/locales/crowdin/sk.yml index 230c33304f3..f97f468336a 100644 --- a/modules/documents/config/locales/crowdin/sk.yml +++ b/modules/documents/config/locales/crowdin/sk.yml @@ -23,6 +23,8 @@ sk: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -125,7 +127,8 @@ sk: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/sl.yml b/modules/documents/config/locales/crowdin/sl.yml index 204cf4cdcd4..d99ccd5277b 100644 --- a/modules/documents/config/locales/crowdin/sl.yml +++ b/modules/documents/config/locales/crowdin/sl.yml @@ -23,6 +23,8 @@ sl: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -125,7 +127,8 @@ sl: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/sr.yml b/modules/documents/config/locales/crowdin/sr.yml index 6ffaf0487ad..fa4bc69c753 100644 --- a/modules/documents/config/locales/crowdin/sr.yml +++ b/modules/documents/config/locales/crowdin/sr.yml @@ -23,6 +23,8 @@ sr: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -123,7 +125,8 @@ sr: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/sv.yml b/modules/documents/config/locales/crowdin/sv.yml index 8cab6b51e42..83b71dfbad5 100644 --- a/modules/documents/config/locales/crowdin/sv.yml +++ b/modules/documents/config/locales/crowdin/sv.yml @@ -23,6 +23,8 @@ sv: plugin_openproject_documents: name: "OpenProject-dokument" description: "Ett OpenProject-plugin som gör det möjligt att skapa dokument i projekt." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ sv: some_unwritable: Vissa värden är konfigurerade via miljövariabler och kan inte redigeras här. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Klienthemlighet" caption: "Klistra in hemligheten som tillhandahålls av Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/th.yml b/modules/documents/config/locales/crowdin/th.yml index 18b49cb300c..598044a926d 100644 --- a/modules/documents/config/locales/crowdin/th.yml +++ b/modules/documents/config/locales/crowdin/th.yml @@ -23,6 +23,8 @@ th: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -119,7 +121,8 @@ th: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/tr.yml b/modules/documents/config/locales/crowdin/tr.yml index a3b0738aad0..ff6e48e16fb 100644 --- a/modules/documents/config/locales/crowdin/tr.yml +++ b/modules/documents/config/locales/crowdin/tr.yml @@ -23,6 +23,8 @@ tr: plugin_openproject_documents: name: "OpenProject Belgeler" description: "Projelerde belge oluşturulmasına izin veren bir OpenProject eklentisi." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ tr: some_unwritable: Bazı değerler ortam değişkenleri aracılığıyla yapılandırılır ve burada düzenlenemez. hocuspocus_server_url: label: "Hocuspocus sunucu URL'si" - caption: "Çalışan bir Hocuspocus sunucusunun adresi." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "İstemci anahtarı" caption: "Hocuspocus sunucusu tarafından sağlanan sırrı yapıştırın." diff --git a/modules/documents/config/locales/crowdin/uk.yml b/modules/documents/config/locales/crowdin/uk.yml index 95fa167a9f7..ad87659461a 100644 --- a/modules/documents/config/locales/crowdin/uk.yml +++ b/modules/documents/config/locales/crowdin/uk.yml @@ -23,6 +23,8 @@ uk: plugin_openproject_documents: name: "Документи OpenProject " description: "Плагін OpenProject дозволяє створювати документи в проектах." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -125,7 +127,8 @@ uk: some_unwritable: Деякі значення налаштовуються за допомогою змінних середовища і не можуть бути відредаговані тут. hocuspocus_server_url: label: "URL-адреса сервера Hocuspocus" - caption: "Адреса робочого сервера Hocuspocus." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Секретний ключ клієнта" caption: "Вставте секретний код, наданий сервером Hocuspocus." diff --git a/modules/documents/config/locales/crowdin/uz.yml b/modules/documents/config/locales/crowdin/uz.yml index 9ae8a2329bc..60fd8ec3745 100644 --- a/modules/documents/config/locales/crowdin/uz.yml +++ b/modules/documents/config/locales/crowdin/uz.yml @@ -23,6 +23,8 @@ uz: plugin_openproject_documents: name: "OpenProject Documents" description: "An OpenProject plugin to allow creation of documents in projects." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -121,7 +123,8 @@ uz: some_unwritable: Some values are configured via environment variables and cannot be edited here. hocuspocus_server_url: label: "Hocuspocus server URL" - caption: "The address of a working Hocuspocus server." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Client secret" caption: "Paste the secret provided by the Hocuspocus server." diff --git a/modules/documents/config/locales/crowdin/vi.yml b/modules/documents/config/locales/crowdin/vi.yml index e68a354e318..eba6ce7889d 100644 --- a/modules/documents/config/locales/crowdin/vi.yml +++ b/modules/documents/config/locales/crowdin/vi.yml @@ -23,6 +23,8 @@ vi: plugin_openproject_documents: name: "Tài liệu dự án mở" description: "Một plugin OpenProject để cho phép tạo tài liệu trong các dự án." + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -119,7 +121,8 @@ vi: some_unwritable: Một số giá trị được định cấu hình thông qua biến môi trường và không thể chỉnh sửa ở đây. hocuspocus_server_url: label: "URL máy chủ Hocuspocus" - caption: "Địa chỉ của máy chủ Hocuspocus đang hoạt động." + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "Bí mật của khách hàng" caption: "Dán bí mật được cung cấp bởi máy chủ Hocuspocus." diff --git a/modules/documents/config/locales/crowdin/zh-CN.yml b/modules/documents/config/locales/crowdin/zh-CN.yml index c48066b2ad6..32a9999c5fe 100644 --- a/modules/documents/config/locales/crowdin/zh-CN.yml +++ b/modules/documents/config/locales/crowdin/zh-CN.yml @@ -23,6 +23,8 @@ zh-CN: plugin_openproject_documents: name: "OpenProject 文档" description: "允许在项目中创建文档的 OpenProject 插件。" + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -119,7 +121,8 @@ zh-CN: some_unwritable: 一些值通过环境变量配置,无法在此处编辑。 hocuspocus_server_url: label: "Hocuspocus 服务器 URL" - caption: "有效 Hocuspocus 服务器的地址。" + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "客户端密钥" caption: "粘贴 Hocuspocus 服务器提供的密钥。" diff --git a/modules/documents/config/locales/crowdin/zh-TW.yml b/modules/documents/config/locales/crowdin/zh-TW.yml index 4f1cc47d152..e455b0d9686 100644 --- a/modules/documents/config/locales/crowdin/zh-TW.yml +++ b/modules/documents/config/locales/crowdin/zh-TW.yml @@ -23,6 +23,8 @@ zh-TW: plugin_openproject_documents: name: "OpenProject 文件" description: "一個允許在 OpenProject 專案中建立文件的外掛。" + attributes: + collaborative_editing_hocuspocus_url: "Hocuspocus server URL" activerecord: errors: models: @@ -119,7 +121,8 @@ zh-TW: some_unwritable: 某些值透過環境變數設定,無法在此編輯。 hocuspocus_server_url: label: "Hocuspocus 伺服器 URL" - caption: "可用的 Hocuspocus 伺服器地址。" + caption: "The WebSocket address of a working Hocuspocus server." + invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." hocuspocus_server_secret: label: "用戶端密碼" caption: "貼上 Hocuspocus 伺服器提供的秘碼。" diff --git a/modules/grids/app/components/grids/project_attribute_widgets.rb b/modules/grids/app/components/grids/project_attribute_widgets.rb index 52d9cf57ac4..53a8e0328b9 100644 --- a/modules/grids/app/components/grids/project_attribute_widgets.rb +++ b/modules/grids/app/components/grids/project_attribute_widgets.rb @@ -50,16 +50,10 @@ module Grids private def available_project_attributes_grouped_by_section - if OpenProject::FeatureDecisions.new_project_overview_active? - @available_project_attributes_grouped_by_section ||= - @project.available_custom_fields - .group_by(&:project_custom_field_section) - .select { |section, _| section.shown_in_overview_main_area? } - else - @available_project_attributes_grouped_by_section ||= - @project.available_custom_fields - .group_by(&:project_custom_field_section) - end + @available_project_attributes_grouped_by_section ||= + @project.available_custom_fields + .group_by(&:project_custom_field_section) + .select { |section, _| section.shown_in_overview_main_area? } end end end diff --git a/modules/grids/config/locales/crowdin/js-af.yml b/modules/grids/config/locales/crowdin/js-af.yml index d033534198f..60d7d3670c7 100644 --- a/modules/grids/config/locales/crowdin/js-af.yml +++ b/modules/grids/config/locales/crowdin/js-af.yml @@ -2,6 +2,7 @@ af: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Verwyder legstuk' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-ar.yml b/modules/grids/config/locales/crowdin/js-ar.yml index f9342345fc0..d0fce6cfc56 100644 --- a/modules/grids/config/locales/crowdin/js-ar.yml +++ b/modules/grids/config/locales/crowdin/js-ar.yml @@ -2,6 +2,7 @@ ar: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'إزالة الأداة' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-az.yml b/modules/grids/config/locales/crowdin/js-az.yml index 5e8a89b984b..75d27c5f35b 100644 --- a/modules/grids/config/locales/crowdin/js-az.yml +++ b/modules/grids/config/locales/crowdin/js-az.yml @@ -2,6 +2,7 @@ az: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remove widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-be.yml b/modules/grids/config/locales/crowdin/js-be.yml index 9984cd0fe6f..22aaf0437f9 100644 --- a/modules/grids/config/locales/crowdin/js-be.yml +++ b/modules/grids/config/locales/crowdin/js-be.yml @@ -2,6 +2,7 @@ be: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remove widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-bg.yml b/modules/grids/config/locales/crowdin/js-bg.yml index 0ed89ac9289..20fb81b7803 100644 --- a/modules/grids/config/locales/crowdin/js-bg.yml +++ b/modules/grids/config/locales/crowdin/js-bg.yml @@ -2,6 +2,7 @@ bg: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Премахване на притурка' configure: 'Конфигурирайте приспособление' widgets: diff --git a/modules/grids/config/locales/crowdin/js-ca.yml b/modules/grids/config/locales/crowdin/js-ca.yml index a9edb654149..322a6d46ff9 100644 --- a/modules/grids/config/locales/crowdin/js-ca.yml +++ b/modules/grids/config/locales/crowdin/js-ca.yml @@ -2,6 +2,7 @@ ca: js: grid: add_widget: 'Afegeix widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Elimina els widgets' configure: 'Configurar widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-ckb-IR.yml b/modules/grids/config/locales/crowdin/js-ckb-IR.yml index 817401785a4..b68ca65e1cd 100644 --- a/modules/grids/config/locales/crowdin/js-ckb-IR.yml +++ b/modules/grids/config/locales/crowdin/js-ckb-IR.yml @@ -2,6 +2,7 @@ ckb-IR: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remove widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-cs.yml b/modules/grids/config/locales/crowdin/js-cs.yml index a49ea2d6612..5e5e03bb660 100644 --- a/modules/grids/config/locales/crowdin/js-cs.yml +++ b/modules/grids/config/locales/crowdin/js-cs.yml @@ -2,6 +2,7 @@ cs: js: grid: add_widget: 'Přidat widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Odstranit widget' configure: 'Konfigurovat widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-da.yml b/modules/grids/config/locales/crowdin/js-da.yml index edfd690c93e..b495f3f0e97 100644 --- a/modules/grids/config/locales/crowdin/js-da.yml +++ b/modules/grids/config/locales/crowdin/js-da.yml @@ -2,6 +2,7 @@ da: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Fjern widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-de.yml b/modules/grids/config/locales/crowdin/js-de.yml index 23a14b669e7..ff582703b09 100644 --- a/modules/grids/config/locales/crowdin/js-de.yml +++ b/modules/grids/config/locales/crowdin/js-de.yml @@ -2,6 +2,7 @@ de: js: grid: add_widget: 'Widget hinzufügen' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Widget entfernen' configure: 'Widget konfigurieren' widgets: diff --git a/modules/grids/config/locales/crowdin/js-el.yml b/modules/grids/config/locales/crowdin/js-el.yml index cf77b283a78..e33d7a973d0 100644 --- a/modules/grids/config/locales/crowdin/js-el.yml +++ b/modules/grids/config/locales/crowdin/js-el.yml @@ -2,6 +2,7 @@ el: js: grid: add_widget: 'Προσθήκη widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Αφαίρεση widget' configure: 'Ρύθμιση widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-eo.yml b/modules/grids/config/locales/crowdin/js-eo.yml index d8bb85f3398..1302d888402 100644 --- a/modules/grids/config/locales/crowdin/js-eo.yml +++ b/modules/grids/config/locales/crowdin/js-eo.yml @@ -2,6 +2,7 @@ eo: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remove widget' configure: 'Agordi kromprogrameton' widgets: diff --git a/modules/grids/config/locales/crowdin/js-es.yml b/modules/grids/config/locales/crowdin/js-es.yml index a4584265113..ea47791f3c7 100644 --- a/modules/grids/config/locales/crowdin/js-es.yml +++ b/modules/grids/config/locales/crowdin/js-es.yml @@ -2,6 +2,7 @@ es: js: grid: add_widget: 'Agregar widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Quitar widget' configure: 'Configurar widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-et.yml b/modules/grids/config/locales/crowdin/js-et.yml index b67f24ff19f..36917ab6c70 100644 --- a/modules/grids/config/locales/crowdin/js-et.yml +++ b/modules/grids/config/locales/crowdin/js-et.yml @@ -2,6 +2,7 @@ et: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Eemalda vidin' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-eu.yml b/modules/grids/config/locales/crowdin/js-eu.yml index e751719d296..d3d2a3f75d6 100644 --- a/modules/grids/config/locales/crowdin/js-eu.yml +++ b/modules/grids/config/locales/crowdin/js-eu.yml @@ -2,6 +2,7 @@ eu: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remove widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-fa.yml b/modules/grids/config/locales/crowdin/js-fa.yml index 5a8cce82fa7..c34e01cad46 100644 --- a/modules/grids/config/locales/crowdin/js-fa.yml +++ b/modules/grids/config/locales/crowdin/js-fa.yml @@ -2,6 +2,7 @@ fa: js: grid: add_widget: 'افزودن ویجت' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'حذف ویجت' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-fi.yml b/modules/grids/config/locales/crowdin/js-fi.yml index f711a9ecfa4..f66cbce2c43 100644 --- a/modules/grids/config/locales/crowdin/js-fi.yml +++ b/modules/grids/config/locales/crowdin/js-fi.yml @@ -2,6 +2,7 @@ fi: js: grid: add_widget: 'Lisää widgetti' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Poista widgetti' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-fil.yml b/modules/grids/config/locales/crowdin/js-fil.yml index 1fa443fd24f..46be5ecd1fb 100644 --- a/modules/grids/config/locales/crowdin/js-fil.yml +++ b/modules/grids/config/locales/crowdin/js-fil.yml @@ -2,6 +2,7 @@ fil: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Alisin ang widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-fr.yml b/modules/grids/config/locales/crowdin/js-fr.yml index ec98e22264f..2584a367f4c 100644 --- a/modules/grids/config/locales/crowdin/js-fr.yml +++ b/modules/grids/config/locales/crowdin/js-fr.yml @@ -2,11 +2,12 @@ fr: js: grid: add_widget: 'Ajouter un widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Supprimer le widget' configure: 'Configurer le widget' widgets: missing_permission: "Vous n'avez pas les autorisations nécessaires pour voir ce widget." - not_available: "This widget is currently unavailable." + not_available: "Ce widget est actuellement indisponible." custom_text: title: 'Texte personnalisé' documents: diff --git a/modules/grids/config/locales/crowdin/js-he.yml b/modules/grids/config/locales/crowdin/js-he.yml index 98d6af4de17..4a8e7435a60 100644 --- a/modules/grids/config/locales/crowdin/js-he.yml +++ b/modules/grids/config/locales/crowdin/js-he.yml @@ -2,6 +2,7 @@ he: js: grid: add_widget: 'הוסף ווידג''ט' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'הסר רכיב' configure: 'הגדר תוסף' widgets: diff --git a/modules/grids/config/locales/crowdin/js-hi.yml b/modules/grids/config/locales/crowdin/js-hi.yml index 7134ce81bdb..fdb9c5d69b8 100644 --- a/modules/grids/config/locales/crowdin/js-hi.yml +++ b/modules/grids/config/locales/crowdin/js-hi.yml @@ -2,6 +2,7 @@ hi: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'विजेट हटाएँ' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-hr.yml b/modules/grids/config/locales/crowdin/js-hr.yml index 03b5d65ccf0..cdc3aeb2b94 100644 --- a/modules/grids/config/locales/crowdin/js-hr.yml +++ b/modules/grids/config/locales/crowdin/js-hr.yml @@ -2,6 +2,7 @@ hr: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Ukloni widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-hu.yml b/modules/grids/config/locales/crowdin/js-hu.yml index 6ee76abf0cc..bbd6367ae25 100644 --- a/modules/grids/config/locales/crowdin/js-hu.yml +++ b/modules/grids/config/locales/crowdin/js-hu.yml @@ -2,6 +2,7 @@ hu: js: grid: add_widget: 'Widget hozzáadása' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Widget eltávolítása' configure: 'Widget konfigurálása' widgets: diff --git a/modules/grids/config/locales/crowdin/js-id.yml b/modules/grids/config/locales/crowdin/js-id.yml index b290af112a7..06521b20044 100644 --- a/modules/grids/config/locales/crowdin/js-id.yml +++ b/modules/grids/config/locales/crowdin/js-id.yml @@ -2,6 +2,7 @@ id: js: grid: add_widget: 'Tambahkan widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Hapus widget' configure: 'Konfigurasi Widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-it.yml b/modules/grids/config/locales/crowdin/js-it.yml index 7c517849638..a5e80fdef43 100644 --- a/modules/grids/config/locales/crowdin/js-it.yml +++ b/modules/grids/config/locales/crowdin/js-it.yml @@ -2,6 +2,7 @@ it: js: grid: add_widget: 'Aggiungi widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Rimuovi widget' configure: 'Configura il widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-ja.yml b/modules/grids/config/locales/crowdin/js-ja.yml index 244c295b3f9..8fab6372567 100644 --- a/modules/grids/config/locales/crowdin/js-ja.yml +++ b/modules/grids/config/locales/crowdin/js-ja.yml @@ -2,6 +2,7 @@ ja: js: grid: add_widget: 'ウィジェットを追加' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'ウィジェットを削除' configure: 'ウィジェットの設定' widgets: diff --git a/modules/grids/config/locales/crowdin/js-ka.yml b/modules/grids/config/locales/crowdin/js-ka.yml index 36517f2c9a2..33e0affabc0 100644 --- a/modules/grids/config/locales/crowdin/js-ka.yml +++ b/modules/grids/config/locales/crowdin/js-ka.yml @@ -2,6 +2,7 @@ ka: js: grid: add_widget: 'ვიჯეტის დამატება' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'ვიჯეტის წაშლა' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-kk.yml b/modules/grids/config/locales/crowdin/js-kk.yml index e83b64ca356..d63bb6e2cca 100644 --- a/modules/grids/config/locales/crowdin/js-kk.yml +++ b/modules/grids/config/locales/crowdin/js-kk.yml @@ -2,6 +2,7 @@ kk: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remove widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-ko.yml b/modules/grids/config/locales/crowdin/js-ko.yml index 24cd464129e..1e8bfc1f2c0 100644 --- a/modules/grids/config/locales/crowdin/js-ko.yml +++ b/modules/grids/config/locales/crowdin/js-ko.yml @@ -2,6 +2,7 @@ ko: js: grid: add_widget: '위젯 추가' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: '위젯 제거' configure: '위젯 구성' widgets: diff --git a/modules/grids/config/locales/crowdin/js-lt.yml b/modules/grids/config/locales/crowdin/js-lt.yml index 83b48789a7b..e0f7d95a41f 100644 --- a/modules/grids/config/locales/crowdin/js-lt.yml +++ b/modules/grids/config/locales/crowdin/js-lt.yml @@ -2,6 +2,7 @@ lt: js: grid: add_widget: 'Pridėti valdiklį' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Išimti valdiklį' configure: 'Valdiklio nustatymai' widgets: diff --git a/modules/grids/config/locales/crowdin/js-lv.yml b/modules/grids/config/locales/crowdin/js-lv.yml index baf35cf8c4d..3487db35b8c 100644 --- a/modules/grids/config/locales/crowdin/js-lv.yml +++ b/modules/grids/config/locales/crowdin/js-lv.yml @@ -2,6 +2,7 @@ lv: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Noņemt logrīku' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-mn.yml b/modules/grids/config/locales/crowdin/js-mn.yml index 76563a3d79b..83bfdcf96d8 100644 --- a/modules/grids/config/locales/crowdin/js-mn.yml +++ b/modules/grids/config/locales/crowdin/js-mn.yml @@ -2,6 +2,7 @@ mn: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remove widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-ms.yml b/modules/grids/config/locales/crowdin/js-ms.yml index 4ae46aa41f8..11d27649ea3 100644 --- a/modules/grids/config/locales/crowdin/js-ms.yml +++ b/modules/grids/config/locales/crowdin/js-ms.yml @@ -2,6 +2,7 @@ ms: js: grid: add_widget: 'Tambah widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Buang widget' configure: 'Konfigurasi widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-ne.yml b/modules/grids/config/locales/crowdin/js-ne.yml index 3bf3e200ca3..e1024b896d0 100644 --- a/modules/grids/config/locales/crowdin/js-ne.yml +++ b/modules/grids/config/locales/crowdin/js-ne.yml @@ -2,6 +2,7 @@ ne: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remove widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-nl.yml b/modules/grids/config/locales/crowdin/js-nl.yml index 533e9a1e2b8..d6586ef19bf 100644 --- a/modules/grids/config/locales/crowdin/js-nl.yml +++ b/modules/grids/config/locales/crowdin/js-nl.yml @@ -2,6 +2,7 @@ nl: js: grid: add_widget: 'Widget toevoegen' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Widget verwijderen' configure: 'Widget configureren' widgets: diff --git a/modules/grids/config/locales/crowdin/js-no.yml b/modules/grids/config/locales/crowdin/js-no.yml index 7fce51a8a29..f7b7cc2618c 100644 --- a/modules/grids/config/locales/crowdin/js-no.yml +++ b/modules/grids/config/locales/crowdin/js-no.yml @@ -2,6 +2,7 @@ js: grid: add_widget: 'Legg til widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Fjern widget' configure: 'Konfigurer widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-pl.yml b/modules/grids/config/locales/crowdin/js-pl.yml index 319284fcc0f..b5725ff8977 100644 --- a/modules/grids/config/locales/crowdin/js-pl.yml +++ b/modules/grids/config/locales/crowdin/js-pl.yml @@ -2,6 +2,7 @@ pl: js: grid: add_widget: 'Dodaj widżet' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Usuń widżet' configure: 'Konfiguruj widżet' widgets: diff --git a/modules/grids/config/locales/crowdin/js-pt-BR.yml b/modules/grids/config/locales/crowdin/js-pt-BR.yml index 3e3eb1ca5d9..efce6d3628b 100644 --- a/modules/grids/config/locales/crowdin/js-pt-BR.yml +++ b/modules/grids/config/locales/crowdin/js-pt-BR.yml @@ -2,6 +2,7 @@ pt-BR: js: grid: add_widget: 'Adicionar widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remover widget' configure: 'Configurar widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-pt-PT.yml b/modules/grids/config/locales/crowdin/js-pt-PT.yml index 3b4c192ad0c..26bb03fa34f 100644 --- a/modules/grids/config/locales/crowdin/js-pt-PT.yml +++ b/modules/grids/config/locales/crowdin/js-pt-PT.yml @@ -2,6 +2,7 @@ pt-PT: js: grid: add_widget: 'Adicionar widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remover o widget' configure: 'Configurar widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-ro.yml b/modules/grids/config/locales/crowdin/js-ro.yml index 9a629aef3bc..dfb2a43a813 100644 --- a/modules/grids/config/locales/crowdin/js-ro.yml +++ b/modules/grids/config/locales/crowdin/js-ro.yml @@ -2,6 +2,7 @@ ro: js: grid: add_widget: 'Adaugă widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Eliminați widget-ul' configure: 'Configurați widget-ul' widgets: diff --git a/modules/grids/config/locales/crowdin/js-ru.yml b/modules/grids/config/locales/crowdin/js-ru.yml index 9e338de75af..bcc3834efc1 100644 --- a/modules/grids/config/locales/crowdin/js-ru.yml +++ b/modules/grids/config/locales/crowdin/js-ru.yml @@ -2,6 +2,7 @@ ru: js: grid: add_widget: 'Добавить виджет' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Удалить виджет' configure: 'Настроить виджет' widgets: diff --git a/modules/grids/config/locales/crowdin/js-rw.yml b/modules/grids/config/locales/crowdin/js-rw.yml index 042898edcd4..b6f3dfe7644 100644 --- a/modules/grids/config/locales/crowdin/js-rw.yml +++ b/modules/grids/config/locales/crowdin/js-rw.yml @@ -2,6 +2,7 @@ rw: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remove widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-si.yml b/modules/grids/config/locales/crowdin/js-si.yml index e75c05d8a27..78bf30d9659 100644 --- a/modules/grids/config/locales/crowdin/js-si.yml +++ b/modules/grids/config/locales/crowdin/js-si.yml @@ -2,6 +2,7 @@ si: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remove widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-sk.yml b/modules/grids/config/locales/crowdin/js-sk.yml index 5c9ed6e19d9..5b8e9447c79 100644 --- a/modules/grids/config/locales/crowdin/js-sk.yml +++ b/modules/grids/config/locales/crowdin/js-sk.yml @@ -2,6 +2,7 @@ sk: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Odstrániť widget' configure: 'Configurovať widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-sl.yml b/modules/grids/config/locales/crowdin/js-sl.yml index 8b51ae50acf..d411dc0ad7a 100644 --- a/modules/grids/config/locales/crowdin/js-sl.yml +++ b/modules/grids/config/locales/crowdin/js-sl.yml @@ -2,6 +2,7 @@ sl: js: grid: add_widget: 'Dodaj gradnik' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Odstrani gradnik' configure: 'Oblikuj gradnik' widgets: diff --git a/modules/grids/config/locales/crowdin/js-sr.yml b/modules/grids/config/locales/crowdin/js-sr.yml index a6fd1b6e3bc..190bcc668dc 100644 --- a/modules/grids/config/locales/crowdin/js-sr.yml +++ b/modules/grids/config/locales/crowdin/js-sr.yml @@ -2,6 +2,7 @@ sr: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remove widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-sv.yml b/modules/grids/config/locales/crowdin/js-sv.yml index 038bd62f149..e7d1ddc3d35 100644 --- a/modules/grids/config/locales/crowdin/js-sv.yml +++ b/modules/grids/config/locales/crowdin/js-sv.yml @@ -2,6 +2,7 @@ sv: js: grid: add_widget: 'Lägg till widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Ta bort widget' configure: 'Konfigurera widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-th.yml b/modules/grids/config/locales/crowdin/js-th.yml index a7c3d481964..42cdeb73200 100644 --- a/modules/grids/config/locales/crowdin/js-th.yml +++ b/modules/grids/config/locales/crowdin/js-th.yml @@ -2,6 +2,7 @@ th: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'เอาเครื่องมือออก' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-tr.yml b/modules/grids/config/locales/crowdin/js-tr.yml index b0b54e66b4b..7630430ac22 100644 --- a/modules/grids/config/locales/crowdin/js-tr.yml +++ b/modules/grids/config/locales/crowdin/js-tr.yml @@ -2,6 +2,7 @@ tr: js: grid: add_widget: 'Bileşen ekle' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Bileşeni kaldır' configure: 'Bileşeni yapılandır' widgets: diff --git a/modules/grids/config/locales/crowdin/js-uk.yml b/modules/grids/config/locales/crowdin/js-uk.yml index 8dde62cca84..1f725dcfb2b 100644 --- a/modules/grids/config/locales/crowdin/js-uk.yml +++ b/modules/grids/config/locales/crowdin/js-uk.yml @@ -2,6 +2,7 @@ uk: js: grid: add_widget: 'Додати віджет' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Видалити віджет' configure: 'Налаштувати віджет' widgets: diff --git a/modules/grids/config/locales/crowdin/js-uz.yml b/modules/grids/config/locales/crowdin/js-uz.yml index aaa0a419ffd..d2e7f50d426 100644 --- a/modules/grids/config/locales/crowdin/js-uz.yml +++ b/modules/grids/config/locales/crowdin/js-uz.yml @@ -2,6 +2,7 @@ uz: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remove widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/config/locales/crowdin/js-vi.yml b/modules/grids/config/locales/crowdin/js-vi.yml index a3ed9faeac4..44b6105e4d7 100644 --- a/modules/grids/config/locales/crowdin/js-vi.yml +++ b/modules/grids/config/locales/crowdin/js-vi.yml @@ -2,6 +2,7 @@ vi: js: grid: add_widget: 'Thêm tiện ích' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Xóa tiện ích' configure: 'Định cấu hình tiện ích' widgets: diff --git a/modules/grids/config/locales/crowdin/js-zh-CN.yml b/modules/grids/config/locales/crowdin/js-zh-CN.yml index 001080a7579..3742e7b4ba4 100644 --- a/modules/grids/config/locales/crowdin/js-zh-CN.yml +++ b/modules/grids/config/locales/crowdin/js-zh-CN.yml @@ -2,6 +2,7 @@ zh-CN: js: grid: add_widget: '添加微件' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: '移除微件' configure: '配置微件' widgets: diff --git a/modules/grids/config/locales/crowdin/js-zh-TW.yml b/modules/grids/config/locales/crowdin/js-zh-TW.yml index 08695061e09..8b4c9263306 100644 --- a/modules/grids/config/locales/crowdin/js-zh-TW.yml +++ b/modules/grids/config/locales/crowdin/js-zh-TW.yml @@ -2,6 +2,7 @@ zh-TW: js: grid: add_widget: '新增小工具' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: '移除小工具' configure: '設定小工具' widgets: diff --git a/modules/grids/config/locales/js-en.yml b/modules/grids/config/locales/js-en.yml index db19d22b8d0..7628e1b0595 100644 --- a/modules/grids/config/locales/js-en.yml +++ b/modules/grids/config/locales/js-en.yml @@ -2,6 +2,7 @@ en: js: grid: add_widget: 'Add widget' + widget_menu_label: 'Show menu options for %{widgetName} widget' remove: 'Remove widget' configure: 'Configure widget' widgets: diff --git a/modules/grids/spec/controllers/grids/overviews_controller_spec.rb b/modules/grids/spec/controllers/grids/overviews_controller_spec.rb index 851d1528854..da408da1656 100644 --- a/modules/grids/spec/controllers/grids/overviews_controller_spec.rb +++ b/modules/grids/spec/controllers/grids/overviews_controller_spec.rb @@ -67,7 +67,7 @@ RSpec.describe Overviews::OverviewsController do end end - describe "#dashboard", with_flag: { new_project_overview: true } do + describe "#dashboard" do it "renders 'dashboard'" do get :dashboard, params: { project_id: project.id } diff --git a/modules/meeting/app/components/meeting_agenda_items/blank_slate_component.rb b/modules/meeting/app/components/meeting_agenda_items/blank_slate_component.rb index d005479391d..be81cccce94 100644 --- a/modules/meeting/app/components/meeting_agenda_items/blank_slate_component.rb +++ b/modules/meeting/app/components/meeting_agenda_items/blank_slate_component.rb @@ -42,19 +42,23 @@ module MeetingAgendaItems @meeting = meeting end - delegate :template?, to: :meeting + delegate :template?, :series_template?, :onetime_template?, to: :meeting def title - if template? + if series_template? t(:"recurring_meeting.template.blank_title") + elsif onetime_template? + t(:text_onetime_meeting_template_empty_heading) else t(:text_meeting_empty_heading) end end def description - if template? + if series_template? t(:"recurring_meeting.template.description") + elsif onetime_template? + t(:text_onetime_meeting_template_description) else t(%i[text_meeting_empty_description1 text_meeting_empty_description2]).join(" ") end diff --git a/modules/meeting/app/components/meeting_agenda_items/list_component.rb b/modules/meeting/app/components/meeting_agenda_items/list_component.rb index 8cbd9c6ffeb..da0002fd86d 100644 --- a/modules/meeting/app/components/meeting_agenda_items/list_component.rb +++ b/modules/meeting/app/components/meeting_agenda_items/list_component.rb @@ -73,7 +73,7 @@ module MeetingAgendaItems icon: :info, dismiss_scheme: :none ) do - if @meeting.template? + if @meeting.series_template? draft = @meeting.draft? ? "draft_" : "" t( "recurring_meeting.template.#{draft}banner_html", @@ -82,6 +82,8 @@ module MeetingAgendaItems project_recurring_meeting_path(@meeting.project, @meeting.recurring_meeting) ) ) + elsif @meeting.onetime_template? + t("text_onetime_meeting_template_banner") elsif @meeting.draft? t("text_meeting_draft_banner") end diff --git a/app/views/groups/_form.html.erb b/modules/meeting/app/components/meeting_templates/index_page_header_component.html.erb similarity index 67% rename from app/views/groups/_form.html.erb rename to modules/meeting/app/components/meeting_templates/index_page_header_component.html.erb index 416c8efb177..04f44b82a0b 100644 --- a/app/views/groups/_form.html.erb +++ b/modules/meeting/app/components/meeting_templates/index_page_header_component.html.erb @@ -27,22 +27,9 @@ See COPYRIGHT and LICENSE files for more details. ++#%> -<%# - needs locals: - f: labelled form builder +<%= + render(Primer::OpenProject::PageHeader.new) do |header| + header.with_title { I18n.t(:label_meeting_templates) } + header.with_breadcrumbs(breadcrumb_items) + end %> - -<%= error_messages_for :group %> - -
-
- <%= f.text_field :lastname, - label: Group.human_attribute_name(:name), - required: true, - container_class: "-middle" %> -
- - <%= render partial: "customizable/form", locals: { form: f, - all_fields: true, - only_required: false } %> -
diff --git a/modules/meeting/app/components/meeting_templates/index_page_header_component.rb b/modules/meeting/app/components/meeting_templates/index_page_header_component.rb new file mode 100644 index 00000000000..df4ecadff11 --- /dev/null +++ b/modules/meeting/app/components/meeting_templates/index_page_header_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 MeetingTemplates + class IndexPageHeaderComponent < ApplicationComponent + include ApplicationHelper + + def initialize(project: nil) + super + @project = project + end + + def breadcrumb_items + [ + ({ href: project_overview_path(@project.id), text: @project.name } if @project.present?), + { href: url_for({ controller: "meetings", action: :index, project_id: @project }), + text: I18n.t(:label_meeting_plural) }, + I18n.t(:label_meeting_templates) + ].compact + end + end +end diff --git a/modules/meeting/app/components/meeting_templates/index_sub_header_component.html.erb b/modules/meeting/app/components/meeting_templates/index_sub_header_component.html.erb new file mode 100644 index 00000000000..38282732404 --- /dev/null +++ b/modules/meeting/app/components/meeting_templates/index_sub_header_component.html.erb @@ -0,0 +1,47 @@ +<%#-- 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::SubHeader.new) do |subheader| + if render_create_button? + subheader.with_action_button( + leading_icon: :plus, + label: accessibility_label_text, + scheme: :primary, + tag: :a, + href: create_path, + id: id, + test_selector: "add-template-button", + data: button_data + ) do + label_text + end + end + end +%> diff --git a/modules/meeting/app/components/meeting_templates/index_sub_header_component.rb b/modules/meeting/app/components/meeting_templates/index_sub_header_component.rb new file mode 100644 index 00000000000..d42b57837b6 --- /dev/null +++ b/modules/meeting/app/components/meeting_templates/index_sub_header_component.rb @@ -0,0 +1,76 @@ +# 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 MeetingTemplates + class IndexSubHeaderComponent < ApplicationComponent + include ApplicationHelper + + def initialize(project: nil) + super + @project = project + end + + def render_create_button? + if @project + User.current.allowed_in_project?(:create_meetings, @project) + else + User.current.allowed_in_any_project?(:create_meetings) + end + end + + def create_path + if @project + create_template_project_meetings_path(@project) + else + new_dialog_template_meetings_path + end + end + + def use_dialog? + @project.nil? + end + + def button_data + use_dialog? ? { controller: "async-dialog" } : { turbo_method: :post } + end + + def id + "add-template-button" + end + + def accessibility_label_text + I18n.t(:label_meeting_template_new) + end + + def label_text + I18n.t(:label_template) + end + end +end diff --git a/modules/meeting/app/components/meeting_templates/row_component.rb b/modules/meeting/app/components/meeting_templates/row_component.rb new file mode 100644 index 00000000000..45c46e869f6 --- /dev/null +++ b/modules/meeting/app/components/meeting_templates/row_component.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 MeetingTemplates + class RowComponent < ::OpPrimer::BorderBoxRowComponent + delegate :current_project, to: :table + delegate :project, to: :model + + def project_name + helpers.link_to_project project, {}, {}, false + end + + def title + render(Primer::Beta::Link.new(href: project_meeting_path(project, model), font_weight: :bold)) { model.title } + end + + def button_links + [action_menu] + end + + def action_menu + render(Primer::Alpha::ActionMenu.new) do |menu| + menu.with_show_button( + icon: "kebab-horizontal", + "aria-label": t(:label_more), + scheme: :invisible, + data: { "test-selector": "more-button" } + ) + + edit_action(menu) + delete_action(menu) + end + end + + def edit_action(menu) + return unless edit_allowed? + + menu.with_item( + label: I18n.t(:label_meeting_template_edit), + href: project_meeting_path(project, model) + ) do |item| + item.with_leading_visual_icon(icon: :pencil) + end + end + + def delete_action(menu) + return unless delete_allowed? + + menu.with_item( + label: I18n.t(:label_meeting_template_delete), + scheme: :danger, + href: delete_dialog_project_meeting_path(project, model), + tag: :a, + content_arguments: { + data: { controller: "async-dialog" } + } + ) do |item| + item.with_leading_visual_icon(icon: :trash) + end + end + + def delete_allowed? + User.current.allowed_in_project?(:delete_meetings, project) + end + + def edit_allowed? + User.current.allowed_in_project?(:edit_meetings, project) + end + end +end diff --git a/modules/meeting/app/components/meeting_templates/table_component.rb b/modules/meeting/app/components/meeting_templates/table_component.rb new file mode 100644 index 00000000000..d1e411279d9 --- /dev/null +++ b/modules/meeting/app/components/meeting_templates/table_component.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. +#++ + +module MeetingTemplates + class TableComponent < ::OpPrimer::BorderBoxTableComponent + options :current_project + + columns :title, :project_name + + mobile_columns :title, :project_name + + main_column :title + + def sortable? + false + end + + def paginated? + false + end + + def has_footer? + false + end + + def has_actions? + true + end + + def mobile_title + I18n.t(:label_meeting_templates) + end + + def headers + @headers ||= [ + [:title, { caption: Meeting.human_attribute_name(:title) }], + current_project.blank? ? [:project_name, { caption: Meeting.human_attribute_name(:project) }] : nil + ].compact + end + + def columns + @columns ||= headers.map(&:first) + end + end +end diff --git a/modules/meeting/app/components/meetings/blank_slate_component.html.erb b/modules/meeting/app/components/meetings/blank_slate_component.html.erb index 4c600f45f89..1ade3e1c3bc 100644 --- a/modules/meeting/app/components/meetings/blank_slate_component.html.erb +++ b/modules/meeting/app/components/meetings/blank_slate_component.html.erb @@ -30,8 +30,8 @@ See COPYRIGHT and LICENSE files for more details. <%= render(Primer::Beta::Blankslate.new(border: true, test_selector: "meetings-blank-slate")) do |component| component.with_visual_icon(icon: :book, size: :medium) - component.with_heading(tag: :h2).with_content(I18n.t("meeting.blankslate.title")) + component.with_heading(tag: :h2).with_content(heading_text) - component.with_description_content(I18n.t("meeting.blankslate.desc")) + component.with_description_content(description_text) end %> diff --git a/modules/meeting/app/components/meetings/blank_slate_component.rb b/modules/meeting/app/components/meetings/blank_slate_component.rb index ecc4c484c7e..f78ff415630 100644 --- a/modules/meeting/app/components/meetings/blank_slate_component.rb +++ b/modules/meeting/app/components/meetings/blank_slate_component.rb @@ -33,11 +33,12 @@ module Meetings include OpPrimer::ComponentHelpers include ApplicationHelper - def initialize(project: nil, current_user: User.current) + def initialize(project: nil, current_user: User.current, template: false) super @project = project @current_user = current_user + @template = template end def can_create_meetings? @@ -55,5 +56,13 @@ module Meetings def new_recurring_meeting_path polymorphic_path([:new_dialog, @project, :meetings], type: :recurring) end + + def heading_text + @template ? I18n.t("text_meeting_template_blank_slate_heading") : I18n.t("meeting.blankslate.title") + end + + def description_text + @template ? I18n.t("text_meeting_template_blank_slate") : I18n.t("meeting.blankslate.desc") + end end end diff --git a/modules/meeting/app/components/meetings/delete_dialog_component.rb b/modules/meeting/app/components/meetings/delete_dialog_component.rb index bda1fb0d05c..048371a4769 100644 --- a/modules/meeting/app/components/meetings/delete_dialog_component.rb +++ b/modules/meeting/app/components/meetings/delete_dialog_component.rb @@ -51,7 +51,8 @@ module Meetings if recurring_meeting.present? I18n.t("meeting.delete_dialog.occurrence.title") else - I18n.t("meeting.delete_dialog.one_time.title") + template = @meeting.onetime_template? ? ".template" : "" + I18n.t("meeting.delete_dialog.one_time#{template}.title") end end @@ -59,7 +60,8 @@ module Meetings if recurring_meeting.present? I18n.t("meeting.delete_dialog.occurrence.heading") else - I18n.t("meeting.delete_dialog.one_time.heading") + template = @meeting.onetime_template? ? ".template" : "" + I18n.t("meeting.delete_dialog.one_time#{template}.heading") end end diff --git a/modules/meeting/app/components/meetings/header_component.html.erb b/modules/meeting/app/components/meetings/header_component.html.erb index c7d2b9f3f01..97c39222044 100644 --- a/modules/meeting/app/components/meetings/header_component.html.erb +++ b/modules/meeting/app/components/meetings/header_component.html.erb @@ -9,10 +9,7 @@ Primer::OpenProject::PageHeader.new( test_selector: "meeting-page-header", state: @state, - data: { - poll_for_changes_target: "reference", - reference_value: @meeting.changed_hash - } + data: page_header_data_attributes ) ) do |header| header.with_title do |title| @@ -41,6 +38,13 @@ end end + if create_from_template_enabled? + header.with_action_button(**create_from_template_button_params) do |button| + button.with_leading_visual_icon(icon: :plus) + I18n.t("label_meeting_create_from_template") + end + end + if can_start_presentation? header.with_action_button( tag: :a, @@ -65,7 +69,7 @@ ) do |menu| if @meeting.editable? && !@series menu.with_item( - label: t("label_meeting_edit_title"), + label: edit_label, href: edit_project_meeting_path(@project, @meeting), content_arguments: { data: { "turbo-stream": true } diff --git a/modules/meeting/app/components/meetings/header_component.rb b/modules/meeting/app/components/meetings/header_component.rb index 1849321326c..0a80d42ba69 100644 --- a/modules/meeting/app/components/meetings/header_component.rb +++ b/modules/meeting/app/components/meetings/header_component.rb @@ -49,6 +49,15 @@ module Meetings @state = fetch_or_fallback(STATE_OPTIONS, state) end + def page_header_data_attributes + { + poll_for_changes_target: "reference", + reference_value: @meeting.changed_hash, + controller: "editable-page-header-title", + "editable-page-header-title-input-id-value": "meeting_title" + } + end + # Define the interval so it can be overriden through tests def check_for_updates_interval 10_000 @@ -71,16 +80,34 @@ module Meetings private def delete_enabled? - !@meeting.template? && User.current.allowed_in_project?(:delete_meetings, @meeting.project) + !@meeting.series_template? && User.current.allowed_in_project?(:delete_meetings, @meeting.project) end def finish_setup_enabled? @meeting.draft? && + !@meeting.onetime_template? && User.current.allowed_in_project?(:edit_meetings, @meeting.project) end def delete_series_enabled? - @meeting.draft? && @meeting.template? && User.current.allowed_in_project?(:delete_meetings, @project) + @meeting.series_template? && @meeting.draft? && User.current.allowed_in_project?(:delete_meetings, @project) + end + + def create_from_template_enabled? + @meeting.onetime_template? && + User.current.allowed_in_project?(:create_meetings, @meeting.project) + end + + def create_from_template_button_params + { + tag: :a, + scheme: :primary, + mobile_label: I18n.t("label_meeting_create_from_template"), + mobile_icon: :plus, + size: :medium, + href: new_dialog_project_meetings_path(@project, template_id: @meeting.id), + data: { turbo_stream: true } + } end def action_button_params @@ -118,13 +145,15 @@ module Meetings ({ href: project_overview_path(@project.id), text: @project.name } if @project.present?), { href: @project.present? ? project_meetings_path(@project.id) : meetings_path, text: I18n.t(:label_meeting_plural) }, - meeting_series_element, + meeting_type_element, meeting_element ].compact end def meeting_element - if @meeting.templated? + if @meeting.onetime_template? + @meeting.title + elsif @meeting.series_template? I18n.t(:label_template) elsif @series.present? format_date(@meeting.start_time) @@ -133,15 +162,20 @@ module Meetings end end - def meeting_series_element + def meeting_type_element if @series.present? { href: project_recurring_meeting_path(@series.project, @series), text: @series.title } + elsif @meeting.onetime_template? + { href: url_for({ controller: "meeting_templates", action: :index, project_id: @project }), + text: I18n.t(:label_meeting_templates) } end end def delete_label if @series.present? I18n.t("label_recurring_meeting_cancel") + elsif @meeting.onetime_template? + I18n.t("label_meeting_template_delete") else I18n.t("label_meeting_delete") end @@ -154,5 +188,13 @@ module Meetings I18n.t("button_duplicate") end end + + def edit_label + if @meeting.onetime_template? + I18n.t("label_meeting_template_edit") + else + I18n.t("label_meeting_edit_title") + end + end end end diff --git a/modules/meeting/app/components/meetings/header_infoline_component.html.erb b/modules/meeting/app/components/meetings/header_infoline_component.html.erb index 0d03e2a37da..05b355bcf0e 100644 --- a/modules/meeting/app/components/meetings/header_infoline_component.html.erb +++ b/modules/meeting/app/components/meetings/header_infoline_component.html.erb @@ -1,5 +1,5 @@ <%= render(Primer::BaseComponent.new(tag: :div, classes: "meeting-infoline")) do %> - <% if @series && @meeting.template? %> + <% if @meeting.series_template? %> <% if @meeting.draft? %> <%= render(Primer::BaseComponent.new(tag: :div, classes: "hidden-for-mobile", mr: 2)) do %> <%= render(Meetings::SidePanel::StatusButtonComponent.new(meeting: @meeting, size: :small)) %> @@ -11,6 +11,13 @@ end %> <%= helpers.primer_link_to_user(@meeting.author, underline: true) %>. + <% elsif @meeting.onetime_template? %> + <%= + render(Primer::Beta::Text.new(mr: 1)) do + t(:label_meeting_created_by) + end + %> + <%= helpers.primer_link_to_user(@meeting.author, underline: true) %>. <% elsif @series %> <%= render(Primer::BaseComponent.new(tag: :div, classes: "hidden-for-mobile", mr: 2)) do %> <%= render(Meetings::SidePanel::StatusButtonComponent.new(meeting: @meeting, size: :small)) %> diff --git a/modules/meeting/app/components/meetings/index/dialog_component.html.erb b/modules/meeting/app/components/meetings/index/dialog_component.html.erb index 703820c53c2..122c9ecd138 100644 --- a/modules/meeting/app/components/meetings/index/dialog_component.html.erb +++ b/modules/meeting/app/components/meetings/index/dialog_component.html.erb @@ -8,12 +8,13 @@ ) ) do |dialog| dialog.with_header(variant: :large) - dialog.with_body do + dialog.with_body(classes: "Overlay-body_autocomplete_height") do render( Meetings::Index::FormComponent.new( meeting: @meeting, project: @project, - copy_from: @copy_from + copy_from: @copy_from, + template: @template ) ) end @@ -35,13 +36,7 @@ type: :submit ) ) do - if @meeting.persisted? - I18n.t(:button_save) - elsif @meeting.is_a?(RecurringMeeting) - I18n.t(:label_recurring_meeting_series_create) - else - I18n.t(:label_meeting_create) - end + submit_button_text end end end diff --git a/modules/meeting/app/components/meetings/index/dialog_component.rb b/modules/meeting/app/components/meetings/index/dialog_component.rb index 7bf52ddd681..eaed9e0a4c1 100644 --- a/modules/meeting/app/components/meetings/index/dialog_component.rb +++ b/modules/meeting/app/components/meetings/index/dialog_component.rb @@ -34,12 +34,13 @@ module Meetings include OpTurbo::Streamable include OpPrimer::ComponentHelpers - def initialize(meeting:, project:, copy_from: nil) + def initialize(meeting:, project:, copy_from: nil, template: false) super @meeting = meeting @project = project @copy_from = copy_from + @template = template end private @@ -50,7 +51,8 @@ module Meetings end def title - return I18n.t(:label_meeting_duplicate) if @copy_from + return I18n.t(:label_meeting_template_new) if @template + return I18n.t(:label_meeting_duplicate) if @copy_from && !@copy_from.onetime_template? return I18n.t(:label_meeting_edit) if @meeting.persisted? case @meeting @@ -60,5 +62,13 @@ module Meetings I18n.t("label_meeting_new_dynamic") end end + + def submit_button_text + return I18n.t(:label_meeting_template_create) if @template + return I18n.t(:button_save) if @meeting.persisted? + return I18n.t(:label_recurring_meeting_series_create) if @meeting.is_a?(RecurringMeeting) + + I18n.t(:label_meeting_create) + end end end diff --git a/modules/meeting/app/components/meetings/index/form_component.html.erb b/modules/meeting/app/components/meetings/index/form_component.html.erb index acb1d6b699b..f889b28fca8 100644 --- a/modules/meeting/app/components/meetings/index/form_component.html.erb +++ b/modules/meeting/app/components/meetings/index/form_component.html.erb @@ -35,19 +35,29 @@ end end - modal_body.with_row do - render(Meeting::Title.new(f)) + if creating_onetime_meeting? && no_preselection? && available_templates.any? + modal_body.with_row(mb: 3) do + render(Meeting::TemplateAutocompleter.new(f, project: @project)) + end end - modal_body.with_row(mt: 3) do - render(Meeting::Location.new(f, meeting: @meeting)) + unless @template + modal_body.with_row do + render(Meeting::Title.new(f)) + end end - modal_body.with_row(mt: 3) do - render(Meeting::TimeGroup.new(f, meeting: @meeting)) + unless @template + modal_body.with_row(mt: 3) do + render(Meeting::Location.new(f, meeting: @meeting)) + end + + modal_body.with_row(mt: 3) do + render(Meeting::TimeGroup.new(f, meeting: @meeting)) + end end - if @meeting.is_a?(RecurringMeeting) + if !@template && @meeting.is_a?(RecurringMeeting) modal_body.with_row(mt: 3) do flex_layout(classes: "FormControl-horizontalGroup") do |frequency_row| frequency_row.with_column(flex: 1) do @@ -121,20 +131,22 @@ render(Meeting::CopiedFrom.new(f, id: @copy_from.id)) end - modal_body.with_row(mt: 3) do - render(Meeting::CopyItems.new(f)) - end + unless @copy_from.onetime_template? + modal_body.with_row(mt: 3) do + render(Meeting::CopyItems.new(f)) + end - modal_body.with_row(mt: 3) do - render(Meeting::CopyParticipants.new(f)) - end + modal_body.with_row(mt: 3) do + render(Meeting::CopyParticipants.new(f)) + end - modal_body.with_row(mt: 3) do - render(Meeting::CopyAttachments.new(f)) + modal_body.with_row(mt: 3) do + render(Meeting::CopyAttachments.new(f)) + end end end - if @meeting.is_a?(RecurringMeeting) && @meeting.persisted? + if !@template && @meeting.is_a?(RecurringMeeting) && @meeting.persisted? modal_body.with_row(mt: 3) do render(Meetings::EmailUpdatesBannerComponent.new(@meeting)) end diff --git a/modules/meeting/app/components/meetings/index/form_component.rb b/modules/meeting/app/components/meetings/index/form_component.rb index da1d60fdf2f..a5acd44b608 100644 --- a/modules/meeting/app/components/meetings/index/form_component.rb +++ b/modules/meeting/app/components/meetings/index/form_component.rb @@ -33,17 +33,20 @@ module Meetings include OpTurbo::Streamable include OpPrimer::ComponentHelpers - def initialize(meeting:, project:, copy_from: nil) + def initialize(meeting:, project:, copy_from: nil, template: false) super @meeting = meeting @project = project @copy_from = copy_from + @template = template end private def form_controller + return "meeting_templates" if @template + if @meeting.is_a?(RecurringMeeting) "/recurring_meetings" else @@ -66,5 +69,19 @@ module Meetings :update end end + + def creating_onetime_meeting? + !@meeting.persisted? && !@meeting.is_a?(RecurringMeeting) && !@template + end + + def no_preselection? + !@copy_from + end + + def available_templates + return [] unless @project + + @available_templates ||= Meeting.onetime_templates.where(project: @project).visible + end end end diff --git a/modules/meeting/app/components/meetings/show_component.html.erb b/modules/meeting/app/components/meetings/show_component.html.erb index fb66da05744..9543598d65a 100644 --- a/modules/meeting/app/components/meetings/show_component.html.erb +++ b/modules/meeting/app/components/meetings/show_component.html.erb @@ -1,7 +1,7 @@ <%= flex_layout(data: show_page_data_attributes) do |show_page| show_page.with_row do - render(Meetings::HeaderComponent.new(meeting: @meeting)) + render(Meetings::HeaderComponent.new(meeting: @meeting, state: @state)) end show_page.with_row do diff --git a/modules/meeting/app/components/meetings/show_component.rb b/modules/meeting/app/components/meetings/show_component.rb index b67d02842f7..25ed243ae5d 100644 --- a/modules/meeting/app/components/meetings/show_component.rb +++ b/modules/meeting/app/components/meetings/show_component.rb @@ -32,11 +32,12 @@ module Meetings include ApplicationHelper include OpPrimer::ComponentHelpers - def initialize(meeting:) + def initialize(meeting:, state: :show) super @meeting = meeting @project = meeting.project + @state = state end private diff --git a/modules/meeting/app/components/meetings/side_panel_component.html.erb b/modules/meeting/app/components/meetings/side_panel_component.html.erb index e98f38ef2cf..c8ceb86e01f 100644 --- a/modules/meeting/app/components/meetings/side_panel_component.html.erb +++ b/modules/meeting/app/components/meetings/side_panel_component.html.erb @@ -1,22 +1,24 @@ <%= component_wrapper do render(Primer::OpenProject::SidePanel.new) do |panel| - panel.with_section(Meetings::SidePanel::DetailsComponent.new(meeting: @meeting)) - - if @meeting.editable? && !@meeting.draft? - panel.with_section(email_updates_mode_selector) - end - - unless @meeting.template? && !@meeting.draft? - panel.with_section(Meetings::SidePanel::StateComponent.new(meeting: @meeting)) - end - desktop_grid_row_arguments = { display: [:none, nil, :table_cell] } - panel.with_section( - Meetings::SidePanel::ParticipantsComponent.new(meeting: @meeting), - grid_row_arguments: desktop_grid_row_arguments.merge({ classes: "meetings-side-panel--participants-section" }) - ) + unless @meeting.onetime_template? + panel.with_section(Meetings::SidePanel::DetailsComponent.new(meeting: @meeting)) + + if @meeting.editable? && !@meeting.draft? + panel.with_section(email_updates_mode_selector) + end + + unless @meeting.template? && !@meeting.draft? + panel.with_section(Meetings::SidePanel::StateComponent.new(meeting: @meeting)) + end + + panel.with_section( + Meetings::SidePanel::ParticipantsComponent.new(meeting: @meeting), + grid_row_arguments: desktop_grid_row_arguments.merge({ classes: "meetings-side-panel--participants-section" }) + ) + end panel.with_section( Meetings::SidePanel::AttachmentsComponent.new(meeting: @meeting), diff --git a/modules/meeting/app/contracts/meeting_agenda_items/create_contract.rb b/modules/meeting/app/contracts/meeting_agenda_items/create_contract.rb index 806f3a1b5e9..8364a11d3b5 100644 --- a/modules/meeting/app/contracts/meeting_agenda_items/create_contract.rb +++ b/modules/meeting/app/contracts/meeting_agenda_items/create_contract.rb @@ -32,7 +32,9 @@ module MeetingAgendaItems class CreateContract < BaseContract attribute :item_type - validate :user_allowed_to_add, :validate_meeting_existence + validate :user_allowed_to_add, + :validate_meeting_existence, + :section_belongs_to_meeting def self.assignable_meetings(user) Meeting @@ -72,6 +74,14 @@ module MeetingAgendaItems errors.add :base, :does_not_exist unless visible? end + def section_belongs_to_meeting + return if model.meeting_section.nil? || model.meeting.nil? + + unless model.meeting_id == model.meeting_section.meeting_id + errors.add :base, :section_not_belong_to_meeting + end + end + private def visible? diff --git a/modules/meeting/app/contracts/meetings/base_contract.rb b/modules/meeting/app/contracts/meetings/base_contract.rb index ec3b5970fdb..8e51766fad3 100644 --- a/modules/meeting/app/contracts/meetings/base_contract.rb +++ b/modules/meeting/app/contracts/meetings/base_contract.rb @@ -44,15 +44,5 @@ module Meetings attribute :start_time_hour attribute :template attribute :notify - - validate :template_requires_series - - private - - def template_requires_series - if model.template && model.recurring_meeting_id.nil? - errors.add(:template, :invalid) - end - end end end diff --git a/modules/meeting/app/controllers/concerns/meetings/agenda_component_streams.rb b/modules/meeting/app/controllers/concerns/meetings/agenda_component_streams.rb index 9df59348fea..4f6792e8d26 100644 --- a/modules/meeting/app/controllers/concerns/meetings/agenda_component_streams.rb +++ b/modules/meeting/app/controllers/concerns/meetings/agenda_component_streams.rb @@ -51,6 +51,8 @@ module Meetings end def update_sidebar_details_component_via_turbo_stream(meeting: @meeting) + return if meeting.onetime_template? + update_via_turbo_stream( component: Meetings::SidePanel::DetailsComponent.new( meeting: @@ -59,6 +61,8 @@ module Meetings end def update_sidebar_state_component_via_turbo_stream(meeting: @meeting) + return if meeting.onetime_template? + update_via_turbo_stream( component: Meetings::SidePanel::StateComponent.new( meeting: @@ -76,6 +80,8 @@ module Meetings end def update_sidebar_participants_component_via_turbo_stream(meeting: @meeting) + return if meeting.onetime_template? + update_via_turbo_stream( component: Meetings::SidePanel::ParticipantsComponent.new( meeting: @@ -156,7 +162,8 @@ module Meetings def render_agenda_item_form_via_turbo_stream(collapsed:, current_occurrence:, meeting: @meeting, meeting_section: @meeting_section, type: :simple) - if meeting.sections.empty? && meeting_section != meeting.backlog + # Nil case is for onetime templates + if meeting.sections.empty? && (meeting_section.nil? || meeting_section != meeting.backlog) render_agenda_item_form_for_empty_meeting_via_turbo_stream(type:) else render_agenda_item_form_in_section_via_turbo_stream(meeting:, meeting_section:, type:, collapsed:, current_occurrence:) diff --git a/modules/meeting/app/controllers/meeting_templates_controller.rb b/modules/meeting/app/controllers/meeting_templates_controller.rb new file mode 100644 index 00000000000..fc7ed86f27b --- /dev/null +++ b/modules/meeting/app/controllers/meeting_templates_controller.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +class MeetingTemplatesController < ApplicationController + before_action :load_and_authorize_in_optional_project + + include Layout + include OpTurbo::ComponentStream + include OpTurbo::FlashStreamHelper + + menu_item :meetings + + def index + @templates = Meeting.onetime_templates + .visible + .order(:title) + + @templates = @templates.where(project_id: @project.id) if @project + + render "meeting_templates/index", + locals: { menu_name: project_or_global_menu } + end + + def new_dialog + @template = Meeting.new( + project: @project, + author: User.current, + template: true, + recurring_meeting_id: nil + ) + + respond_with_dialog Meetings::Index::DialogComponent.new( + meeting: @template, + project: @project, + template: true + ) + end + + def create # rubocop:disable Metrics/AbcSize + call = ::Meetings::CreateService + .new(user: current_user) + .call(create_template_params) + + @template = call.result + + if call.success? + redirect_to project_meeting_path(@template.project, @template, state: :edit), status: :see_other + elsif @project + flash[:error] = call.errors.full_messages.join(", ") + redirect_to action: :index, status: :unprocessable_entity + else + update_via_turbo_stream( + component: Meetings::Index::FormComponent.new( + meeting: @template, + project: @template.project, + template: true + ), + status: :bad_request + ) + + respond_with_turbo_streams + end + end + + private + + def require_project + render_404 unless @project + end + + def create_template_params + project_id = @project&.id || params.dig(:meeting, :project_id) + project = project_id ? Project.find_by(id: project_id) : nil + + { + title: I18n.t(:label_meeting_template_new), + project:, + template: true, + recurring_meeting_id: nil + } + end +end diff --git a/modules/meeting/app/controllers/meetings_controller.rb b/modules/meeting/app/controllers/meetings_controller.rb index 19a6a074bab..7130a006f45 100644 --- a/modules/meeting/app/controllers/meetings_controller.rb +++ b/modules/meeting/app/controllers/meetings_controller.rb @@ -39,7 +39,7 @@ class MeetingsController < ApplicationController before_action :set_activity, only: %i[history] before_action :find_copy_from_meeting, only: %i[create] before_action :convert_params, only: %i[create update] - before_action :prevent_template_destruction, only: :destroy + before_action :prevent_series_template_destruction, only: :destroy helper :watchers include MeetingsHelper @@ -74,7 +74,7 @@ class MeetingsController < ApplicationController if @meeting.state == "cancelled" render_404 else - render(Meetings::ShowComponent.new(meeting: @meeting), layout: true) + render(Meetings::ShowComponent.new(meeting: @meeting, state: show_edit_state), layout: true) end end end @@ -155,7 +155,8 @@ class MeetingsController < ApplicationController def new_dialog respond_with_dialog Meetings::Index::DialogComponent.new( meeting: @meeting, - project: @project + project: @project, + copy_from: @copy_from ) end @@ -336,10 +337,10 @@ class MeetingsController < ApplicationController @meeting.toggle!(:notify) # Reload to get the updated value - @meeting.recurring_meeting.template.reload if @meeting.template? + @meeting.recurring_meeting.template.reload if @meeting.series_template? if @meeting.notify? - if @meeting.template? + if @meeting.series_template? handle_series_notification else handle_notification(type: :toggle_notifications) @@ -433,7 +434,7 @@ class MeetingsController < ApplicationController end end - def build_meeting + def build_meeting # rubocop:disable Metrics/AbcSize meeting = if params[:type] == "recurring" RecurringMeeting.new @@ -447,6 +448,9 @@ class MeetingsController < ApplicationController .call(project: @project) @meeting = call.result + + # When coming from the "Create from template" button, load the template to hide the form field + @copy_from = Meeting.onetime_templates.visible.find_by(id: params[:template_id]) if params[:template_id].present? end def global_upcoming_meetings @@ -484,6 +488,9 @@ class MeetingsController < ApplicationController # Recurring meeting occurrences can only be copied as one-time meetings @converted_params[:recurring_meeting_id] = nil + + # Onetime templates can only be copied as one-time meetings + @converted_params[:template] = false if @copy_from&.onetime_template? end def meeting_params @@ -534,22 +541,44 @@ class MeetingsController < ApplicationController end def find_copy_from_meeting - copied_from_meeting_id = params[:copied_from_meeting_id] || params[:meeting][:copied_from_meeting_id] + # Check for template selection from form submission + template_id = params[:meeting][:template_id] + if template_id.present? + @copy_from = Meeting.onetime_templates.visible.find_by(id: template_id) + return + end + + # Check for regular copy + copied_from_meeting_id = params[:meeting][:copied_from_meeting_id] return unless copied_from_meeting_id @copy_from = Meeting.visible.find(copied_from_meeting_id) end def copy_attributes - { - copy_agenda: copy_param(:copy_agenda), - copy_attachments: copy_param(:copy_attachments), - send_notifications: @converted_params[:send_notifications] - } + if @copy_from&.onetime_template? + { + copy_agenda: true, + copy_attachments: true, + send_notifications: @converted_params[:send_notifications] + } + elsif @copy_from&.series_template? + { + copy_agenda: true, + copy_attachments: false, + send_notifications: @converted_params[:send_notifications] + } + else + { + copy_agenda: copy_param(:copy_agenda), + copy_attachments: copy_param(:copy_attachments), + send_notifications: @converted_params[:send_notifications] + } + end end - def prevent_template_destruction - render_400 if @meeting.templated? + def prevent_series_template_destruction + render_400 if @meeting.series_template? end def redirect_to_project @@ -613,4 +642,8 @@ class MeetingsController < ApplicationController render_success_flash_message_via_turbo_stream(message: I18n.t(:notice_successful_notification)) end + + def show_edit_state + params[:state] == "edit" ? :edit : :show + end end diff --git a/modules/meeting/app/forms/meeting/template_autocompleter.rb b/modules/meeting/app/forms/meeting/template_autocompleter.rb new file mode 100644 index 00000000000..e236a3d1ec5 --- /dev/null +++ b/modules/meeting/app/forms/meeting/template_autocompleter.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. +#++ + +class Meeting::TemplateAutocompleter < ApplicationForm + form do |f| + f.autocompleter( + name: :template_id, + label: I18n.t(:label_meeting_template), + caption: I18n.t(:caption_meeting_template_select), + autocomplete_options: { + decorated: true, + defaultData: false, + multiple: false, + appendTo: "#new-meeting-dialog", + data: { + "test-selector": "template_id" + } + } + ) do |select| + templates.each do |template| + select.option( + value: template.id, + label: template.title + ) + end + end + end + + def initialize(project:) + super() + @project = project + end + + private + + def templates + Meeting.onetime_templates.where(project: @project).visible.order(:title) + end +end diff --git a/modules/meeting/app/menus/meetings/menu.rb b/modules/meeting/app/menus/meetings/menu.rb index bae9e271458..8c2b0685f5f 100644 --- a/modules/meeting/app/menus/meetings/menu.rb +++ b/modules/meeting/app/menus/meetings/menu.rb @@ -45,7 +45,8 @@ module Meetings [ my_meetings_item, recurring_menu_item, - all_meetings_item + all_meetings_item, + templates_menu_item ].compact end @@ -57,6 +58,21 @@ module Meetings selected: params[:current_href] == my_meetings_href && params[:filters].blank?) end + def templates_menu_item + return unless User.current.logged? + + templates_href = if project + templates_project_meetings_path(project) + else + templates_meetings_path + end + menu_item( + title: I18n.t(:label_meeting_templates), + href: templates_href, + selected: params[:current_href] == templates_href + ) + end + def all_meetings_item all_filter = [{ invited_user_id: { operator: "*", values: [] } }].to_json my_meetings_href = polymorphic_path([project, :meetings]) diff --git a/modules/meeting/app/models/meeting.rb b/modules/meeting/app/models/meeting.rb index bdd149c4d23..d5af7813fbf 100644 --- a/modules/meeting/app/models/meeting.rb +++ b/modules/meeting/app/models/meeting.rb @@ -56,6 +56,8 @@ class Meeting < ApplicationRecord scope :templated, -> { where(template: true) } scope :not_templated, -> { where(template: false) } + scope :onetime_templates, -> { where(template: true, recurring_meeting_id: nil) } + scope :series_templates, -> { where(template: true).where.not(recurring_meeting_id: nil) } scope :not_cancelled, -> { where.not.cancelled } @@ -157,14 +159,16 @@ class Meeting < ApplicationRecord end def start_month - start_time.month + start_time&.month end def start_year - start_time.year + start_time&.year end def end_time + return nil if start_time.nil? + start_time + duration.hours end @@ -176,6 +180,14 @@ class Meeting < ApplicationRecord !!template end + def series_template? + template? && recurring_meeting_id.present? + end + + def onetime_template? + template? && recurring_meeting_id.nil? + end + # One-time meeting time zone # is always in the user's time zone def time_zone @@ -192,6 +204,8 @@ class Meeting < ApplicationRecord end def notify? + return false if onetime_template? + if recurring? recurring_meeting.template.notify else @@ -258,11 +272,25 @@ class Meeting < ApplicationRecord end def send_emails? + return false if onetime_template? return false if template? && recurring_meeting.scheduled_meetings.none? persisted? && notify? end + # Override virtual_start_time methods for onetime templates + def set_initial_values + return if onetime_template? + + super + end + + def validate_date_and_time + return if onetime_template? + + super + end + private def add_new_participants_as_watcher diff --git a/modules/meeting/app/services/meetings/create_service.rb b/modules/meeting/app/services/meetings/create_service.rb index 72044a0f2fc..e061e5d80c4 100644 --- a/modules/meeting/app/services/meetings/create_service.rb +++ b/modules/meeting/app/services/meetings/create_service.rb @@ -32,9 +32,12 @@ module Meetings class CreateService < ::BaseServices::Create protected - def after_perform(call) + def after_perform(call) # rubocop:disable Metrics/AbcSize meeting = call.result + # Skip post creation steps for one-time templates + return call if meeting.onetime_template? + if call.success? && Journal::NotificationConfiguration.active? && meeting.send_emails? meeting.participants.where(invited: true).find_each do |participant| MeetingMailer diff --git a/modules/meeting/app/views/meeting_templates/index.html.erb b/modules/meeting/app/views/meeting_templates/index.html.erb new file mode 100644 index 00000000000..4ccd8bf058d --- /dev/null +++ b/modules/meeting/app/views/meeting_templates/index.html.erb @@ -0,0 +1,38 @@ +<%#-- 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_meeting_templates) %> + +<%= render(MeetingTemplates::IndexPageHeaderComponent.new(project: @project)) %> +<%= render(MeetingTemplates::IndexSubHeaderComponent.new(project: @project)) %> + +<% if @templates.any? %> + <%= render(MeetingTemplates::TableComponent.new(rows: @templates, current_project: @project)) %> +<% else %> + <%= render(Meetings::BlankSlateComponent.new(project: @project, template: true)) %> +<% end %> diff --git a/modules/meeting/config/locales/crowdin/af.yml b/modules/meeting/config/locales/crowdin/af.yml index 934ad58b7fb..37d02ad1c6e 100644 --- a/modules/meeting/config/locales/crowdin/af.yml +++ b/modules/meeting/config/locales/crowdin/af.yml @@ -70,6 +70,7 @@ af: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ af: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "Meeting" label_meeting_plural: "Meetings" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "New Meeting" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ af: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ af: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -491,6 +504,8 @@ af: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ af: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/ar.yml b/modules/meeting/config/locales/crowdin/ar.yml index b633fbfd2ee..0dd3f765308 100644 --- a/modules/meeting/config/locales/crowdin/ar.yml +++ b/modules/meeting/config/locales/crowdin/ar.yml @@ -74,6 +74,7 @@ ar: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -125,7 +126,15 @@ ar: error_notification_with_errors: "فشِل في إرسال الإشعار. لم يتم إشعار المستلمين التالية أسماؤهم: %{recipients}" label_meeting: "الاجتماع" label_meeting_plural: "الاجتماعات" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "اجتماع جديد" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -298,6 +307,9 @@ ar: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -505,6 +517,7 @@ ar: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: لم يتم تعيين المنطقة الزمنية و%{zone} مُفترض. لاختيار منطقتك الزمنية، من فضلك اضغط هنا. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "إنشاء الاجتماعات" @@ -519,6 +532,8 @@ ar: text_duration_in_hours: "المدة بالساعات" text_in_hours: "في الساعات" text_meeting_agenda_for_meeting: 'جدول أعمال للاجتماع "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -624,6 +639,9 @@ ar: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/az.yml b/modules/meeting/config/locales/crowdin/az.yml index 897a70456c5..aa91fd51091 100644 --- a/modules/meeting/config/locales/crowdin/az.yml +++ b/modules/meeting/config/locales/crowdin/az.yml @@ -70,6 +70,7 @@ az: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ az: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "Meeting" label_meeting_plural: "Meetings" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "New Meeting" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ az: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ az: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -491,6 +504,8 @@ az: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ az: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/be.yml b/modules/meeting/config/locales/crowdin/be.yml index 2ec94afa7ca..14c5baff942 100644 --- a/modules/meeting/config/locales/crowdin/be.yml +++ b/modules/meeting/config/locales/crowdin/be.yml @@ -72,6 +72,7 @@ be: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -121,7 +122,15 @@ be: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "Meeting" label_meeting_plural: "Meetings" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "New Meeting" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -288,6 +297,9 @@ be: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -491,6 +503,7 @@ be: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -505,6 +518,8 @@ be: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -610,6 +625,9 @@ be: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/bg.yml b/modules/meeting/config/locales/crowdin/bg.yml index 26d85a3fffc..da322856098 100644 --- a/modules/meeting/config/locales/crowdin/bg.yml +++ b/modules/meeting/config/locales/crowdin/bg.yml @@ -70,6 +70,7 @@ bg: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ bg: error_notification_with_errors: "Не успя да изпрати известие. Следните получатели не могат да бъдат уведомени: %{recipients}" label_meeting: "среща" label_meeting_plural: "Срещи" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Нова среща" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ bg: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ bg: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -491,6 +504,8 @@ bg: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ bg: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/ca.yml b/modules/meeting/config/locales/crowdin/ca.yml index 36ca638daeb..9ef63cd5ade 100644 --- a/modules/meeting/config/locales/crowdin/ca.yml +++ b/modules/meeting/config/locales/crowdin/ca.yml @@ -70,6 +70,7 @@ ca: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ ca: error_notification_with_errors: "Error en enviar la notificació. Els següents recipients no han pogut ser notificats: %{recipients}" label_meeting: "Reunió" label_meeting_plural: "Reunions" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nova reunió" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ ca: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ ca: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No s'ha configurat la zona horària i la %{zone} és assumida. Per seleccionar la teva zona horària, si us plau, fes clic aquí. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Crea reunions" @@ -491,6 +504,8 @@ ca: text_duration_in_hours: "Duració en hores" text_in_hours: "en hores" text_meeting_agenda_for_meeting: 'agenda per la reunió "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Estàs segur que vols tancar l'agenda de la reunió?" @@ -596,6 +611,9 @@ ca: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/ckb-IR.yml b/modules/meeting/config/locales/crowdin/ckb-IR.yml index 730f7c4f592..a792629bb9f 100644 --- a/modules/meeting/config/locales/crowdin/ckb-IR.yml +++ b/modules/meeting/config/locales/crowdin/ckb-IR.yml @@ -70,6 +70,7 @@ ckb-IR: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ ckb-IR: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "Meeting" label_meeting_plural: "Meetings" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "New Meeting" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ ckb-IR: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ ckb-IR: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -491,6 +504,8 @@ ckb-IR: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ ckb-IR: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/cs.yml b/modules/meeting/config/locales/crowdin/cs.yml index 8d35d7a204c..d06d16f4f2f 100644 --- a/modules/meeting/config/locales/crowdin/cs.yml +++ b/modules/meeting/config/locales/crowdin/cs.yml @@ -72,6 +72,7 @@ cs: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -121,7 +122,15 @@ cs: error_notification_with_errors: "Odeslání oznámení se nezdařilo. Následující příjemci nelze upozornit: %{recipients}" label_meeting: "Schůzka" label_meeting_plural: "Schůzky" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nová schůzka" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Nová jednorázová schůzka" label_meeting_new_recurring: "Nová opakovaná schůzka" label_meeting_create: "Vytvořit schůzku" @@ -288,6 +297,9 @@ cs: heading: "Smazat tuto schůzku?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Zrušit výskyt schůzky" heading: "Zrušit výskyt schůzky" @@ -491,6 +503,7 @@ cs: end_series_dialog: title: "Ukončení série schůzek" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Není nastaveno žádné časové pásmo a předpokládá se %{zone} . Chcete-li vybrat časové pásmo, klikněte prosím zde. notice_meeting_updated: "Tato stránka byla aktualizována někým jiným. Pro zobrazení změn znovu načtena." permission_create_meetings: "Vytvořit schůzku\n" @@ -505,6 +518,8 @@ cs: text_duration_in_hours: "Doba trvání v hodinách" text_in_hours: "v hodinách" text_meeting_agenda_for_meeting: 'Agenda schůzky "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Vymazat budoucí výskyty?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Jste si jisti, že chcete ukončit program schůzky?" @@ -610,6 +625,9 @@ cs: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/da.yml b/modules/meeting/config/locales/crowdin/da.yml index eecfc2d7237..a02e03c4668 100644 --- a/modules/meeting/config/locales/crowdin/da.yml +++ b/modules/meeting/config/locales/crowdin/da.yml @@ -70,6 +70,7 @@ da: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ da: error_notification_with_errors: "Kunne ikke sende påmindelse. Følgende modtagere blev ikke nået: %{recipients}" label_meeting: "Møde" label_meeting_plural: "Møder" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nyt møde" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ da: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ da: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Der er ikke sat en tidszone og systemet har valgt %{zone}. For at vælge din egen tidszone, klik venligst her. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Opret møder" @@ -491,6 +504,8 @@ da: text_duration_in_hours: "Duration in hours" text_in_hours: "i timer" text_meeting_agenda_for_meeting: 'dagsorden for mødet "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ da: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/de.yml b/modules/meeting/config/locales/crowdin/de.yml index 6be51b44ccc..d85927f983f 100644 --- a/modules/meeting/config/locales/crowdin/de.yml +++ b/modules/meeting/config/locales/crowdin/de.yml @@ -70,6 +70,7 @@ de: meeting_participant: user_invalid: "ist kein gültiger Teilnehmer." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "ist kein gültiger Teilnehmer." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ de: error_notification_with_errors: "Benachrichtigungversenden fehlgeschlagen. Folgende Empfänger konnten nicht benachrichtigt werden: %{recipients}" label_meeting: "Besprechung" label_meeting_plural: "Besprechungen" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Neue Besprechung" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Neue einmalige Besprechung" label_meeting_new_recurring: "Neue Terminserie" label_meeting_create: "Besprechung erstellen" @@ -278,6 +287,9 @@ de: heading: "Diese Besprechung löschen?" confirmation_message_html: > Diese Aktion kann nicht rückgängig gemacht werden. Bitte prüfen Sie die Angaben, bevor Sie fortfahren. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Wiederkehrende Besprechung absagen" heading: "Dieses Ereignis absagen?" @@ -477,6 +489,7 @@ de: end_series_dialog: title: "Terminserie beenden" notice_successful_notification: "E-Mail-Kalendereinladung an alle Teilnehmer gesendet" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Keine Zeitzone eingestellt und daher %{zone} angenommen. Um Ihre Zeitzone einzustellen, klicken Sie bitte hier. notice_meeting_updated: "Diese Seite wurde von einem anderen Benutzer verändert. Laden Sie neu, um die Änderungen zu sehen." permission_create_meetings: "Besprechungen erstellen" @@ -491,6 +504,8 @@ de: text_duration_in_hours: "Dauer in Stunden" text_in_hours: "in Stunden" text_meeting_agenda_for_meeting: 'die Agenda für die Besprechung "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Zukünftige Ereignisse löschen?" text_meeting_series_end_early: "Durch das Beenden der Terminserie werden alle zukünftigen offenen oder geplanten Besprechungsereignisse gelöscht" text_meeting_closing_are_you_sure: "Sind Sie sicher, dass Sie die Agenda schließen wollen?" @@ -596,6 +611,9 @@ de: text_meeting_in_progress_dropdown_description: "Ergebnisse der Besprechung festhalten, wie z. B. Informationsbedarf oder Entscheidungen, die während der Besprechung getroffen werden." text_meeting_closed_dropdown_description: "Diese Besprechung ist geschlossen. Sie können keine Tagesordnungspunkte mehr hinzufügen/entfernen." text_meeting_draft_banner: "Dieses Meeting ist derzeit ein Entwurf. Diese Besprechung sendet keine Kalenderaktualisierungen oder Einladungen, selbst wenn Sie Besprechungsdetails ändern oder Teilnehmer hinzufügen/entfernen." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Diese Besprechung öffnen und Einladungen verschicken?" text_exit_draft_mode_dialog_subtitle: "Sie können nicht mehr zum Entwurfsmodus zurückkehren, sobald Sie die Besprechung eröffnen." text_exit_draft_mode_dialog_template_title: "Das erste Vorkommen dieser Terminserie öffnen?" diff --git a/modules/meeting/config/locales/crowdin/el.yml b/modules/meeting/config/locales/crowdin/el.yml index befadfa9541..d15def16097 100644 --- a/modules/meeting/config/locales/crowdin/el.yml +++ b/modules/meeting/config/locales/crowdin/el.yml @@ -70,6 +70,7 @@ el: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ el: error_notification_with_errors: "Αποτυχία αποστολής ειδοποίησης. Οι ακόλουθοι παραλήπτες δεν ήταν δυνατό να ειδοποιηθούν: %{recipients}" label_meeting: "Συνάντηση" label_meeting_plural: "Συναντήσεις" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Νέα Συνάντηση" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Δημιουργία συνάντησης" @@ -278,6 +287,9 @@ el: heading: "Διαγραφή αυτής της συνάντησης;" confirmation_message_html: > Αυτή η ενέργεια δεν είναι αναστρέψιμη. Παρακαλώ προχωρήστε με προσοχή. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ el: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Δεν έχει οριστεί ζώνη ώρας και θεωρήθηκε η %{zone}. Για να επιλέξετε την ζώνη ώρας σας, παρακαλούμε κάντε κλικ εδώ. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Δημιουργία συναντήσεων" @@ -491,6 +504,8 @@ el: text_duration_in_hours: "Διάρκεια σε ώρες" text_in_hours: "σε ώρες" text_meeting_agenda_for_meeting: 'ατζέντα για τη συνάντηση "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ el: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/eo.yml b/modules/meeting/config/locales/crowdin/eo.yml index 1057ebeda36..7255cb343dd 100644 --- a/modules/meeting/config/locales/crowdin/eo.yml +++ b/modules/meeting/config/locales/crowdin/eo.yml @@ -70,6 +70,7 @@ eo: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ eo: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "Meeting" label_meeting_plural: "Renkontiĝoj" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nova renkontiĝo" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ eo: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ eo: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -491,6 +504,8 @@ eo: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ eo: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/es.yml b/modules/meeting/config/locales/crowdin/es.yml index a8485411308..00325fe1e17 100644 --- a/modules/meeting/config/locales/crowdin/es.yml +++ b/modules/meeting/config/locales/crowdin/es.yml @@ -70,6 +70,7 @@ es: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ es: error_notification_with_errors: "Error al enviar la notificación. Los siguientes destinatarios no han sido notificados: %{recipients}" label_meeting: "Reunión" label_meeting_plural: "Reuniones" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nueva reunión" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Nueva reunión única" label_meeting_new_recurring: "Nueva reunión periódica" label_meeting_create: "Crear reunión" @@ -278,6 +287,9 @@ es: heading: "¿Eliminar esta reunión?" confirmation_message_html: > Esta acción no es reversible. Proceda con precaución. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancelar repetición de reunión" heading: "¿Desea cancelar esta repetición de reunión?" @@ -477,6 +489,7 @@ es: end_series_dialog: title: "Finalizar serie de reuniones" notice_successful_notification: "Actualización de calendario enviada por correo electrónico a todos los participantes" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No se ha establecido zona horaria y se asume %{zone}. Para elegir su zona horaria, por favor, haga clic aquí. notice_meeting_updated: "Esta página ha sido actualizada por otra persona. Recárguela para ver los cambios." permission_create_meetings: "Crear reuniones" @@ -491,6 +504,8 @@ es: text_duration_in_hours: "Duración en horas" text_in_hours: "en horas" text_meeting_agenda_for_meeting: 'agenda para la reunión "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "¿Eliminar futuras repeticiones?" text_meeting_series_end_early: "Al finalizar la serie, se eliminarán todas las repeticiones futuras abiertas o programadas" text_meeting_closing_are_you_sure: "¿Seguro que quiere cerrar el orden del día de la reunión?" @@ -596,6 +611,9 @@ es: text_meeting_in_progress_dropdown_description: "Documente los resultados, como las necesidades de información o las decisiones tomadas durante la reunión." text_meeting_closed_dropdown_description: "Esta reunión está cerrada. Ya no puede modificar los puntos del orden del día ni los resultados." text_meeting_draft_banner: "Estás en modo Borrador. Esta reunión no enviará actualizaciones ni invitaciones de calendario, incluso si cambias los detalles de la reunión o añades/eliminas participantes." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "¿Abrir esta reunión y enviar invitaciones?" text_exit_draft_mode_dialog_subtitle: "Una vez programada una reunión, no puedes volver al modo Borrador." text_exit_draft_mode_dialog_template_title: "¿Abrir la primera repetición de esta serie de reuniones?" diff --git a/modules/meeting/config/locales/crowdin/et.yml b/modules/meeting/config/locales/crowdin/et.yml index 7ee0cc1c775..aa7b72eca9c 100644 --- a/modules/meeting/config/locales/crowdin/et.yml +++ b/modules/meeting/config/locales/crowdin/et.yml @@ -70,6 +70,7 @@ et: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ et: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "Koosolek" label_meeting_plural: "Koosolekud" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Uus koosolek" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ et: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ et: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Koosolekute loomine" @@ -491,6 +504,8 @@ et: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ et: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/eu.yml b/modules/meeting/config/locales/crowdin/eu.yml index d0c2d90bb90..c76935de7db 100644 --- a/modules/meeting/config/locales/crowdin/eu.yml +++ b/modules/meeting/config/locales/crowdin/eu.yml @@ -70,6 +70,7 @@ eu: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ eu: error_notification_with_errors: "Jakinarazpena bidaltzean akats bat egon da. Ondorengo hartzaileei ezin izan zaie jakinarazi: %{recipients}" label_meeting: "Hitzordua" label_meeting_plural: "Hitzorduak" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Hitzordu berria" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ eu: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ eu: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Ez da ordu-eremurik zehaztu eta %{zone} hartu da ontzat. Ordu-eremua aukeratzeko klikatu hemen, mesedez. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Sortu hitzordua" @@ -491,6 +504,8 @@ eu: text_duration_in_hours: "Iraupena ordutan" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ eu: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/fa.yml b/modules/meeting/config/locales/crowdin/fa.yml index 0097823cf82..4417db3e544 100644 --- a/modules/meeting/config/locales/crowdin/fa.yml +++ b/modules/meeting/config/locales/crowdin/fa.yml @@ -70,6 +70,7 @@ fa: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ fa: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "جلسه" label_meeting_plural: "جلسات" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "ایجاد جلسه" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ fa: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ fa: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "ایجاد جلسات" @@ -491,6 +504,8 @@ fa: text_duration_in_hours: "Duration in hours" text_in_hours: "در ساعات" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ fa: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/fi.yml b/modules/meeting/config/locales/crowdin/fi.yml index 4632c04a073..08b829522a8 100644 --- a/modules/meeting/config/locales/crowdin/fi.yml +++ b/modules/meeting/config/locales/crowdin/fi.yml @@ -70,6 +70,7 @@ fi: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ fi: error_notification_with_errors: "Ei voinut lähettää ilmoitusta. Seuraaville vastaanottajille ei voitu ilmoittaa: %{recipients}" label_meeting: "Kokous" label_meeting_plural: "Kokoukset" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Uusi kokous" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ fi: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ fi: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Aikavyöhykettä ei ole määritetty, joten oletuksena on %{zone}. Valitaksesi aikavyöhykkeen, klikkaa tästä. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Luo kokouksia" @@ -491,6 +504,8 @@ fi: text_duration_in_hours: "Keston tunteina" text_in_hours: "tuntia" text_meeting_agenda_for_meeting: 'kokouksen "%{meeting}" esityslista' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ fi: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/fil.yml b/modules/meeting/config/locales/crowdin/fil.yml index 303d561ad87..585e5aad842 100644 --- a/modules/meeting/config/locales/crowdin/fil.yml +++ b/modules/meeting/config/locales/crowdin/fil.yml @@ -70,6 +70,7 @@ fil: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ fil: error_notification_with_errors: "Bigo sa pagpaadala ng abiso. Ang mga sumusunod tatanggap ay hindi maaabisuhan: %{recipients}" label_meeting: "Pagpupulong" label_meeting_plural: "Mga Pagpupulong" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Bagong Pagpupulong" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ fil: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ fil: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Walang nakatakdang time zone at ang %{zone} ay ipinagpalagay na siyang time zone. Para piliin ang iyong time zone, mangyaring magclick dito. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Lumikha ng mga pulong" @@ -491,6 +504,8 @@ fil: text_duration_in_hours: "Tagal sa oras" text_in_hours: "sa mga oras" text_meeting_agenda_for_meeting: 'adyenda para sa pulong "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ fil: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/fr.yml b/modules/meeting/config/locales/crowdin/fr.yml index af09ce7e2af..7ae2029cc9c 100644 --- a/modules/meeting/config/locales/crowdin/fr.yml +++ b/modules/meeting/config/locales/crowdin/fr.yml @@ -60,7 +60,7 @@ fr: end_date: "Échéance" iterations: "Occurrences" recurring_meeting_interim_response: - start_time: "Start time" + start_time: "Heure de début" meeting_participant: invited: "Invité" attended: "Participations" @@ -68,11 +68,12 @@ fr: errors: models: meeting_participant: - user_invalid: "is not a valid participant." + user_invalid: "n'est pas un participant valide." meeting_agenda_item: - user_invalid: "is not a valid participant." + section_not_belong_to_meeting: "Section does not belong to the same meeting." + user_invalid: "n'est pas un participant valide." recurring_meeting_interim_response: - not_an_occurrence: "is not a valid occurrence time for this recurring meeting" + not_an_occurrence: "n'est pas une heure d'occurrence valide pour cette réunion récurrente" recurring_meeting: must_cover_existing_meetings: one: "Il y a une réunion ouverte dans la série qui n'est pas couverte par le nouvel horaire. Modifiez l'horaire pour inclure toutes les réunions existantes." @@ -117,7 +118,15 @@ fr: error_notification_with_errors: "L'envoi de notifications a échoué. Les destinataires suivant n'ont pas pu être notifiés : %{recipients}" label_meeting: "Réunion" label_meeting_plural: "Réunions" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nouvelle réunion" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Nouvelle réunion ponctuelle" label_meeting_new_recurring: "Nouvelle réunion récurrente" label_meeting_create: "Créer une réunion" @@ -237,9 +246,9 @@ fr: header: "Annulée : Réunion « %{title} »" header_occurrence: "Annulée : occurrence de la réunion « %{title} »" header_series: "Annulée : série de réunions « %{title} »" - summary_occurrence: "An occurrence of '%{title}' has been cancelled by %{actor}, or you have been removed as a participant" - summary_series: "Meeting series '%{title}' has been cancelled by %{actor}, or you have been removed as a participant" - summary: "'%{title}' has been cancelled by %{actor}, or you have been removed as a participant" + summary_occurrence: "Une occurrence de '%{title}' a été annulée par %{actor}, ou vous avez été supprimé en tant que participant" + summary_series: "La série de réunions '%{title}' a été annulée par %{actor}, ou vous avez été retiré en tant que participant" + summary: "'%{title}' a été annulé par %{actor}, ou vous avez été supprimé en tant que participant" date_time: "Date/heure prévue" participant_added: header: "Réunion « %{title} » - Participant ajouté" @@ -253,7 +262,7 @@ fr: summary_series: "%{actor} a supprimé %{participant} de la série de réunions « %{title} »" ended: header_series: "Terminé : Série de rencontres '%{title}'" - summary_series: "Meeting series '%{title}' has been ended by %{actor}" + summary_series: "La série de réunions '%{title}' a été clôturée par %{actor}" updated: header: "La réunion « %{title} » a été mise à jour" summary: "La réunion « %{title} » a été mise à jour par %{actor}" @@ -278,6 +287,9 @@ fr: heading: "Supprimer cette réunion ?" confirmation_message_html: > Cette action est irréversible. Veuillez procéder avec prudence. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Annuler l'occurrence de la réunion" heading: "Annuler cette occurrence de réunion ?" @@ -477,6 +489,7 @@ fr: end_series_dialog: title: "Terminer la série de réunions" notice_successful_notification: "Mise à jour du calendrier par e-mail envoyée à tous les participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Aucun fuseau horaire n'est défini et %{zone} est supposé. Pour choisir votre fuseau horaire, veuillez cliquer ici. notice_meeting_updated: "Cette page a été mise à jour par quelqu'un d'autre. Rechargez pour voir les changements." permission_create_meetings: "Créer des réunions" @@ -491,6 +504,8 @@ fr: text_duration_in_hours: "Durée en heures" text_in_hours: "en heures" text_meeting_agenda_for_meeting: 'ordre du jour de la réunion «%{meeting} »' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Supprimer les futures occurrences ?" text_meeting_series_end_early: "La fin de la série supprimera les futures occurrences de réunions ouvertes ou planifiées" text_meeting_closing_are_you_sure: "Voulez-vous vraiment clôturer l'ordre du jour de la réunion ?" @@ -536,8 +551,8 @@ fr: label_agenda_item_move_up: "Monter" label_agenda_item_move_down: "Descendre" label_agenda_item_duplicate: "Duplicata" - label_agenda_item_duplicate_in_next: "Duplicate in next meeting" - label_agenda_item_duplicate_in_next_title: "Duplicate in next meeting?" + label_agenda_item_duplicate_in_next: "Dupliquer lors de la prochaine réunion" + label_agenda_item_duplicate_in_next_title: "Duplication lors de la prochaine réunion ?" label_agenda_item_add_notes: "Ajouter des notes" label_agenda_item_add_outcome: "Ajouter un résultat" label_agenda_item_work_package_add: "Ajouter lot de travaux" @@ -596,6 +611,9 @@ fr: text_meeting_in_progress_dropdown_description: "Documentez les résultats tels que les besoins d'information ou les décisions prises pendant la réunion." text_meeting_closed_dropdown_description: "Cette réunion est terminée. Vous ne pouvez plus modifier les points de l'ordre du jour ou les résultats." text_meeting_draft_banner: "Vous êtes actuellement en mode brouillon. Cette réunion n'enverra pas de mises à jour de calendrier ni d'invitations, même si vous modifiez les détails de la réunion ou si vous ajoutez/supprimez des participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Ouvrez cette réunion et envoyez des invitations ?" text_exit_draft_mode_dialog_subtitle: "Vous ne pouvez pas revenir au mode brouillon une fois que vous avez planifié une réunion." text_exit_draft_mode_dialog_template_title: "Ouvrez la première occurrence de cette série de réunions ?" diff --git a/modules/meeting/config/locales/crowdin/he.seeders.yml b/modules/meeting/config/locales/crowdin/he.seeders.yml index aedaeba6b1a..410d95d801b 100644 --- a/modules/meeting/config/locales/crowdin/he.seeders.yml +++ b/modules/meeting/config/locales/crowdin/he.seeders.yml @@ -9,21 +9,21 @@ he: demo-project: meeting_series: item_0: - title: Weekly + title: שבועי meeting_agenda_items: item_0: - title: Good news + title: חדשות טובות item_1: - title: Updates from development team + title: עדכונים מצוות הפיתוח item_2: - title: Updates from product team + title: עדכונים מצוות המוצר item_3: - title: Updates from marketing team + title: עדכונים מצוות השיווק item_5: - title: Updates from sales team + title: עדכונים מצוות המכירות item_6: - title: Review of quarterly goals + title: סקירת יעדי רבעון item_7: - title: Core values feedback + title: משוב על ערכי הליבה item_8: - title: General topics + title: נושאים כלליים diff --git a/modules/meeting/config/locales/crowdin/he.yml b/modules/meeting/config/locales/crowdin/he.yml index b080e1de5ef..a3e95a24b71 100644 --- a/modules/meeting/config/locales/crowdin/he.yml +++ b/modules/meeting/config/locales/crowdin/he.yml @@ -72,6 +72,7 @@ he: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -121,7 +122,15 @@ he: error_notification_with_errors: "שליחת ההודעה נכשלה. הנמענים הבאים לא יקבלו את ההודעה: %{recipients}" label_meeting: "פגישה" label_meeting_plural: "פגישות" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "פגישה חדשה" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -288,6 +297,9 @@ he: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -491,6 +503,7 @@ he: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: אין איזור זמן מוגדר, ההערכה היא %{zone}. כדי לבחור את איזור הזמן שלך, אנא לחץ כאן. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "צור פגישות" @@ -505,6 +518,8 @@ he: text_duration_in_hours: "משך הזמן בשעות" text_in_hours: "בשעות" text_meeting_agenda_for_meeting: 'סדר היום לפגישה "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -610,6 +625,9 @@ he: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/hi.yml b/modules/meeting/config/locales/crowdin/hi.yml index 0292c7240dc..c5ce01bbb70 100644 --- a/modules/meeting/config/locales/crowdin/hi.yml +++ b/modules/meeting/config/locales/crowdin/hi.yml @@ -70,6 +70,7 @@ hi: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ hi: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "Meeting" label_meeting_plural: "Meetings" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "New Meeting" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ hi: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ hi: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -491,6 +504,8 @@ hi: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ hi: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/hr.yml b/modules/meeting/config/locales/crowdin/hr.yml index fd01e47fecd..6808bd82efa 100644 --- a/modules/meeting/config/locales/crowdin/hr.yml +++ b/modules/meeting/config/locales/crowdin/hr.yml @@ -71,6 +71,7 @@ hr: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -119,7 +120,15 @@ hr: error_notification_with_errors: "Neuspjelo slanje notifikacije. Sljedeći korisnici nisu mogli biti obaviješteni: %{recipients}" label_meeting: "Sastanak" label_meeting_plural: "Sastanci" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Novi sastanak" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -283,6 +292,9 @@ hr: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -484,6 +496,7 @@ hr: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Vremenska zona nije postavljena na osnovu zamišljene %{zone} zone. Da bi ste odabrali vremensku zonu molim vas kliknite ovdje. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Dodaj sastanke" @@ -498,6 +511,8 @@ hr: text_duration_in_hours: "Duration in hours" text_in_hours: "u satima" text_meeting_agenda_for_meeting: 'dnevni red za sastank "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -603,6 +618,9 @@ hr: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/hu.yml b/modules/meeting/config/locales/crowdin/hu.yml index 25b4e6d239b..b60274c9397 100644 --- a/modules/meeting/config/locales/crowdin/hu.yml +++ b/modules/meeting/config/locales/crowdin/hu.yml @@ -70,6 +70,7 @@ hu: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ hu: error_notification_with_errors: "Nem sikerült elküldeni az értesítőt. A következő címzettek nem lettek értesítve: %{recipients}" label_meeting: "Megbeszélés" label_meeting_plural: "Megbeszélések" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Új megbeszélés" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Új egyszeri megbeszélés" label_meeting_new_recurring: "Új ismétlődő megbeszélés" label_meeting_create: "Megbeszélés létrehozása" @@ -278,6 +287,9 @@ hu: heading: "Megbeszélés törlése?" confirmation_message_html: > Nem visszavonható művelet! Biztos benne? + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ hu: end_series_dialog: title: "Ismétlődő megbeszélés befejezése" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Nincs időzóna beállítva, %{zone} a feltételezett. Időzóna beállításához kattintson ide. notice_meeting_updated: "Ezt az oldalt valaki más frissítette. A változások megtekintéséhez töltse újra." permission_create_meetings: "Megbeszélések létrehozása" @@ -491,6 +504,8 @@ hu: text_duration_in_hours: "Hossza (óra)" text_in_hours: "órában" text_meeting_agenda_for_meeting: 'a "%{meeting}" megbeszélés napirendje' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Biztosan le szeretnéd zárni a megbeszélés napirendjét?" @@ -596,6 +611,9 @@ hu: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/id.yml b/modules/meeting/config/locales/crowdin/id.yml index fcf33ecb8d3..70c8b50eb78 100644 --- a/modules/meeting/config/locales/crowdin/id.yml +++ b/modules/meeting/config/locales/crowdin/id.yml @@ -69,6 +69,7 @@ id: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -115,7 +116,15 @@ id: error_notification_with_errors: "Gagal mengirim notifikasi. Berikut penerima yang tidak terkirim: %{recipients}" label_meeting: "Rapat" label_meeting_plural: "Rapat" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Rapat Baru" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Rapat satu waktu baru" label_meeting_new_recurring: "Rapat berulang baru" label_meeting_create: "Buat rapat" @@ -273,6 +282,9 @@ id: heading: "Hapus rapat ini?" confirmation_message_html: > Tindakan ini tidak dapat dibatalkan. Harap berhati-hati saat melakukannya. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -470,6 +482,7 @@ id: end_series_dialog: title: "Akhiri seri rapat" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Tidak ada zona waktu yang ditetapkan sehingga diasumsikan %{zone}. Untuk memilih zona waktu, silakan mengeklik di sini. notice_meeting_updated: "Halaman ini telah diperbarui oleh orang lain. Segarkan halaman untuk melihat perubahan." permission_create_meetings: "Membuat rapat" @@ -484,6 +497,8 @@ id: text_duration_in_hours: "Durasi dalam jam" text_in_hours: "dalam jam" text_meeting_agenda_for_meeting: 'agenda untuk rapat "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Hapus acara yang akan datang?" text_meeting_series_end_early: "Mengakhiri seri ini akan menghapus semua rapat terbuka atau rapat yang akan datang" text_meeting_closing_are_you_sure: "Apakah Anda yakin ingin menutup agenda rapat?" @@ -589,6 +604,9 @@ id: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "Saat ini, Anda berada dalam mode draf. Rapat ini tidak akan mengirimkan pembaruan kalender atau undangan, bahkan jika Anda mengubah detail rapat atau menambahkan/menghapus peserta." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Buka rapat ini dan kirim undangan?" text_exit_draft_mode_dialog_subtitle: "Anda tidak dapat kembali ke mode draf setellah Anda menjadwalkan rapat." text_exit_draft_mode_dialog_template_title: "Buka acara yang pertama dalam seri rapat ini?" diff --git a/modules/meeting/config/locales/crowdin/it.yml b/modules/meeting/config/locales/crowdin/it.yml index 64288513a68..1f132fcd611 100644 --- a/modules/meeting/config/locales/crowdin/it.yml +++ b/modules/meeting/config/locales/crowdin/it.yml @@ -70,6 +70,7 @@ it: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ it: error_notification_with_errors: "Impossibile inviare notifica. Non è possibile notificare i seguenti destinatari: %{recipients}" label_meeting: "Riunione" label_meeting_plural: "Riunioni" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nuova riunione" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Nuova riunione una tantum" label_meeting_new_recurring: "Nuova riunione ricorrente" label_meeting_create: "Crea riunione" @@ -278,6 +287,9 @@ it: heading: "Cancellare questa riunione?" confirmation_message_html: > Questa azione è irreversibile. Procedi con cautela. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancella l'occorrenza della riunione" heading: "Cancellare questa occorrenza della riunione?" @@ -477,6 +489,7 @@ it: end_series_dialog: title: "Termina serie di riunioni" notice_successful_notification: "Aggiornamento del calendario via e-mail inviato a tutti i partecipanti" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Nessun fuso orario è impostato e la %{zone} è un requisito necessario. Per scegliere il tuo fuso orario, fare clic qui. notice_meeting_updated: "Questa pagina è stata aggiornata da qualcun altro. Ricarica per visualizzare le modifiche." permission_create_meetings: "Creare riunioni" @@ -491,6 +504,8 @@ it: text_duration_in_hours: "Durata in ore" text_in_hours: "in ore" text_meeting_agenda_for_meeting: 'ordine del giorno della riunione "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Cancellare le occorrenze future?" text_meeting_series_end_early: "La chiusura della serie cancellerà tutte le future occorrenze di riunioni aperte o programmate" text_meeting_closing_are_you_sure: "Vuoi davvero chiudere l'agenda di questa riunione?" @@ -596,6 +611,9 @@ it: text_meeting_in_progress_dropdown_description: "Documenta i risultati, come le esigenze informative o le decisioni prese durante la riunione." text_meeting_closed_dropdown_description: "Questa riunione è chiusa. Non puoi più modificare i punti all'ordine del giorno." text_meeting_draft_banner: "Al momento sei in modalità bozza. Questa riunione non invierà alcun aggiornamento del calendario né inviti, anche in caso di modifica dei dettagli della riunione o di aggiunta o rimozione di partecipanti." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Aprire questa riunione e inviare gli inviti?" text_exit_draft_mode_dialog_subtitle: "Non può tornare alla modalità bozza una volta programmata una riunione." text_exit_draft_mode_dialog_template_title: "Aprire la prima occorrenza di questa serie di riunioni?" diff --git a/modules/meeting/config/locales/crowdin/ja.yml b/modules/meeting/config/locales/crowdin/ja.yml index 56ab02acd4e..cf508ab71d3 100644 --- a/modules/meeting/config/locales/crowdin/ja.yml +++ b/modules/meeting/config/locales/crowdin/ja.yml @@ -69,6 +69,7 @@ ja: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -115,7 +116,15 @@ ja: error_notification_with_errors: "通知を送信できませんでした。次の受信者には通知できませんでした: %{recipients}" label_meeting: "会議" label_meeting_plural: "会議" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "新しい会議" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "新しい一度限りの会議" label_meeting_new_recurring: "定期的な会議の作成" label_meeting_create: "会議を作成" @@ -273,6 +282,9 @@ ja: heading: "この会議を削除しますか?" confirmation_message_html: > この操作は元に戻せません。続行しますか? + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "会議のキャンセル" heading: "この会議をキャンセルしますか?" @@ -470,6 +482,7 @@ ja: end_series_dialog: title: "一連の会議を終了" notice_successful_notification: "すべての出席者にカレンダーの更新をメールしました" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: タイムゾーンが設定されていない場合、%{zone} が使用されます。タイムゾーンを選択するには、ここをクリックしてください。 notice_meeting_updated: "このページは他の誰かによって更新されました。変更を表示するには再読み込みしてください。" permission_create_meetings: "会議を作成" @@ -484,6 +497,8 @@ ja: text_duration_in_hours: "期間(時間)" text_in_hours: "数時間以内" text_meeting_agenda_for_meeting: '会議の議題 "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "今後の予定を削除しますか?" text_meeting_series_end_early: "シリーズを終了すると、今後開かれた会議や予定された会議の発生が削除されます" text_meeting_closing_are_you_sure: "会議アジェンダを終了してもよろしいですか?" @@ -589,6 +604,9 @@ ja: text_meeting_in_progress_dropdown_description: "会議中に取られた情報のニーズや意思決定などの成果を記録します。" text_meeting_closed_dropdown_description: "この会議は終了しました。これ以上、議題や結果を変更することはできません。" text_meeting_draft_banner: "現在下書きモードです。 会議の詳細を変更したり出席者を追加/削除したりしても,この会議はカレンダーの更新や招待状を送信しません。" + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "この会議を開いて招待を送信しますか?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/ka.yml b/modules/meeting/config/locales/crowdin/ka.yml index 908765f192c..2efc4cdf9c5 100644 --- a/modules/meeting/config/locales/crowdin/ka.yml +++ b/modules/meeting/config/locales/crowdin/ka.yml @@ -70,6 +70,7 @@ ka: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ ka: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "შეხვედრა" label_meeting_plural: "შეხვედრები" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "ახალი შეხვედრა" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ ka: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ ka: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -491,6 +504,8 @@ ka: text_duration_in_hours: "Duration in hours" text_in_hours: "საათებში" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ ka: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/kk.yml b/modules/meeting/config/locales/crowdin/kk.yml index 138cb931532..2a621f920f6 100644 --- a/modules/meeting/config/locales/crowdin/kk.yml +++ b/modules/meeting/config/locales/crowdin/kk.yml @@ -70,6 +70,7 @@ kk: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ kk: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "Meeting" label_meeting_plural: "Meetings" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "New Meeting" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ kk: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ kk: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -491,6 +504,8 @@ kk: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ kk: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/ko.yml b/modules/meeting/config/locales/crowdin/ko.yml index 2007da31668..8a9352355c2 100644 --- a/modules/meeting/config/locales/crowdin/ko.yml +++ b/modules/meeting/config/locales/crowdin/ko.yml @@ -69,6 +69,7 @@ ko: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -115,7 +116,15 @@ ko: error_notification_with_errors: "알림을 보내지 못했습니다. 다음 수신자에게 알리지 못했습니다. %{recipients}" label_meeting: "미팅" label_meeting_plural: "미팅" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "새 미팅" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "새로운 일회성 미팅" label_meeting_new_recurring: "새로운 반복 미팅" label_meeting_create: "미팅 생성" @@ -273,6 +282,9 @@ ko: heading: "이 미팅을 삭제하시겠습니까?" confirmation_message_html: > 이 작업은 되돌릴 수 없습니다. 주의하여 진행하세요. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "미팅 항목 취소" heading: "이 미팅 항목을 취소하시겠습니까?" @@ -470,6 +482,7 @@ ko: end_series_dialog: title: "미팅 시리즈 종료" notice_successful_notification: "모든 참가자에게 보내는 이메일 캘린더 업데이트" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: 표준 시간대가 설정되지 않았으며 %{zone}(으)로 간주됩니다. 해당 표준 시간대를 선택하려면 여기를 클릭하세요. notice_meeting_updated: "이 페이지는 다른 사람이 업데이트했습니다. 변경 사항을 보려면 다시 로드하세요." permission_create_meetings: "미팅 생성" @@ -484,6 +497,8 @@ ko: text_duration_in_hours: "기간(시간)" text_in_hours: " 시간" text_meeting_agenda_for_meeting: '미팅 "%{meeting}"에 대한 의제' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "향후 항목을 삭제하시겠습니까?" text_meeting_series_end_early: "이 시리즈를 종료하면 향후 오픈 또는 예정된 미팅 항목이 모두 삭제됩니다" text_meeting_closing_are_you_sure: "미팅 어젠더를 닫으시겠습니까?" @@ -589,6 +604,9 @@ ko: text_meeting_in_progress_dropdown_description: "미팅 중에 이루어진 결정 사항 또는 필요한 정보 등의 결과를 문서화합니다." text_meeting_closed_dropdown_description: "이 미팅은 종료되었습니다. 더 이상 의제 항목 또는 결과를 수정할 수 없습니다." text_meeting_draft_banner: "초안 모드에 현재 있습니다. 이 미팅은 미팅 세부 정보를 변경하거나 참가자를 추가/제거하더라도 캘린더 업데이트나 초대장을 전송하지 않습니다." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "이 미팅을 열고 초대장을 보내시겠습니까?" text_exit_draft_mode_dialog_subtitle: "미팅을 예약한 후에는 초안 모드로 돌아갈 수 없습니다." text_exit_draft_mode_dialog_template_title: "이 미팅 시리즈의 첫 번째 항목을 열어보시겠습니까?" diff --git a/modules/meeting/config/locales/crowdin/lt.yml b/modules/meeting/config/locales/crowdin/lt.yml index cc11f3d0c9b..dbc68a9267e 100644 --- a/modules/meeting/config/locales/crowdin/lt.yml +++ b/modules/meeting/config/locales/crowdin/lt.yml @@ -72,6 +72,7 @@ lt: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -121,7 +122,15 @@ lt: error_notification_with_errors: "Nepavyko išsiųsti pranešimo. Toliau išvardinti adresatai nebuvo įspėti: %{recipients}" label_meeting: "Pasitarimas" label_meeting_plural: "Pasitarimai" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Naujas pasitarimas" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -288,6 +297,9 @@ lt: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -491,6 +503,7 @@ lt: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Laiko zona nenustatyta ir %{zone} zona yra rekomenduojama. Kad pasirinktumėte laiko zoną, paspauskite čia. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Sukurti pasitarimus" @@ -505,6 +518,8 @@ lt: text_duration_in_hours: "Trukmė valandomis" text_in_hours: " valandos(-ų) " text_meeting_agenda_for_meeting: 'pasitarimo „%{meeting}“ darbotvarkę' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Ar tikrai norite uždaryti susitikimo planą?" @@ -610,6 +625,9 @@ lt: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/lv.yml b/modules/meeting/config/locales/crowdin/lv.yml index 5d460399aa6..5fbe4e3b3c9 100644 --- a/modules/meeting/config/locales/crowdin/lv.yml +++ b/modules/meeting/config/locales/crowdin/lv.yml @@ -71,6 +71,7 @@ lv: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -119,7 +120,15 @@ lv: error_notification_with_errors: "Neizdevās nosūtīt paziņojumu. Nebija iespējams sasniegt šādus adresātus: %{recipients}" label_meeting: "Sanāksmes" label_meeting_plural: "Sanāksmes" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Jauna sanāksme" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Izveidot sanāksmi" @@ -283,6 +292,9 @@ lv: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -484,6 +496,7 @@ lv: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -498,6 +511,8 @@ lv: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -603,6 +618,9 @@ lv: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/mn.yml b/modules/meeting/config/locales/crowdin/mn.yml index 6968ca507f5..7d40a116f67 100644 --- a/modules/meeting/config/locales/crowdin/mn.yml +++ b/modules/meeting/config/locales/crowdin/mn.yml @@ -70,6 +70,7 @@ mn: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ mn: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "Meeting" label_meeting_plural: "Meetings" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "New Meeting" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ mn: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ mn: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -491,6 +504,8 @@ mn: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ mn: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/ms.yml b/modules/meeting/config/locales/crowdin/ms.yml index 2469d8ec515..7e718524ab4 100644 --- a/modules/meeting/config/locales/crowdin/ms.yml +++ b/modules/meeting/config/locales/crowdin/ms.yml @@ -69,6 +69,7 @@ ms: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -115,7 +116,15 @@ ms: error_notification_with_errors: "Gagal untuk hantar pemberitahuan. Penerima berikut tidak dapat dimaklumkan: %{recipients}" label_meeting: "Mesyuarat" label_meeting_plural: "Mesyuarat-mesyuarat" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Mesyuarat Baharu" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Mesyuarat baru sekali" label_meeting_new_recurring: "Mesyuarat berulang yang baru" label_meeting_create: "Buat mesyuarat" @@ -273,6 +282,9 @@ ms: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -470,6 +482,7 @@ ms: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Tiada zon waktu yang ditetapkan dan %{zone} adalah diandaikan. Untuk pilih zon waktu anda, sila klik sini. notice_meeting_updated: "Halaman ini telah dikemas kini oleh orang lain. Muat semula untuk melihat perubahan." permission_create_meetings: "Cipta mesyuarat" @@ -484,6 +497,8 @@ ms: text_duration_in_hours: "Tempoh masa dalam jam" text_in_hours: "dalam jam" text_meeting_agenda_for_meeting: 'agenda untuk mesyuarat "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Adakah anda pasti anda ingin menutup agenda mesyuarat?" @@ -589,6 +604,9 @@ ms: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/ne.yml b/modules/meeting/config/locales/crowdin/ne.yml index 33936a539d3..d086140dee7 100644 --- a/modules/meeting/config/locales/crowdin/ne.yml +++ b/modules/meeting/config/locales/crowdin/ne.yml @@ -70,6 +70,7 @@ ne: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ ne: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "Meeting" label_meeting_plural: "Meetings" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "New Meeting" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ ne: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ ne: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -491,6 +504,8 @@ ne: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ ne: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/nl.yml b/modules/meeting/config/locales/crowdin/nl.yml index 22249dc22d2..3e9ee2a2133 100644 --- a/modules/meeting/config/locales/crowdin/nl.yml +++ b/modules/meeting/config/locales/crowdin/nl.yml @@ -70,6 +70,7 @@ nl: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ nl: error_notification_with_errors: "Notificatie verzenden mislukt. De volgende geadresseerden konden niet worden gemeld: %{recipients}" label_meeting: "Vergadering" label_meeting_plural: "Vergaderingen" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nieuwe vergadering" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Nieuwe eenmalige vergadering" label_meeting_new_recurring: "Nieuwe terugkerende vergadering" label_meeting_create: "Creëer vergadering" @@ -278,6 +287,9 @@ nl: heading: "Verwijder deze vergadering?" confirmation_message_html: > Deze actie is niet omkeerbaar. Ga voorzichtig te werk. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ nl: end_series_dialog: title: "Vergaderreeks beëindigen" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Geen tijdzone is ingesteld en %{zone} is aangenomen. Om uw tijdzone te kiezen, klik dan hier. notice_meeting_updated: "Deze pagina is door iemand anders bijgewerkt. Herlaad om wijzigingen te bekijken." permission_create_meetings: "Creëer vergaderingen" @@ -491,6 +504,8 @@ nl: text_duration_in_hours: "Duur in uren" text_in_hours: "in uren" text_meeting_agenda_for_meeting: 'agenda voor de vergadering "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Toekomstige gebeurtenissen verwijderen?" text_meeting_series_end_early: "Als u de serie beëindigt, worden alle toekomstige open of geplande vergaderingen verwijderd" text_meeting_closing_are_you_sure: "Weet je zeker dat je de vergaderagenda wilt sluiten?" @@ -596,6 +611,9 @@ nl: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/no.yml b/modules/meeting/config/locales/crowdin/no.yml index 878a82c7f5e..d1d844f21d9 100644 --- a/modules/meeting/config/locales/crowdin/no.yml +++ b/modules/meeting/config/locales/crowdin/no.yml @@ -70,6 +70,7 @@ meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ error_notification_with_errors: "Kan ikke sende påminning. Følgende mottakere kan ikke varsles: %{recipients}" label_meeting: "Møte" label_meeting_plural: "Møter" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nytt møte" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Ingen tidssone angis og %{zone} antas. Vennligst klikk her for å velge egen tidssone. notice_meeting_updated: "Denne siden er oppdatert av noen andre. Last siden på nytt for å se endringer." permission_create_meetings: "Opprett møter" @@ -491,6 +504,8 @@ text_duration_in_hours: "Varighet i timer" text_in_hours: "i timer" text_meeting_agenda_for_meeting: 'saksliste for møtet "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Er du sikker på at du vil lukke agendaen?" @@ -596,6 +611,9 @@ text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/pl.yml b/modules/meeting/config/locales/crowdin/pl.yml index 77d6eb59272..56a3c1c0cd9 100644 --- a/modules/meeting/config/locales/crowdin/pl.yml +++ b/modules/meeting/config/locales/crowdin/pl.yml @@ -72,6 +72,7 @@ pl: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -121,7 +122,15 @@ pl: error_notification_with_errors: "Nie udało się wysłać powiadomienie do adresatów: %{recipients}" label_meeting: "Spotkanie" label_meeting_plural: "Spotkania" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nowe spotkanie" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Nowe spotkanie jednorazowe" label_meeting_new_recurring: "Nowe spotkanie cykliczne" label_meeting_create: "Utwórz spotkanie" @@ -288,6 +297,9 @@ pl: heading: "Usunąć to spotkanie?" confirmation_message_html: > To działanie jest nieodwracalne. Postępuj ostrożnie. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Anuluj wystąpienie spotkania" heading: "Anulować to wystąpienie spotkania?" @@ -491,6 +503,7 @@ pl: end_series_dialog: title: "Zakończ serię spotkań" notice_successful_notification: "Aktualizacja zaproszenia do kalendarza jest wysyłana pocztą elektroniczną do wszystkich uczestników" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: 'Została ustawiona domyślna strefa czasowa: %{zone}. Aby zmienić strefę czasową, kliknij tutaj.' notice_meeting_updated: "Ta strona została zaktualizowana przez kogoś innego. Załaduj ją ponownie, aby wyświetlić zmiany." permission_create_meetings: "Utwórz spotkanie" @@ -505,6 +518,8 @@ pl: text_duration_in_hours: "Czas trwania w godzinach" text_in_hours: "w godzinach" text_meeting_agenda_for_meeting: 'agenda spotkania "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Usunąć przyszłe wystąpienia?" text_meeting_series_end_early: "Zakończenie serii spowoduje usunięcie wszelkich przyszłych otwartych lub zaplanowanych wystąpień spotkania" text_meeting_closing_are_you_sure: "Czy na pewno chcesz zamknąć plan spotkania?" @@ -610,6 +625,9 @@ pl: text_meeting_in_progress_dropdown_description: "Udokumentuj wyniki, takie jak potrzeby informacyjne lub decyzje podjęte podczas spotkania." text_meeting_closed_dropdown_description: "To spotkanie jest zamknięte. Nie można już modyfikować pozycji planu spotkania ani wyników." text_meeting_draft_banner: "Jesteś teraz w trybie wersji roboczej. To spotkanie nie wyśle żadnych aktualizacji kalendarza ani zaproszeń, nawet jeśli zmienisz szczegóły spotkania lub dodasz/usuniesz uczestników." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Otworzyć to spotkanie i wysłać zaproszenia?" text_exit_draft_mode_dialog_subtitle: "Po zaplanowaniu spotkania nie można powrócić do trybu wersji roboczej." text_exit_draft_mode_dialog_template_title: "Otworzyć pierwsze wystąpienie z tej serii spotkań?" diff --git a/modules/meeting/config/locales/crowdin/pt-BR.yml b/modules/meeting/config/locales/crowdin/pt-BR.yml index dee51c6750b..fe8478ef851 100644 --- a/modules/meeting/config/locales/crowdin/pt-BR.yml +++ b/modules/meeting/config/locales/crowdin/pt-BR.yml @@ -70,6 +70,7 @@ pt-BR: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ pt-BR: error_notification_with_errors: "Falha ao enviar notificação. Os seguintes destinatários não puderam ser notificados: %{recipients}" label_meeting: "Reunião" label_meeting_plural: "Reuniões" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nova Reunião" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Nova reunião única" label_meeting_new_recurring: "Nova reunião recorrente" label_meeting_create: "Criar reunião" @@ -278,6 +287,9 @@ pt-BR: heading: "Excluir esta reunião?" confirmation_message_html: > Essa ação não pode ser desfeita. Prossiga com cautela. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancelar ocorrência da reunião" heading: "Cancelar esta ocorrência da reunião?" @@ -477,6 +489,7 @@ pt-BR: end_series_dialog: title: "Encerrar série de reuniões" notice_successful_notification: "Atualização do calendário enviada por e-mail para todos os participantes" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Nenhum fuso horário está definido, portanto assumiu-se %{zone}. Para escolher o seu fuso horário, clique aqui. notice_meeting_updated: "Esta página foi atualizada por outra pessoa. Recarregue para visualizar as alterações." permission_create_meetings: "Criar reuniões" @@ -491,6 +504,8 @@ pt-BR: text_duration_in_hours: "Duração em horas" text_in_hours: "em horas" text_meeting_agenda_for_meeting: 'agenda para a reunião "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Excluir ocorrências futuras?" text_meeting_series_end_early: "Encerrar a série excluirá qualquer reunião futura, aberta ou agendada" text_meeting_closing_are_you_sure: "Você tem certeza de que deseja encerar a pauta da reunião?" @@ -596,6 +611,9 @@ pt-BR: text_meeting_in_progress_dropdown_description: "Registre os resultados como solicitações de informação ou decisões tomadas durante a reunião." text_meeting_closed_dropdown_description: "Esta reunião está encerrada. Já não é possível modificar os pontos da ordem de trabalhos ou os resultados." text_meeting_draft_banner: "Você está no modo rascunho. Esta reunião não enviará nenhuma atualização de calendário ou convite, mesmo que você altere os detalhes da reunião ou adicione/remoca participantes." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Abrir esta reunião e enviar convites?" text_exit_draft_mode_dialog_subtitle: "Você não pode retornar ao modo rascunho após você agendar a reunião." text_exit_draft_mode_dialog_template_title: "Abrir a primeira ocorrência desta série de reuniões?" diff --git a/modules/meeting/config/locales/crowdin/pt-PT.yml b/modules/meeting/config/locales/crowdin/pt-PT.yml index c06dec3fe7e..02f5468c954 100644 --- a/modules/meeting/config/locales/crowdin/pt-PT.yml +++ b/modules/meeting/config/locales/crowdin/pt-PT.yml @@ -70,6 +70,7 @@ pt-PT: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ pt-PT: error_notification_with_errors: "Falha ao enviar a notificação. Os seguintes destinatários não podem ser notificados: %{recipients}" label_meeting: "Reunião" label_meeting_plural: "Reuniões" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nova reunião" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Nova reunião única" label_meeting_new_recurring: "Nova reunião recorrente" label_meeting_create: "Criar reunião" @@ -278,6 +287,9 @@ pt-PT: heading: "Eliminar esta reunião?" confirmation_message_html: > Esta ação não pode ser anulada. Proceda com cuidado. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancelar ocorrência de reunião" heading: "Cancelar esta ocorrência de reunião?" @@ -477,6 +489,7 @@ pt-PT: end_series_dialog: title: "Terminar série de reuniões" notice_successful_notification: "Atualização de calendário por e-mail enviada a todos os participantes" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Sem fuso horário definido, presume-se %{zone}. Para escolher o seu fuso horário, por favor clique aqui. notice_meeting_updated: "Esta página foi atualizada por outra pessoa. Recarregue para ver as alterações." permission_create_meetings: "Criar reuniões" @@ -491,6 +504,8 @@ pt-PT: text_duration_in_hours: "Duração em horas" text_in_hours: "em horas" text_meeting_agenda_for_meeting: 'agenda para a reunião "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Eliminar ocorrências futuras?" text_meeting_series_end_early: "Ao terminar a série, elimina todas as ocorrências de reuniões futuras abertas ou agendadas" text_meeting_closing_are_you_sure: "Tem a certeza de que deseja fechar a agenda da reunião?" @@ -596,6 +611,9 @@ pt-PT: text_meeting_in_progress_dropdown_description: "Documentar os resultados, como as necessidades de informação ou as decisões tomadas durante a reunião." text_meeting_closed_dropdown_description: "Esta reunião está encerrada. Já não é possível modificar os pontos da ordem de trabalhos ou os resultados." text_meeting_draft_banner: "Está no modo de rascunho. Esta reunião não enviará atualizações de calendário ou convites, mesmo que altere os detalhes da reunião ou adicione/remova participantes." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Abrir esta reunião e enviar convites?" text_exit_draft_mode_dialog_subtitle: "Não pode regressar ao modo de rascunho após agendar uma reunião." text_exit_draft_mode_dialog_template_title: "Abrir a primeira ocorrência desta série de reuniões?" diff --git a/modules/meeting/config/locales/crowdin/ro.yml b/modules/meeting/config/locales/crowdin/ro.yml index 24676ce2e6e..36217f5af22 100644 --- a/modules/meeting/config/locales/crowdin/ro.yml +++ b/modules/meeting/config/locales/crowdin/ro.yml @@ -71,6 +71,7 @@ ro: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -119,7 +120,15 @@ ro: error_notification_with_errors: "Nu s-a reușit trimiterea notificării. Următorii destinatari nu au putut fi notificați: %{recipients}" label_meeting: "ID Întâlnire" label_meeting_plural: "Întâlniri" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Noua întâlnire" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "Întâlnire periodică nouă" label_meeting_create: "Creează întâlnire" @@ -283,6 +292,9 @@ ro: heading: "Ștergi această întâlnire?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -484,6 +496,7 @@ ro: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Nu este setat niciun fus orar și se presupune %{zone}. Pentru a vă alege fusul orar, vă rugăm să faceți clic aici. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Creați întâlniri" @@ -498,6 +511,8 @@ ro: text_duration_in_hours: "Durata în ore" text_in_hours: "Ore" text_meeting_agenda_for_meeting: 'ordinea de zi a reuniunii "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -603,6 +618,9 @@ ro: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/ru.yml b/modules/meeting/config/locales/crowdin/ru.yml index abc93647701..c656ca15357 100644 --- a/modules/meeting/config/locales/crowdin/ru.yml +++ b/modules/meeting/config/locales/crowdin/ru.yml @@ -72,6 +72,7 @@ ru: meeting_participant: user_invalid: "не является действительным участником." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "не является действительным участником." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -121,7 +122,15 @@ ru: error_notification_with_errors: "Не удалось отправить уведомление. Следующие получатели не могут быть уведомлены: %{recipients}" label_meeting: "Совещание" label_meeting_plural: "Совещания" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Новое совещание" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Новое однократное совещание" label_meeting_new_recurring: "Новое повторяющееся совещание" label_meeting_create: "Создать совещание" @@ -288,6 +297,9 @@ ru: heading: "Удалить это совещание?" confirmation_message_html: > Это действие не обратимо. Пожалуйста, действуйте с осторожностью. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Отменить это событие" heading: "Отменить это событие?" @@ -491,6 +503,7 @@ ru: end_series_dialog: title: "Завершить серию совещаний" notice_successful_notification: "Обновление календаря отправлено всем участникам по электронной почте" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Не установлен часовой пояс и применена %{zone}. Чтобы выбрать часовой пояс, пожалуйста, нажмите сюда. notice_meeting_updated: "Эта страница была обновлена кем-то другим. Перезагрузите страницу, чтобы просмотреть изменения." permission_create_meetings: "Создание совещания" @@ -505,6 +518,8 @@ ru: text_duration_in_hours: "Длительность в часах" text_in_hours: "в часах" text_meeting_agenda_for_meeting: 'Повестка дня встречи «%{meeting}»' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Удалить будущие события?" text_meeting_series_end_early: "Завершение серии удалит любые будущие открытые или запланированные события совещания" text_meeting_closing_are_you_sure: "Вы уверены, что хотите закрыть повестку дня собрания?" @@ -610,6 +625,9 @@ ru: text_meeting_in_progress_dropdown_description: "Итоги совещания, такие как информация или решения, принятые в ходе совещания." text_meeting_closed_dropdown_description: "Совещание закрыто. Вы больше не можете изменять пункты повестки дня или его результаты." text_meeting_draft_banner: "В настоящее время Вы находитесь в режиме черновика. Это совещание не будет рассылать никаких обновлений календаря или приглашений, даже если Вы измените детали совещания или добавите/удалите участников." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Открыть это совещание и разослать приглашения?" text_exit_draft_mode_dialog_subtitle: "Вы не можете вернуться в режим черновика после того, как запланировали совещание." text_exit_draft_mode_dialog_template_title: "Открыть первое совещание из этой серии?" diff --git a/modules/meeting/config/locales/crowdin/rw.yml b/modules/meeting/config/locales/crowdin/rw.yml index acef788a27d..a052d210e4d 100644 --- a/modules/meeting/config/locales/crowdin/rw.yml +++ b/modules/meeting/config/locales/crowdin/rw.yml @@ -70,6 +70,7 @@ rw: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ rw: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "Meeting" label_meeting_plural: "Meetings" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "New Meeting" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ rw: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ rw: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -491,6 +504,8 @@ rw: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ rw: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/si.yml b/modules/meeting/config/locales/crowdin/si.yml index 1d4544b1d16..c3a20286a91 100644 --- a/modules/meeting/config/locales/crowdin/si.yml +++ b/modules/meeting/config/locales/crowdin/si.yml @@ -70,6 +70,7 @@ si: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ si: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "Meeting" label_meeting_plural: "Meetings" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "New Meeting" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ si: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ si: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -491,6 +504,8 @@ si: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ si: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/sk.yml b/modules/meeting/config/locales/crowdin/sk.yml index dfb92a6d95b..0099b207b19 100644 --- a/modules/meeting/config/locales/crowdin/sk.yml +++ b/modules/meeting/config/locales/crowdin/sk.yml @@ -72,6 +72,7 @@ sk: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -121,7 +122,15 @@ sk: error_notification_with_errors: "Nepodarilo sa odoslať notifikáciu. Nasledovní príjemcovia nemohli byť oboznámení: %{recipients}" label_meeting: "Stretnutie" label_meeting_plural: "Stretnutia" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nové stretnutie" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -288,6 +297,9 @@ sk: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -491,6 +503,7 @@ sk: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Časové pásmo nebolo špecificky nastavené, použilo sa teda %{zone}. Ak chcete vybrať iné časové pásmo, kliknite prosím tu. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Vytvárať stretnutia" @@ -505,6 +518,8 @@ sk: text_duration_in_hours: "Doba trvania v hodinách" text_in_hours: "v hodinách" text_meeting_agenda_for_meeting: 'agenda stretnutia "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -610,6 +625,9 @@ sk: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/sl.yml b/modules/meeting/config/locales/crowdin/sl.yml index 41bab3bd6a3..a26338b80c7 100644 --- a/modules/meeting/config/locales/crowdin/sl.yml +++ b/modules/meeting/config/locales/crowdin/sl.yml @@ -72,6 +72,7 @@ sl: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -121,7 +122,15 @@ sl: error_notification_with_errors: "Pošiljanje obvestil je bilo neuspešno. Naslednji naslovniki nisi bili obveščeni: %{recipients}" label_meeting: "Sestanek" label_meeting_plural: "Sestanki" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nov sestanek" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -288,6 +297,9 @@ sl: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -491,6 +503,7 @@ sl: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Časovni pas ni določen, %{zone} je izbran avtomatsko. Za določitev časovnega pasu kliknite tukaj. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Ustvarite sestanke" @@ -505,6 +518,8 @@ sl: text_duration_in_hours: "Trajanje v urah" text_in_hours: "v urah" text_meeting_agenda_for_meeting: 'dnevni red za sestanek "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -610,6 +625,9 @@ sl: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/sr.yml b/modules/meeting/config/locales/crowdin/sr.yml index ad52fc19467..571c9d929f3 100644 --- a/modules/meeting/config/locales/crowdin/sr.yml +++ b/modules/meeting/config/locales/crowdin/sr.yml @@ -71,6 +71,7 @@ sr: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -119,7 +120,15 @@ sr: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "Meeting" label_meeting_plural: "Meetings" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "New Meeting" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -283,6 +292,9 @@ sr: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -484,6 +496,7 @@ sr: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -498,6 +511,8 @@ sr: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -603,6 +618,9 @@ sr: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/sv.yml b/modules/meeting/config/locales/crowdin/sv.yml index 8acd1a72bfc..d57feb9f9ff 100644 --- a/modules/meeting/config/locales/crowdin/sv.yml +++ b/modules/meeting/config/locales/crowdin/sv.yml @@ -70,6 +70,7 @@ sv: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ sv: error_notification_with_errors: "Det gick inte att skicka underrättelse. Följande mottagare kunde inte underrättas: %{recipients}" label_meeting: "Möte" label_meeting_plural: "Möten" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Nytt möte" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Nytt engångsmöte" label_meeting_new_recurring: "Nytt återkommande möte" label_meeting_create: "Skapa möte" @@ -278,6 +287,9 @@ sv: heading: "Ta bort detta möte?" confirmation_message_html: > Denna åtgärd är inte reversibel. Vänligen fortsätt med försiktighet. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Avbryt förekomst av möte" heading: "Avbryt denna förekomst av möte?" @@ -477,6 +489,7 @@ sv: end_series_dialog: title: "Avsluta mötesserier" notice_successful_notification: "Uppdatering av e-postkalender skickad till alla deltagare" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Ingen tidszon angiven och %{zone} antas. För att välja din tidszon, vänligen klicka här. notice_meeting_updated: "Denna sida har uppdaterats av någon annan. Ladda om för att se ändringar." permission_create_meetings: "Skapa möten" @@ -491,6 +504,8 @@ sv: text_duration_in_hours: "Varaktighet i timmar" text_in_hours: "i timmar" text_meeting_agenda_for_meeting: 'agenda för mötet "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Ta bort framtida händelser?" text_meeting_series_end_early: "Om du avslutar serien raderas alla framtida öppna eller schemalagda möteshändelser" text_meeting_closing_are_you_sure: "Är du säker på att du vill stänga möteskalendern?" @@ -596,6 +611,9 @@ sv: text_meeting_in_progress_dropdown_description: "Dokumentresultat som informationsbehov eller beslut som fattas under mötet." text_meeting_closed_dropdown_description: "Det här mötet är stängt. Du kan inte ändra agendapunkter eller resultat längre." text_meeting_draft_banner: "Du befinner dig för närvarande i utkastläge. Det här mötet kommer inte att skicka ut några kalenderuppdateringar eller inbjudningar, även om du ändrar mötesdetaljer eller lägger till/tar bort deltagare." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Öppna detta möte och skicka inbjudningar?" text_exit_draft_mode_dialog_subtitle: "Du kan inte gå tillbaka till utkastet när du schemalägger ett möte." text_exit_draft_mode_dialog_template_title: "Öppna den första förekomsten av denna mötesserie?" diff --git a/modules/meeting/config/locales/crowdin/th.yml b/modules/meeting/config/locales/crowdin/th.yml index 3d6e206420c..46962101485 100644 --- a/modules/meeting/config/locales/crowdin/th.yml +++ b/modules/meeting/config/locales/crowdin/th.yml @@ -69,6 +69,7 @@ th: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -115,7 +116,15 @@ th: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "ประชุม" label_meeting_plural: "ประชุม" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "New Meeting" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -273,6 +282,9 @@ th: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -470,6 +482,7 @@ th: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -484,6 +497,8 @@ th: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -589,6 +604,9 @@ th: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/tr.yml b/modules/meeting/config/locales/crowdin/tr.yml index 62c096202e4..07b6c8b277b 100644 --- a/modules/meeting/config/locales/crowdin/tr.yml +++ b/modules/meeting/config/locales/crowdin/tr.yml @@ -70,6 +70,7 @@ tr: meeting_participant: user_invalid: "geçerli bir katılımcı değildir." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "geçerli bir katılımcı değildir." recurring_meeting_interim_response: not_an_occurrence: "yinelenen toplantı için geçerli bir gerçekleşme zamanı değil" @@ -117,7 +118,15 @@ tr: error_notification_with_errors: "Bildirim hatalı. Aşağıdaki alıcılar bilgilendirilemedi: %{recipients}" label_meeting: "Toplantı" label_meeting_plural: "Toplantılar" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Yeni Toplantı" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Tek seferlik yeni toplantı" label_meeting_new_recurring: "Yeni yinelenen toplantı" label_meeting_create: "Toplantı oluştur" @@ -278,6 +287,9 @@ tr: heading: "Bu toplantı silinsin mi?" confirmation_message_html: > Bu işlem geri döndürülemez. Lütfen dikkatli olun. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Bu toplantıyı iptal et" heading: "Sadece bu toplantı mı iptal edilsin?" @@ -477,6 +489,7 @@ tr: end_series_dialog: title: "Toplantı serisinin bitir" notice_successful_notification: "Tüm katılımcılara gönderilen e-posta takvimi güncellemesi" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Saat dilimi ayarlanmadı ve %{zone} kabul edildi. Saat dilimini seçmek için lütfen buraya tıklayın. notice_meeting_updated: "Bu sayfa başka biri tarafından güncellendi. Değişiklikleri görmek için yeniden yükleyin." permission_create_meetings: "Toplantı oluşturma" @@ -491,6 +504,8 @@ tr: text_duration_in_hours: "Saat süresi" text_in_hours: "saat olarak" text_meeting_agenda_for_meeting: 'toplantı gündemi "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Gelecekteki toplantılar silinsin mi?" text_meeting_series_end_early: "Seriyi sonlandırmak, gelecekteki tüm açık veya planlanmış toplantıları silecektir" text_meeting_closing_are_you_sure: "Toplantı gündemini kapatmak istediğinizden emin misiniz?" @@ -596,6 +611,9 @@ tr: text_meeting_in_progress_dropdown_description: "Toplantı sırasında alınan kararları ya da bilgi gereksinimleri sonuçlarını belgeleyin." text_meeting_closed_dropdown_description: "Bu toplantı kapalı. Gündem maddelerini ya da sonuçları artık düzenleyemezsiniz." text_meeting_draft_banner: "Şu anda taslak modundasınız. Toplantı ayrıntılarını değiştirseniz veya katılımcı ekleyip çıkarsanız bile bu toplantı herhangi bir takvim güncellemesi veya davet göndermeyecektir." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Bu toplantıyı açıp davetiye gönderelim mi?" text_exit_draft_mode_dialog_subtitle: "Bir toplantı planladıktan sonra taslak moduna geri dönemezsiniz." text_exit_draft_mode_dialog_template_title: "Bu toplantı serisinin ilkini açar mısınız?" diff --git a/modules/meeting/config/locales/crowdin/uk.yml b/modules/meeting/config/locales/crowdin/uk.yml index 85e6dd4c78f..35cead4401c 100644 --- a/modules/meeting/config/locales/crowdin/uk.yml +++ b/modules/meeting/config/locales/crowdin/uk.yml @@ -72,6 +72,7 @@ uk: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -121,7 +122,15 @@ uk: error_notification_with_errors: "Не вдалося надіслати сповіщення. Неможливо отримати сповіщення про таких одержувачів: %{recipients}" label_meeting: "Зустріч" label_meeting_plural: "Зустрічі" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Нова зустріч" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Нова одноразова нарада" label_meeting_new_recurring: "Нова повторювана нарада" label_meeting_create: "Створити нараду" @@ -288,6 +297,9 @@ uk: heading: "Видалити цю нараду?" confirmation_message_html: > Зверніть увагу: цю дію не можна відмінити. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Скасувати нараду серії" heading: "Скасувати цю нараду серії?" @@ -491,6 +503,7 @@ uk: end_series_dialog: title: "Завершити серію нарад" notice_successful_notification: "Оновлення з календаря надіслано електронною поштою всім учасникам" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Часовий пояс не встановлено і %{zone} передбачається. Щоб вибрати часовий пояс, натисніть тут. notice_meeting_updated: "Цю сторінку оновив інший користувач. Перезавантажте її, щоб побачити зміни." permission_create_meetings: "Створюйте зустрічі" @@ -505,6 +518,8 @@ uk: text_duration_in_hours: "Тривалість у годинах" text_in_hours: "у годинах" text_meeting_agenda_for_meeting: 'порядку денного засідання %{meeting}' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Видалити майбутні наради серії?" text_meeting_series_end_early: "Завершення серії призведе до видалення всіх наступних відкритих або запланованих нарад" text_meeting_closing_are_you_sure: "Справді закрити порядок денний заходу?" @@ -610,6 +625,9 @@ uk: text_meeting_in_progress_dropdown_description: "Результати документа, такі як потреби в інформації або рішення, прийняті під час наради." text_meeting_closed_dropdown_description: "Це закрита нарада. Ви більше не можете змінювати пункти порядку денного або результати." text_meeting_draft_banner: "Зараз ви працюєте в режимі чернетки: оновлення або запрошення з календаря не надсилатимуться щодо цієї наради, навіть якщо змінити її дані або додати/видалити учасників." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Відкрити цю нараду й надіслати запрошення?" text_exit_draft_mode_dialog_subtitle: "Запланувавши нараду, не можна повернутися в режим чернетки." text_exit_draft_mode_dialog_template_title: "Відкрити першу нараду цієї серії?" diff --git a/modules/meeting/config/locales/crowdin/uz.yml b/modules/meeting/config/locales/crowdin/uz.yml index f30483ec914..96d23784fc1 100644 --- a/modules/meeting/config/locales/crowdin/uz.yml +++ b/modules/meeting/config/locales/crowdin/uz.yml @@ -70,6 +70,7 @@ uz: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -117,7 +118,15 @@ uz: error_notification_with_errors: "Failed to send notification. The following recipients could not be notified: %{recipients}" label_meeting: "Meeting" label_meeting_plural: "Meetings" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "New Meeting" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -278,6 +287,9 @@ uz: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -477,6 +489,7 @@ uz: end_series_dialog: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." permission_create_meetings: "Create meetings" @@ -491,6 +504,8 @@ uz: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -596,6 +611,9 @@ uz: text_meeting_in_progress_dropdown_description: "Document outcomes like information needs or decisions taken during the meeting." text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/locales/crowdin/vi.yml b/modules/meeting/config/locales/crowdin/vi.yml index 4d87a0e5599..32967296f72 100644 --- a/modules/meeting/config/locales/crowdin/vi.yml +++ b/modules/meeting/config/locales/crowdin/vi.yml @@ -69,6 +69,7 @@ vi: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -115,7 +116,15 @@ vi: error_notification_with_errors: "Không thể gửi thông báo. Những người không thể nhận được thông báo: %{recipients}" label_meeting: "Cuộc họp" label_meeting_plural: "Những cuộc họp" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "Cuộc họp mới" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "Cuộc họp một lần mới" label_meeting_new_recurring: "Cuộc họp định kỳ mới" label_meeting_create: "Tạo cuộc họp" @@ -273,6 +282,9 @@ vi: heading: "Xóa cuộc họp này?" confirmation_message_html: > Hành động này không thể đảo ngược. Hãy tiến hành thận trọng. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Hủy cuộc họp diễn ra" heading: "Hủy cuộc họp này diễn ra?" @@ -470,6 +482,7 @@ vi: end_series_dialog: title: "Kết thúc chuỗi cuộc họp" notice_successful_notification: "Cập nhật lịch qua email được gửi tới tất cả người tham gia" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: Chưa đặt múi giờ và %{zone} đã được chọn. Để chọn múi giowf của bạn, nhấn vào đây. notice_meeting_updated: "Trang này đã được cập nhật bởi người khác. Tải lại để xem các thay đổi." permission_create_meetings: "Tạo cuộc họp" @@ -484,6 +497,8 @@ vi: text_duration_in_hours: "Thời lượng tính bằng giờ" text_in_hours: "bằng giờ" text_meeting_agenda_for_meeting: 'Các ý chính cho cuộc họp "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Xóa các lần xuất hiện trong tương lai?" text_meeting_series_end_early: "Việc kết thúc chuỗi sẽ xóa mọi cuộc họp mở hoặc đã lên lịch trong tương lai" text_meeting_closing_are_you_sure: "Bạn có chắc chắn muốn đóng chương trình cuộc họp không?" @@ -589,6 +604,9 @@ vi: text_meeting_in_progress_dropdown_description: "Ghi lại các kết quả như nhu cầu thông tin hoặc các quyết định được đưa ra trong cuộc họp." text_meeting_closed_dropdown_description: "Cuộc họp này đã kết thúc. Bạn không thể sửa đổi các mục hoặc kết quả của chương trình nghị sự nữa." text_meeting_draft_banner: "Bạn hiện đang ở chế độ nháp. Cuộc họp này sẽ không gửi bất kỳ cập nhật lịch hoặc lời mời nào, ngay cả khi bạn thay đổi chi tiết cuộc họp hoặc thêm/xóa người tham gia." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Mở cuộc họp này và gửi lời mời?" text_exit_draft_mode_dialog_subtitle: "Bạn không thể quay lại chế độ nháp sau khi lên lịch cuộc họp." text_exit_draft_mode_dialog_template_title: "Mở lần xuất hiện đầu tiên của chuỗi cuộc họp này?" diff --git a/modules/meeting/config/locales/crowdin/zh-CN.yml b/modules/meeting/config/locales/crowdin/zh-CN.yml index 94575700f97..216357e7fde 100644 --- a/modules/meeting/config/locales/crowdin/zh-CN.yml +++ b/modules/meeting/config/locales/crowdin/zh-CN.yml @@ -69,6 +69,7 @@ zh-CN: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -115,7 +116,15 @@ zh-CN: error_notification_with_errors: "发送通知失败。无法通知下列收件人:%{recipients}" label_meeting: "会议" label_meeting_plural: "会议" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "新增会议" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "新的动态会议" label_meeting_new_recurring: "新的定期会议" label_meeting_create: "创建会议" @@ -273,6 +282,9 @@ zh-CN: heading: "删除这个会议?" confirmation_message_html: > 此操作不可逆。请谨慎处理。 + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "取消会议事件" heading: "取消此会议事件?" @@ -470,6 +482,7 @@ zh-CN: end_series_dialog: title: "结束会议系列" notice_successful_notification: "已向所有与会者发送电子邮件日历更新" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: 没有设定时区,预设时区为 %{zone}。要选择您的时区,请单击此处。 notice_meeting_updated: "此页面已被其他人更新。重新加载以查看更改。" permission_create_meetings: "创建会议" @@ -484,6 +497,8 @@ zh-CN: text_duration_in_hours: "持续时间(单位:小时)" text_in_hours: "小时" text_meeting_agenda_for_meeting: '“%{meeting}”会议议程' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "删除未来发生的事件?" text_meeting_series_end_early: "结束系列将删除任何将来的公开会议或预定会议的事件" text_meeting_closing_are_you_sure: "确定要关闭该会议议程吗?" @@ -589,6 +604,9 @@ zh-CN: text_meeting_in_progress_dropdown_description: "记录会议期间的信息需求或决策等成果。" text_meeting_closed_dropdown_description: "此会议已关闭。您不能再修改议程项目或成果。" text_meeting_draft_banner: "您目前处于草稿模式。即使您更改了会议详细信息或添加/移除了参与者,此会议也不会发送任何日历更新或邀请。" + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "是否打开此会议并发送邀请?" text_exit_draft_mode_dialog_subtitle: "会议安排完成后,您将无法返回到草稿模式。" text_exit_draft_mode_dialog_template_title: "是否打开此会议系列的第一个会议?" diff --git a/modules/meeting/config/locales/crowdin/zh-TW.yml b/modules/meeting/config/locales/crowdin/zh-TW.yml index a1996180d20..354846abb56 100644 --- a/modules/meeting/config/locales/crowdin/zh-TW.yml +++ b/modules/meeting/config/locales/crowdin/zh-TW.yml @@ -69,6 +69,7 @@ zh-TW: meeting_participant: user_invalid: "不是有效的參與者。" meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "不是有效的參與者。" recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -115,7 +116,15 @@ zh-TW: error_notification_with_errors: "傳送通知失敗。以下的收件者將不會被通知到: %{recipients}" label_meeting: "會議" label_meeting_plural: "會議" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "新增會議" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "建立一次性會議" label_meeting_new_recurring: "新的重複性會議" label_meeting_create: "新增會議" @@ -273,6 +282,9 @@ zh-TW: heading: "刪除這次會議?" confirmation_message_html: > 此動作無法還原。請小心操作。 + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "取消會議事件" heading: "取消此會議事件?" @@ -470,6 +482,7 @@ zh-TW: end_series_dialog: title: "結束系列會議" notice_successful_notification: "透過電子郵件更新行事曆,並發送給所有參與者" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: 沒有設定時區,預設時區為 %{zone} 。請按這裡選擇您的時區。 notice_meeting_updated: "本頁已由他人更新。重新載入以查看變更。" permission_create_meetings: "建立會議" @@ -484,6 +497,8 @@ zh-TW: text_duration_in_hours: "持續時間 (小時)" text_in_hours: "小時數" text_meeting_agenda_for_meeting: '%{meeting} 的會議大綱' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "刪除未來會議?" text_meeting_series_end_early: "結束該系列會刪除任何未來開放或排程的會議事件" text_meeting_closing_are_you_sure: "確實要關閉議程嗎?" @@ -589,6 +604,9 @@ zh-TW: text_meeting_in_progress_dropdown_description: "記錄會議期間的結果,例如資訊需求或所做的決策。" text_meeting_closed_dropdown_description: "此會議已結束,您無法再修改議程項目或結果。" text_meeting_draft_banner: "您目前處於草稿模式。即使您變更會議詳細資訊或新增/移除參與者,此會議也不會傳送任何行事曆更新或邀請。" + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "開啟此會議並發送邀請函?" text_exit_draft_mode_dialog_subtitle: "一旦排定會議,就無法返回草稿模式。" text_exit_draft_mode_dialog_template_title: "要開啟此會議系列的第一場會議嗎?" diff --git a/modules/meeting/config/locales/en.yml b/modules/meeting/config/locales/en.yml index 4d4058759f6..c2da851b58c 100644 --- a/modules/meeting/config/locales/en.yml +++ b/modules/meeting/config/locales/en.yml @@ -81,6 +81,7 @@ en: meeting_participant: user_invalid: "is not a valid participant." meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." user_invalid: "is not a valid participant." recurring_meeting_interim_response: not_an_occurrence: "is not a valid occurrence time for this recurring meeting" @@ -134,7 +135,15 @@ en: label_meeting: "Meeting" label_meeting_plural: "Meetings" + label_meeting_templates: "Templates" + label_meeting_template: "Template" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" + label_meeting_template_delete: "Delete template" + label_meeting_template_edit: "Edit template" + label_meeting_create_from_template: "Create meeting from template" label_meeting_new: "New Meeting" + caption_meeting_template_select: "Select a template to automatically copy its agenda items" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" label_meeting_create: "Create meeting" @@ -296,6 +305,9 @@ en: heading: "Delete this meeting?" confirmation_message_html: > This action is not reversible. Please proceed with caution. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Cancel meeting occurrence" heading: "Cancel this meeting occurrence?" @@ -526,6 +538,7 @@ en: title: "End meeting series" notice_successful_notification: "Email calendar update sent to all participants" + notice_meeting_template_created: "Template successfully created" notice_timezone_missing: No time zone is set and %{zone} is assumed. To choose your time zone, please click here. notice_meeting_updated: "This page has been updated by someone else. Reload to view changes." @@ -543,6 +556,8 @@ en: text_duration_in_hours: "Duration in hours" text_in_hours: "in hours" text_meeting_agenda_for_meeting: 'agenda for the meeting "%{meeting}"' + text_meeting_template_blank_slate_heading: "There are no templates to display" + text_meeting_template_blank_slate: "You can create a new template for one-time meetings" text_meeting_series_end_early_heading: "Delete future occurrences?" text_meeting_series_end_early: "Ending the series will delete any future open or scheduled meeting occurrences" text_meeting_closing_are_you_sure: "Are you sure you want to close the meeting agenda?" @@ -665,6 +680,9 @@ en: text_meeting_closed_dropdown_description: "This meeting is closed. You cannot modify agenda items or outcomes anymore." text_meeting_draft_banner: "You are currently in draft mode. This meeting will not send out any calendar updates or invites, even if you change meeting details or add/remove participants." + text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." + text_onetime_meeting_template_empty_heading: "Your meeting template is empty" + text_onetime_meeting_template_description: "You can add agenda items, participants, and attachments to this template." text_exit_draft_mode_dialog_title: "Open this meeting and send invites?" text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" diff --git a/modules/meeting/config/routes.rb b/modules/meeting/config/routes.rb index 5876e00cc76..6679305e5f2 100644 --- a/modules/meeting/config/routes.rb +++ b/modules/meeting/config/routes.rb @@ -44,6 +44,10 @@ Rails.application.routes.draw do get :fetch_timezone get "ical/:token", controller: "meetings/ical", action: :index, as: "ical_feed" + + get "templates", action: :index, controller: "meeting_templates", as: "templates" + get "templates/new_dialog", action: :new_dialog, controller: "meeting_templates", as: "new_dialog_template" + post "templates", action: :create, controller: "meeting_templates", as: "create_template" end end @@ -54,6 +58,10 @@ Rails.application.routes.draw do get :new_dialog get "menu" => "meetings/menus#show" get :fetch_timezone + + get "templates", action: :index, controller: "meeting_templates", as: "templates" + get "templates/new_dialog", action: :new_dialog, controller: "meeting_templates", as: "new_dialog_template" + post "templates", action: :create, controller: "meeting_templates", as: "create_template" end member do diff --git a/modules/meeting/lib/open_project/meeting/engine.rb b/modules/meeting/lib/open_project/meeting/engine.rb index e059dc6f254..ea092bf9af0 100644 --- a/modules/meeting/lib/open_project/meeting/engine.rb +++ b/modules/meeting/lib/open_project/meeting/engine.rb @@ -47,7 +47,8 @@ module OpenProject::Meeting presentation generate_pdf_dialog history], "meetings/menus": %i[show], work_package_meetings_tab: %i[index count], - recurring_meetings: %i[index show new create download_ics] + recurring_meetings: %i[index show new create download_ics], + meeting_templates: %i[index] }, permissible_on: :project permission :create_meetings, @@ -55,7 +56,8 @@ module OpenProject::Meeting meetings: %i[new create copy new_dialog fetch_timezone], recurring_meetings: %i[new create copy init template_completed], "recurring_meetings/schedule": %i[update_text], - "meetings/menus": %i[show] + "meetings/menus": %i[show], + meeting_templates: %i[new create new_dialog] }, dependencies: :view_meetings, permissible_on: :project, diff --git a/modules/meeting/spec/contracts/meeting_agenda_items/create_contract_spec.rb b/modules/meeting/spec/contracts/meeting_agenda_items/create_contract_spec.rb index b81829134ae..a3f4b7b2165 100644 --- a/modules/meeting/spec/contracts/meeting_agenda_items/create_contract_spec.rb +++ b/modules/meeting/spec/contracts/meeting_agenda_items/create_contract_spec.rb @@ -102,4 +102,19 @@ RSpec.describe MeetingAgendaItems::CreateContract do include_examples "contract reuses the model errors" do let(:user) { build_stubbed(:user) } end + + context "when creating an agenda item and using a section from another meeting" do + let(:other_meeting) { create(:meeting) } + let(:other_section) { create(:meeting_section, meeting: other_meeting) } + let(:user) do + create(:user, member_with_permissions: { project => %i[view_meetings manage_agendas] }) + end + + let(:item) { build(:meeting_agenda_item, meeting: meeting, meeting_section: other_section) } + + it "is invalid" do + expect(contract).not_to be_valid + expect(contract.errors[:base]).to include("Section does not belong to the same meeting.") + end + end end diff --git a/modules/meeting/spec/factories/meeting_factory.rb b/modules/meeting/spec/factories/meeting_factory.rb index 8bbc1b1500a..1711cc37caa 100644 --- a/modules/meeting/spec/factories/meeting_factory.rb +++ b/modules/meeting/spec/factories/meeting_factory.rb @@ -62,5 +62,11 @@ FactoryBot.define do end end end + + factory :onetime_template do |meeting| + meeting.sequence(:title) { |n| "Onetime template #{n}" } + template { true } + recurring_meeting { nil } + end end end diff --git a/modules/meeting/spec/features/meeting_templates/create_meeting_from_template_spec.rb b/modules/meeting/spec/features/meeting_templates/create_meeting_from_template_spec.rb new file mode 100644 index 00000000000..c5f7308c984 --- /dev/null +++ b/modules/meeting/spec/features/meeting_templates/create_meeting_from_template_spec.rb @@ -0,0 +1,265 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require "spec_helper" + +require_relative "../../support/pages/meetings/show" +require_relative "../../support/pages/meetings/index" + +RSpec.describe "Create meeting from template", :js do + include Components::Autocompleter::NgSelectAutocompleteHelpers + + shared_let(:admin) { create(:admin) } + shared_let(:project) { create(:project, enabled_module_names: %i[meetings]) } + shared_let(:other_project) { create(:project, enabled_module_names: %i[meetings]) } + + let(:meetings_page) { Pages::Meetings::Index.new(project:) } + let(:show_page) { Pages::Meetings::Show.new(Meeting.last) } + + before { login_as(admin) } + + describe "creating meeting from template using template selector" do + context "with templates in project" do + let!(:template) do + create(:onetime_template, project:, title: "Standup Template").tap do |t| + create(:meeting_agenda_item, meeting: t, title: "Updates") + create(:meeting_agenda_item, meeting: t, title: "Blockers") + create(:meeting_agenda_item, meeting: t, title: "Next steps") + end + end + + let!(:other_template) do + create(:onetime_template, project:, title: "Retro Template").tap do |t| + create(:meeting_agenda_item, meeting: t, title: "What went well") + create(:meeting_agenda_item, meeting: t, title: "What to improve") + end + end + + before do + meetings_page.visit! + end + + it "can create a meeting from a template with agenda items copied" do + meetings_page.click_on "add-meeting-button" + meetings_page.click_on "One-time" + + expect(page).to have_dialog("New one-time meeting") + + within_dialog "New one-time meeting" do + expect(page).to have_css('[data-test-selector="template_id"]') + + select_autocomplete find('[data-test-selector="template_id"]'), + query: "Standup", + select_text: "Standup Template", + results_selector: "body" + + fill_in "Title", with: "Tomorrow's standup" + + click_button "Create" + end + + wait_for_network_idle + + meeting = Meeting.last + + expect(meeting.template).to be false + expect(meeting.title).to eq("Tomorrow's standup") + + expect(meeting.agenda_items.count).to eq(3) + + expect(page).to have_text("Updates") + expect(page).to have_text("Blockers") + expect(page).to have_text("Next steps") + end + + it "can create a meeting without selecting a template" do + meetings_page.click_on "add-meeting-button" + meetings_page.click_on "One-time" + + expect(page).to have_dialog("New one-time meeting") + + within_dialog "New one-time meeting" do + expect(page).to have_css('[data-test-selector="template_id"]') + + fill_in "Title", with: "Non template meeting" + + click_button "Create" + end + + wait_for_network_idle + + meeting = Meeting.last + expect(meeting.template).to be false + expect(meeting.title).to eq("Non template meeting") + expect(meeting.agenda_items.count).to eq(0) + end + + it "shows all project templates in autocompleter" do + meetings_page.click_on "add-meeting-button" + meetings_page.click_on "One-time" + + within_dialog "New one-time meeting" do + find('[data-test-selector="template_id"]').click + + expect(page).to have_text("Standup Template") + expect(page).to have_text("Retro Template") + end + end + end + + context "with no templates in project" do + before do + Meeting.onetime_templates.where(project:).destroy_all + meetings_page.visit! + end + + it "does not show template selector when no templates exist" do + meetings_page.click_on "add-meeting-button" + meetings_page.click_on "One-time" + + expect(page).to have_dialog("New one-time meeting") + + within_dialog "New one-time meeting" do + expect(page).to have_no_css('[data-test-selector="template_id"]') + end + end + end + + context "with templates from other project" do + let!(:current_project_template) do + create(:onetime_template, project:, title: "Current project template") + end + + let!(:other_project_template) do + create(:onetime_template, project: other_project, title: "Other project template") + end + + before do + meetings_page.visit! + end + + it "only shows templates from current project" do + meetings_page.click_on "add-meeting-button" + meetings_page.click_on "One-time" + + within_dialog "New one-time meeting" do + find('[data-test-selector="template_id"]').click + + expect(page).to have_text("Current project template") + + expect(page).to have_no_text("Other project template") + end + end + end + end + + describe "creating meeting from template using 'Create from template' button" do + let!(:template) do + create(:onetime_template, project:, title: "Planning template").tap do |t| + create(:meeting_agenda_item, meeting: t, title: "Goals") + create(:meeting_agenda_item, meeting: t, title: "Tasks") + create(:meeting_agenda_item, meeting: t, title: "Timeline") + end + end + + let(:template_show_page) { Pages::Meetings::Show.new(template) } + + before do + template_show_page.visit! + end + + it "can create a meeting from template page with button" do + expect(page).to have_link("Create meeting from template") + click_link "Create meeting from template" + + expect(page).to have_dialog("New one-time meeting") + + within_dialog "New one-time meeting" do + expect(page).to have_no_css('[data-test-selector="template_id"]') + + fill_in "Title", with: "Sprint planning" + + click_button "Create" + end + + wait_for_network_idle + + meeting = Meeting.last + expect(meeting.template).to be false + expect(meeting.title).to eq("Sprint planning") + + expect(meeting.agenda_items.count).to eq(3) + + expect(page).to have_text("Goals") + expect(page).to have_text("Tasks") + expect(page).to have_text("Timeline") + end + end + + describe "permissions" do + let!(:template) do + create(:onetime_template, + project:, + title: "Permission test template") + end + + context "as user with view_meetings only" do + let(:user_view_only) do + create(:user, member_with_permissions: { project => [:view_meetings] }) + end + + before do + logout + login_as(user_view_only) + visit project_meeting_path(project, template) + end + + it "does not show 'Create meeting from template' button" do + expect(page).to have_no_link("Create meeting from template") + end + end + + context "as user with create_meetings permission" do + let(:user_with_create) do + create(:user, member_with_permissions: { project => %i[view_meetings create_meetings] }) + end + + before do + logout + login_as(user_with_create) + visit project_meeting_path(project, template) + end + + it "shows 'Create meeting from template' button" do + expect(page).to have_link("Create meeting from template") + end + end + end +end diff --git a/modules/meeting/spec/features/meeting_templates/onetime_template_crud_spec.rb b/modules/meeting/spec/features/meeting_templates/onetime_template_crud_spec.rb new file mode 100644 index 00000000000..41b8e74f5b9 --- /dev/null +++ b/modules/meeting/spec/features/meeting_templates/onetime_template_crud_spec.rb @@ -0,0 +1,297 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require "spec_helper" + +RSpec.describe "Onetime templates CRUD", :js do + shared_let(:admin) { create(:admin) } + shared_let(:project) { create(:project, enabled_module_names: %i[meetings]) } + shared_let(:other_project) { create(:project, enabled_module_names: %i[meetings]) } + + before { login_as(admin) } + + describe "viewing templates index" do + context "with existing templates" do + shared_let(:onetime_template1) { create(:onetime_template, project:, title: "Template 1") } + shared_let(:onetime_template2) { create(:onetime_template, project: other_project, title: "Template 2") } + shared_let(:recurring_meeting) { create(:recurring_meeting, project:) } + shared_let(:series_template) { recurring_meeting.template } + shared_let(:regular_meeting) { create(:meeting, template: false, project:, title: "Regular meeting") } + + before { visit templates_meetings_path } + + it "shows all onetime templates" do + expect(page).to have_text("Template 1") + expect(page).to have_text("Template 2") + end + + it "excludes series templates" do + expect(page).to have_no_text(series_template.title) + end + + it "excludes regular meetings" do + expect(page).to have_no_text("Regular meeting") + end + + it "shows project names" do + expect(page).to have_text(project.name) + expect(page).to have_text(other_project.name) + end + + it "shows action menu for each template" do + within_row("Template 1") do + expect(page).to have_css('[data-test-selector="more-button"]') + end + end + end + + context "with no templates" do + before do + Meeting.onetime_templates.destroy_all + visit templates_meetings_path + end + + it "shows blank slate" do + expect(page).to have_css(".blankslate", text: "There are no templates to display") + end + end + + context "with project-scoped index" do + shared_let(:project_template) { create(:onetime_template, project:, title: "Project template") } + shared_let(:other_template) { create(:onetime_template, project: other_project, title: "Other template") } + + before { visit templates_project_meetings_path(project) } + + it "shows only project templates" do + expect(page).to have_text("Project template") + expect(page).to have_no_text("Other template") + end + end + end + + describe "creating onetime templates" do + include Components::Autocompleter::NgSelectAutocompleteHelpers + + context "when creating from global templates page" do + before { visit templates_meetings_path } + + it "can create a template" do + find_by_id("add-template-button").click + + expect(page).to have_dialog("New template") + + within_dialog "New template" do + select_autocomplete find('[data-test-selector="project_id"]'), + query: project.name, + results_selector: "body" + + click_button "Create template" + end + + wait_for_network_idle + + template = Meeting.last + + expect(template.template).to be true + expect(template.recurring_meeting_id).to be_nil + expect(template.project).to eq(project) + expect(template.title).to eq(I18n.t(:label_meeting_template_new)) + + expect(page).to have_current_path(project_meeting_path(project, template)) + end + end + + context "when creating from project templates page" do + before { visit templates_project_meetings_path(project) } + + it "can create a template with a preselected project" do + find_by_id("add-template-button").click + + wait_for_network_idle + + template = Meeting.last + + expect(template.template).to be true + expect(template.recurring_meeting_id).to be_nil + expect(template.project).to eq(project) + expect(template.title).to eq(I18n.t(:label_meeting_template_new)) + + expect(page).to have_current_path(project_meeting_path(project, template)) + end + end + end + + describe "editing onetime templates" do + let!(:template) { create(:onetime_template, project:, title: "Original title") } + + before do + visit templates_meetings_path + end + + it "can navigate to edit view via more menu" do + within_row("Original title") do + find('[data-test-selector="more-button"]').click + end + + click_link_or_button "Edit template" + + # Should navigate to the meeting show page (templates don't have a separate edit path) + expect(page).to have_current_path(project_meeting_path(project, template)) + end + end + + describe "deleting onetime templates" do + let!(:template_to_delete) { create(:onetime_template, project:, title: "Template to delete") } + + before do + visit templates_meetings_path + end + + it "can delete template via more menu" do + within_row("Template to delete") do + find('[data-test-selector="more-button"]').click + end + + click_link_or_button "Delete template" + + expect(page).to have_dialog("Delete template") + + within_dialog "Delete template" do + click_button "Delete" + end + + wait_for_network_idle + + expect(page).to have_no_text("Template to delete") + expect(Meeting.exists?(template_to_delete.id)).to be false + end + end + + describe "permissions" do + shared_let(:permissions_template) { create(:onetime_template, project:, title: "Permission test template") } + + context "as user with view_meetings only" do + let(:user_view_only) do + create(:user, member_with_permissions: { project => [:view_meetings] }) + end + + before do + logout + login_as(user_view_only) + visit templates_meetings_path + end + + it "can view templates but cannot see create button or action menu" do + expect(page).to have_text("Permission test template") + + expect(page).to have_no_css("#add-template-button") + + within_row("Permission test template") do + expect(page).to have_no_css('[data-test-selector="more-button"]') + end + end + end + + context "as user with edit_meetings permission" do + let(:user_with_edit) do + create(:user, member_with_permissions: { project => %i[view_meetings edit_meetings] }) + end + + before do + logout + login_as(user_with_edit) + visit templates_meetings_path + end + + it "can see edit action in menu but not delete or create button" do + expect(page).to have_no_css("#add-template-button") + + within_row("Permission test template") do + find('[data-test-selector="more-button"]').click + end + + expect(page).to have_link("Edit template") + expect(page).to have_no_link("Delete template") + end + end + + context "as user with delete_meetings permission" do + let(:user_with_delete) do + create(:user, member_with_permissions: { project => %i[view_meetings delete_meetings] }) + end + + before do + logout + login_as(user_with_delete) + visit templates_meetings_path + end + + it "can see delete action in menu but not edit or create button" do + expect(page).to have_no_css("#add-template-button") + + within_row("Permission test template") do + find('[data-test-selector="more-button"]').click + end + + expect(page).to have_link("Delete template") + expect(page).to have_no_link("Edit template") + end + end + + context "as user with both edit and delete permissions" do + let(:user_with_both) do + create(:user, member_with_permissions: { project => %i[view_meetings edit_meetings delete_meetings] }) + end + + before do + logout + login_as(user_with_both) + visit templates_meetings_path + end + + it "can see both edit and delete actions in menu" do + within_row("Permission test template") do + find('[data-test-selector="more-button"]').click + end + + expect(page).to have_link("Edit template") + expect(page).to have_link("Delete template") + end + end + end + + def within_row(title, &) + template_link = page.find("a", text: title) + + row = template_link.ancestor('[role="row"]') + + within(row, &) + end +end diff --git a/modules/meeting/spec/requests/meeting_templates_spec.rb b/modules/meeting/spec/requests/meeting_templates_spec.rb new file mode 100644 index 00000000000..4778a2925b4 --- /dev/null +++ b/modules/meeting/spec/requests/meeting_templates_spec.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. +#++ + +require "spec_helper" + +RSpec.describe "Meeting templates requests", + :skip_csrf, + type: :rails_request do + shared_let(:project) { create(:project, enabled_module_names: %i[meetings]) } + shared_let(:other_project) { create(:project, enabled_module_names: %i[meetings]) } + shared_let(:user_with_permissions) do + create(:user, + member_with_permissions: { + project => %i[view_meetings create_meetings edit_meetings], + other_project => %i[view_meetings create_meetings edit_meetings] + }) + end + shared_let(:user_without_permissions) { create(:user, member_with_permissions: { project => [] }) } + + shared_let(:onetime_template1) { create(:onetime_template, project:, title: "Template 1") } + shared_let(:onetime_template2) { create(:onetime_template, project: other_project, title: "Template 2") } + shared_let(:recurring_meeting) { create(:recurring_meeting, project:) } + shared_let(:series_template) { recurring_meeting.template } + shared_let(:regular_meeting) { create(:meeting, project:, template: false, title: "Regular meeting") } + + describe "GET /meetings/templates" do + context "without project" do + before { login_as user_with_permissions } + + it "lists all onetime templates" do + get templates_meetings_path + + expect(response).to have_http_status(:ok) + expect(response.body).to include("Template 1") + expect(response.body).to include("Template 2") + + expect(response.body).not_to include(series_template.title) + expect(response.body).not_to include("Regular meeting") + end + end + + context "with project" do + before { login_as user_with_permissions } + + it "lists only project's onetime templates" do + get templates_project_meetings_path(project) + + expect(response).to have_http_status(:ok) + expect(response.body).to include("Template 1") + expect(response.body).not_to include("Template 2") + end + end + + context "without view_meetings permission" do + before { login_as user_without_permissions } + + it "returns 403" do + get templates_project_meetings_path(project) + + expect(response).to have_http_status(:forbidden) + end + end + end + + describe "POST /meetings/templates" do + context "with valid params and project context" do + before { login_as user_with_permissions } + + it "creates onetime template" do + expect do + post create_template_project_meetings_path(project) + end.to change(Meeting, :count).by(1) + + template = Meeting.last + expect(template.template).to be true + expect(template.recurring_meeting_id).to be_nil + expect(template.project).to eq(project) + + expect(response).to redirect_to(project_meeting_path(project, template, state: :edit)) + end + end + + context "with project parameter in the global context" do + before { login_as user_with_permissions } + + it "creates template in specified project" do + expect do + post create_template_meetings_path, params: { meeting: { project_id: project.id } } + end.to change(Meeting, :count).by(1) + + template = Meeting.last + expect(template.template).to be true + expect(template.recurring_meeting_id).to be_nil + expect(template.project).to eq(project) + + expect(response).to redirect_to(project_meeting_path(template.project, template, state: :edit)) + end + end + + context "with invalid project parameter in the global context" do + before { login_as user_with_permissions } + + it "returns 400" do + expect do + post create_template_meetings_path, + params: { meeting: { project_id: nil } }, + as: :turbo_stream + end.not_to change(Meeting, :count) + + expect(response).to have_http_status(:bad_request) + end + end + + context "without create_meetings permission" do + before { login_as user_without_permissions } + + it "returns 403" do + expect do + post create_template_project_meetings_path(project) + end.not_to change(Meeting, :count) + + expect(response).to have_http_status(:forbidden) + end + end + end +end diff --git a/modules/meeting/spec/requests/meetings_spec.rb b/modules/meeting/spec/requests/meetings_spec.rb index 057299d8234..86c060c4946 100644 --- a/modules/meeting/spec/requests/meetings_spec.rb +++ b/modules/meeting/spec/requests/meetings_spec.rb @@ -136,4 +136,69 @@ RSpec.describe "Meeting requests", end end end + + describe "delete" do + shared_let(:user_with_delete) do + create(:user, member_with_permissions: { project => %i[view_meetings delete_meetings] }) + end + + before { login_as user_with_delete } + + describe "template deletion restrictions" do + context "when deleting series template" do + let(:recurring_meeting) { create(:recurring_meeting, project:) } + let(:series_template) { recurring_meeting.template } + + it "renders a 400" do + delete project_meeting_path(project, series_template) + + expect(response).to have_http_status(:bad_request) + end + + it "does not delete the template" do + series_template_id = series_template.id + + delete project_meeting_path(project, series_template) + + expect(Meeting.exists?(series_template_id)).to be true + end + end + + context "when deleting onetime template" do + let(:onetime_template) { create(:onetime_template, project:) } + + it "returns successful redirect" do + delete project_meeting_path(project, onetime_template) + + expect(response).to have_http_status(:see_other) + end + + it "deletes the template" do + onetime_template_id = onetime_template.id + + delete project_meeting_path(project, onetime_template) + + expect(Meeting.exists?(onetime_template_id)).to be false + end + end + + context "when deleting regular onetime meeting" do + let(:regular_meeting) { create(:meeting, project:, template: false) } + + it "returns successful redirect" do + delete project_meeting_path(project, regular_meeting) + + expect(response).to have_http_status(:see_other) + end + + it "deletes the meeting" do + regular_meeting_id = regular_meeting.id + + delete project_meeting_path(project, regular_meeting) + + expect(Meeting.exists?(regular_meeting_id)).to be false + end + end + end + end end diff --git a/modules/my_page/spec/features/my/my_page_spec.rb b/modules/my_page/spec/features/my/my_page_spec.rb index c83fac736e9..e25ef0e3c4d 100644 --- a/modules/my_page/spec/features/my/my_page_spec.rb +++ b/modules/my_page/spec/features/my/my_page_spec.rb @@ -104,7 +104,8 @@ RSpec.describe "My page", global_html_title.expect_first_segment "My page" # Waits for the default view to be created - my_page.expect_toast(message: "Successful update") + # Waits for the default view to be created + my_page.expect_and_dismiss_toaster message: I18n.t("js.notice_successful_update") assigned_area.expect_to_exist created_area.expect_to_exist @@ -123,7 +124,7 @@ RSpec.describe "My page", sleep(0.5) reload_grid! - calendar_area.expect_to_span(1, 1, 2, 2) + calendar_area.expect_to_span(2, 1, 3, 2) # resizing will move the created area down calendar_area.resize_to(1, 2) @@ -133,7 +134,7 @@ RSpec.describe "My page", # resizing again will not influence the created area. It will stay down calendar_area.resize_to(1, 1) - calendar_area.expect_to_span(1, 1, 2, 2) + calendar_area.expect_to_span(3, 2, 4, 3) # add widget right next to the calendar widget my_page.add_widget(1, 2, :within, "News") @@ -141,17 +142,17 @@ RSpec.describe "My page", sleep(0.5) reload_grid! - news_area.expect_to_span(1, 2, 2, 3) + news_area.expect_to_span(3, 2, 4, 3) calendar_area.resize_to(2, 1) sleep(0.3) # Resizing leads to the calendar area now spanning a larger area - calendar_area.expect_to_span(1, 1, 3, 2) + calendar_area.expect_to_span(2, 1, 4, 2) # Because of the added row, and the resizing the other widgets (assigned and created) have moved down - assigned_area.expect_to_span(3, 1, 4, 2) - created_area.expect_to_span(2, 2, 3, 3) + assigned_area.expect_to_span(1, 1, 2, 2) + created_area.expect_to_span(1, 2, 2, 3) my_page.add_widget(1, 3, :column, "Work packages watched by me") @@ -170,11 +171,11 @@ RSpec.describe "My page", reload_grid! - calendar_area.expect_to_span(1, 1, 3, 2) - watched_area.expect_to_span(2, 3, 3, 4) - assigned_area.expect_to_span(3, 1, 4, 2) + calendar_area.expect_to_span(2, 1, 4, 2) + watched_area.expect_to_span(3, 2, 4, 4) + assigned_area.expect_to_span(1, 1, 2, 2) created_area.expect_to_span(1, 3, 2, 4) - news_area.expect_to_span(1, 2, 2, 3) + news_area.expect_to_span(2, 3, 3, 4) # dragging again makes room for the dragged widget which means # that widgets that have been there are moved down. Additionally, @@ -189,10 +190,10 @@ RSpec.describe "My page", visit home_path my_page.visit! - calendar_area.expect_to_span(1, 1, 3, 2) - news_area.expect_to_span(1, 2, 2, 3) - created_area.expect_to_span(2, 2, 3, 3) - assigned_area.expect_to_span(3, 1, 4, 2) - watched_area.expect_to_span(3, 2, 4, 3) + calendar_area.expect_to_span(2, 1, 4, 2) + news_area.expect_to_span(2, 3, 3, 4) + created_area.expect_to_span(1, 3, 2, 4) + assigned_area.expect_to_span(1, 1, 2, 2) + watched_area.expect_to_span(3, 2, 4, 4) end end diff --git a/modules/my_page/spec/features/my/work_package_table_spec.rb b/modules/my_page/spec/features/my/work_package_table_spec.rb index a8f284a8020..f8617e51e26 100644 --- a/modules/my_page/spec/features/my/work_package_table_spec.rb +++ b/modules/my_page/spec/features/my/work_package_table_spec.rb @@ -72,6 +72,7 @@ RSpec.describe "Arbitrary WorkPackage query table widget on my page", login_as user my_page.visit! + wait_for_network_idle end context "with the permission to save queries" do @@ -92,7 +93,7 @@ RSpec.describe "Arbitrary WorkPackage query table widget on my page", my_page.expect_and_dismiss_toaster message: I18n.t("js.notice_successful_update") filter_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(3)") - filter_area.expect_to_span(1, 2, 2, 3) + filter_area.expect_to_span(1, 3, 2, 4) # At the beginning, the default query is displayed expect(filter_area.area) @@ -105,7 +106,7 @@ RSpec.describe "Arbitrary WorkPackage query table widget on my page", filter_area.configure_wp_table modal.switch_to("Filters") - filters.expect_filter_count(2) + filters.expect_filter_count(3) filters.add_filter_by("Type", "is (OR)", type.name) modal.save @@ -141,6 +142,7 @@ RSpec.describe "Arbitrary WorkPackage query table widget on my page", visit root_path my_page.visit! + wait_for_network_idle filter_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(3)") expect(filter_area.area) diff --git a/modules/overviews/app/components/overviews/page_header_component.html.erb b/modules/overviews/app/components/overviews/page_header_component.html.erb index 720d296cb21..bf35d49bf1e 100644 --- a/modules/overviews/app/components/overviews/page_header_component.html.erb +++ b/modules/overviews/app/components/overviews/page_header_component.html.erb @@ -51,26 +51,24 @@ end end - if OpenProject::FeatureDecisions.new_project_overview_active? - header.with_tab_nav(label: nil, test_selector: "overview-tabs") do |tab_nav| - tab_nav.with_tab( - test_selector: "project-overview-tab", - selected: current_page?(project_overview_path), - href: helpers.project_overview_path - ) do |t| - t.with_icon(icon: :"op-view-split") - t.with_text { I18n.t("overviews.label_overview") } - end + header.with_tab_nav(label: nil, test_selector: "overview-tabs") do |tab_nav| + tab_nav.with_tab( + test_selector: "project-overview-tab", + selected: current_page?(project_overview_path), + href: helpers.project_overview_path + ) do |t| + t.with_icon(icon: :"op-view-split") + t.with_text { I18n.t("overviews.label_overview") } + end - if current_user.allowed_in_project?(:view_project, project) - tab_nav.with_tab( - test_selector: "project-dashboard-tab", - selected: current_page?(dashboard_project_overview_path), - href: helpers.dashboard_project_overview_path - ) do |t| - t.with_icon(icon: :"op-view-list") - t.with_text { I18n.t("overviews.label_dashboard") } - end + if current_user.allowed_in_project?(:view_project, project) + tab_nav.with_tab( + test_selector: "project-dashboard-tab", + selected: current_page?(dashboard_project_overview_path), + href: helpers.dashboard_project_overview_path + ) do |t| + t.with_icon(icon: :"op-view-list") + t.with_text { I18n.t("overviews.label_dashboard") } end end end diff --git a/modules/overviews/app/components/overviews/page_header_component.rb b/modules/overviews/app/components/overviews/page_header_component.rb index 698412d7dd6..6f174190784 100644 --- a/modules/overviews/app/components/overviews/page_header_component.rb +++ b/modules/overviews/app/components/overviews/page_header_component.rb @@ -42,27 +42,23 @@ module Overviews private def breadcrumb_items - return nil if project.ancestors.blank? - items = - project.ancestors.map do |ancestor| + project.ancestors.visible.map do |ancestor| { href: project_path(ancestor), text: ancestor.name, skip_for_mobile: true } end - items << page_title + return nil if items.empty? + + items << page_title items end def page_title - if OpenProject::FeatureDecisions.new_project_overview_active? - project.name - else - I18n.t("overviews.label_overview") - end + project.name end def favorited? diff --git a/modules/overviews/app/components/overviews/project_custom_fields/side_panel_component.rb b/modules/overviews/app/components/overviews/project_custom_fields/side_panel_component.rb index fe0d4bc9c5f..7311f243146 100644 --- a/modules/overviews/app/components/overviews/project_custom_fields/side_panel_component.rb +++ b/modules/overviews/app/components/overviews/project_custom_fields/side_panel_component.rb @@ -49,16 +49,10 @@ module Overviews private def available_project_custom_fields_grouped_by_section - if OpenProject::FeatureDecisions.new_project_overview_active? - @available_project_custom_fields_grouped_by_section ||= - @project.available_custom_fields - .group_by(&:project_custom_field_section) - .select { |section, _| section.shown_in_overview_sidebar? } - else - @available_project_custom_fields_grouped_by_section ||= - @project.available_custom_fields - .group_by(&:project_custom_field_section) - end + @available_project_custom_fields_grouped_by_section ||= + @project.available_custom_fields + .group_by(&:project_custom_field_section) + .select { |section, _| section.shown_in_overview_sidebar? } end end end diff --git a/modules/overviews/app/components/overviews/show_component.html.erb b/modules/overviews/app/components/overviews/show_component.html.erb index 14a1a5bf04f..3b31e113bc9 100644 --- a/modules/overviews/app/components/overviews/show_component.html.erb +++ b/modules/overviews/app/components/overviews/show_component.html.erb @@ -58,10 +58,6 @@ See COPYRIGHT and LICENSE files for more details. end end - if OpenProject::FeatureDecisions.new_project_overview_active? - render Overviews::OverviewGridComponent.new(project:) - else - render Overviews::DashboardComponent.new(project:, current_user:) - end + render Overviews::OverviewGridComponent.new(project:) end %> diff --git a/modules/overviews/config/locales/crowdin/js-he.yml b/modules/overviews/config/locales/crowdin/js-he.yml index f3d2daf05d6..4044913eba6 100644 --- a/modules/overviews/config/locales/crowdin/js-he.yml +++ b/modules/overviews/config/locales/crowdin/js-he.yml @@ -1,4 +1,4 @@ he: js: overviews: - label: 'Overview' + label: 'סקירה' diff --git a/modules/overviews/config/routes.rb b/modules/overviews/config/routes.rb index 38940b95467..fb2782830f1 100644 --- a/modules/overviews/config/routes.rb +++ b/modules/overviews/config/routes.rb @@ -5,9 +5,7 @@ Rails.application.routes.draw do scope "projects/:project_id", as: "project" do scope module: "overviews" do resource :overview, path: "/", only: [:show] do - constraints(Constraints::FeatureDecision.new(:new_project_overview)) do - get :dashboard, on: :member - end + get :dashboard, on: :member end controller :overviews do diff --git a/modules/overviews/lib/overviews/engine.rb b/modules/overviews/lib/overviews/engine.rb index 2d315ae73f8..8161cd70af2 100644 --- a/modules/overviews/lib/overviews/engine.rb +++ b/modules/overviews/lib/overviews/engine.rb @@ -36,13 +36,7 @@ module Overviews ::Redmine::MenuManager.map(:project_menu) do |menu| menu.push(:overview, { controller: "/overviews/overviews", action: "show" }, - caption: ->(project) { - if OpenProject::FeatureDecisions.new_project_overview_active? - I18n.t("overviews.label_home", workspace_type: project.workspace_label) - else - I18n.t("overviews.label_overview") - end - }, + caption: ->(project) { I18n.t("overviews.label_home", workspace_type: project.workspace_label) }, first: true, icon: "info") end diff --git a/modules/overviews/spec/components/overviews/page_header_component_spec.rb b/modules/overviews/spec/components/overviews/page_header_component_spec.rb index 5a4fbb6e56b..b850795ff28 100644 --- a/modules/overviews/spec/components/overviews/page_header_component_spec.rb +++ b/modules/overviews/spec/components/overviews/page_header_component_spec.rb @@ -54,18 +54,13 @@ RSpec.describe Overviews::PageHeaderComponent, type: :component do expect(rendered_component).to have_css ".PageHeader-contextBar" end - it "renders current page without breadcrumbs", with_flag: { new_project_overview: true } do + it "renders current page without breadcrumbs" do expect(rendered_component).to have_text project.name expect(rendered_component).to have_css ".PageHeader--noBreadcrumb" end - - it "renders current page without breadcrumbs", with_flag: { new_project_overview: false } do - expect(rendered_component).to have_text "Overview" - expect(rendered_component).to have_css ".PageHeader--noBreadcrumb" - end end - context "with the feature flag enabled", with_flag: { new_project_overview: true } do + context "with the feature flag enabled" do it "renders a Page Header (with tab nav)" do expect(rendered_component).to have_element "page-header", class: "PageHeader--withTabNav" end @@ -74,7 +69,6 @@ RSpec.describe Overviews::PageHeaderComponent, type: :component do it "renders title" do expect(rendered_component).to have_heading project.name, class: "PageHeader-title" end - end context "with Portfolio" do @@ -83,7 +77,6 @@ RSpec.describe Overviews::PageHeaderComponent, type: :component do it "renders title" do expect(rendered_component).to have_heading project.name, class: "PageHeader-title" end - end context "with Program" do @@ -95,16 +88,6 @@ RSpec.describe Overviews::PageHeaderComponent, type: :component do end end - context "with the feature flag disabled", with_flag: { new_project_overview: false } do - it "renders a Page Header" do - expect(rendered_component).to have_element "page-header" - end - - it "renders title" do - expect(rendered_component).to have_heading "Overview", class: "PageHeader-title" - end - end - describe "actions" do it "renders actions" do expect(rendered_component).to have_css ".PageHeader-actions" @@ -189,7 +172,7 @@ RSpec.describe Overviews::PageHeaderComponent, type: :component do end end - describe "tab bar", with_flag: { new_project_overview: true } do + describe "tab bar" do context "when user has permission to view project" do let(:user) { build_stubbed(:admin) } @@ -231,7 +214,9 @@ RSpec.describe Overviews::PageHeaderComponent, type: :component do describe "breadcrumbs" do context "when the project has no parent" do before do - allow(project).to receive(:ancestors).and_return([]) + allow(project) + .to receive_message_chain(:ancestors, :visible) # rubocop:disable RSpec/MessageChain + .and_return([]) end it "does not render breadcrumbs" do @@ -244,7 +229,9 @@ RSpec.describe Overviews::PageHeaderComponent, type: :component do let(:parent) { build_stubbed(:project) } before do - allow(project).to receive(:ancestors).and_return([grandparent, parent]) + allow(project) + .to receive_message_chain(:ancestors, :visible) # rubocop:disable RSpec/MessageChain + .and_return([grandparent, parent]) end it "renders the full hierarchy breadcrumb path and ends with the current project name", :aggregate_failures do diff --git a/modules/overviews/spec/components/overviews/show_component_spec.rb b/modules/overviews/spec/components/overviews/show_component_spec.rb index e33211b977c..31192d31aa2 100644 --- a/modules/overviews/spec/components/overviews/show_component_spec.rb +++ b/modules/overviews/spec/components/overviews/show_component_spec.rb @@ -74,24 +74,12 @@ RSpec.describe Overviews::ShowComponent, type: :component do end end - context "with the feature flag enabled", with_flag: { new_project_overview: true } do - it "renders overview grid" do - expect(rendered_component).to have_css ".widget-boxes" - end - - it "does not render widgets" do - expect(rendered_component).to have_no_element "opce-dashboard" - end + it "renders overview grid" do + expect(rendered_component).to have_css ".widget-boxes" end - context "with the feature flag disabled", with_flag: { new_project_overview: false } do - it "does not render overview grid" do - expect(rendered_component).to have_no_css ".widget-boxes" - end - - it "renders widgets" do - expect(rendered_component).to have_element "opce-dashboard" - end + it "does not render widgets" do + expect(rendered_component).to have_no_element "opce-dashboard" end context "when project has neither project attributes or life cycle" do diff --git a/modules/overviews/spec/features/low_permissions_page_creation_spec.rb b/modules/overviews/spec/features/low_permissions_page_creation_spec.rb index cb96e1be94b..51b98cd3445 100644 --- a/modules/overviews/spec/features/low_permissions_page_creation_spec.rb +++ b/modules/overviews/spec/features/low_permissions_page_creation_spec.rb @@ -30,9 +30,7 @@ require "spec_helper" require_relative "../support/pages/dashboard" -RSpec.describe "Dashboard page on the fly creation if user lacks :manage_dashboards permission", - :js, - with_flag: { new_project_overview: true } do +RSpec.describe "Dashboard page on the fly creation if user lacks :manage_dashboards permission", :js do let!(:type) { create(:type) } let!(:project) { create(:project, types: [type]) } let!(:open_status) { create(:default_status) } diff --git a/modules/overviews/spec/features/managing_dashboard_page_spec.rb b/modules/overviews/spec/features/managing_dashboard_page_spec.rb index b596f50a1ab..4ddd1006730 100644 --- a/modules/overviews/spec/features/managing_dashboard_page_spec.rb +++ b/modules/overviews/spec/features/managing_dashboard_page_spec.rb @@ -30,7 +30,7 @@ require "spec_helper" require_relative "../support/pages/dashboard" -RSpec.describe "Dashboard page managing", :js, with_flag: { new_project_overview: true } do +RSpec.describe "Dashboard page managing", :js do let!(:type) { create(:type) } let!(:project) { create(:project, types: [type], description: "My **custom** description") } let!(:open_status) { create(:default_status) } @@ -97,8 +97,8 @@ RSpec.describe "Dashboard page managing", :js, with_flag: { new_project_overview members_area.expect_to_exist description_area.expect_to_span(1, 1, 3, 2) status_area.expect_to_span(1, 2, 2, 3) - overview_area.expect_to_span(3, 1, 4, 3) - members_area.expect_to_span(2, 2, 3, 3) + overview_area.expect_to_span(2, 2, 3, 3) + members_area.expect_to_span(3, 1, 4, 3) # The widgets load their respective contents within description_area.area do @@ -120,28 +120,28 @@ RSpec.describe "Dashboard page managing", :js, with_flag: { new_project_overview dashboard_page.expect_and_dismiss_toaster message: I18n.t("js.notice_successful_update") table_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(5)") - table_area.expect_to_span(1, 1, 2, 2) + table_area.expect_to_span(4, 1, 5, 3) # A useless resizing shows no message and does not alter the size table_area.resize_to(1, 1) dashboard_page.expect_no_toaster message: I18n.t("js.notice_successful_update") - table_area.expect_to_span(1, 1, 2, 2) + table_area.expect_to_span(4, 1, 5, 2) table_area.resize_to(1, 2) dashboard_page.expect_and_dismiss_toaster message: I18n.t("js.notice_successful_update") # Resizing leads to the table area now spanning a larger area - table_area.expect_to_span(1, 1, 2, 3) + table_area.expect_to_span(4, 1, 5, 3) + - within table_area.area do expect(page) .to have_content(created_work_package.subject) expect(page) .to have_content(assigned_work_package.subject) - end + sleep(0.1) @@ -152,11 +152,11 @@ RSpec.describe "Dashboard page managing", :js, with_flag: { new_project_overview ## Because of the added column and the resizing the other widgets have moved down # For unknown, undesired reasons, the project description no longer spans two rows. # This happens when resizing the table area. - description_area.expect_to_span(2, 1, 3, 2) - status_area.expect_to_span(2, 2, 3, 3) - overview_area.expect_to_span(4, 1, 5, 3) + description_area.expect_to_span(1, 1, 2, 2) + status_area.expect_to_span(1, 2, 3, 3) + overview_area.expect_to_span(2, 1, 4, 2) members_area.expect_to_span(3, 2, 4, 3) - table_area.expect_to_span(1, 1, 2, 3) + table_area.expect_to_span(4, 1, 5, 3) end it "can add a new widget via a primary button" do @@ -172,8 +172,8 @@ RSpec.describe "Dashboard page managing", :js, with_flag: { new_project_overview description_area.expect_to_span(1, 1, 3, 2) status_area.expect_to_span(1, 2, 2, 3) - overview_area.expect_to_span(3, 1, 4, 3) - members_area.expect_to_span(2, 2, 3, 3) + overview_area.expect_to_span(2, 2, 3, 3) + members_area.expect_to_span(3, 1, 4, 3) page.find_test_selector("overview--add-widgets-button").click @@ -186,11 +186,11 @@ RSpec.describe "Dashboard page managing", :js, with_flag: { new_project_overview end second_members_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(5)") - second_members_area.expect_to_span(1, 1, 2, 2) + second_members_area.expect_to_span(4, 1, 5, 3) - description_area.expect_to_span(2, 1, 4, 2) + description_area.expect_to_span(1, 1, 2, 2) status_area.expect_to_span(1, 2, 3, 3) - overview_area.expect_to_span(4, 1, 5, 3) + overview_area.expect_to_span(2, 1, 4, 2) members_area.expect_to_span(3, 2, 4, 3) end end diff --git a/modules/overviews/spec/features/managing_overview_page_spec.rb b/modules/overviews/spec/features/managing_overview_page_spec.rb deleted file mode 100644 index 70a29abfb95..00000000000 --- a/modules/overviews/spec/features/managing_overview_page_spec.rb +++ /dev/null @@ -1,216 +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. -#++ - -require "spec_helper" - -require_relative "../support/pages/overview" - -RSpec.describe "Overview page managing", :js, with_flag: { new_project_overview: false } do - let!(:type) { create(:type) } - let!(:project) { create(:project, types: [type], description: "My **custom** description") } - let!(:open_status) { create(:default_status) } - let!(:created_work_package) do - create(:work_package, - project:, - type:, - author: user) - end - let!(:assigned_work_package) do - create(:work_package, - project:, - type:, - assigned_to: user) - end - - let(:permissions) do - %i[manage_dashboards - view_members - view_work_packages - add_work_packages - save_queries - manage_public_queries] - end - - let(:user) do - create(:user, - member_with_permissions: { project => permissions }) - end - - let(:user_without_permission) do - create(:user, - member_with_permissions: { - project => %i[ - view_members - view_work_packages - add_work_packages - save_queries - manage_public_queries - ] - }) - end - - let(:overview_page) do - Pages::Overview.new(project) - end - - context "as a user with permission" do - before do - login_as user - - overview_page.visit! - end - - it "renders the default view, allows altering and saving" do - description_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(1)") - status_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(2)") - overview_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(3)") - members_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(4)") - - description_area.expect_to_exist - status_area.expect_to_exist - overview_area.expect_to_exist - members_area.expect_to_exist - description_area.expect_to_span(1, 1, 3, 2) - status_area.expect_to_span(1, 2, 2, 3) - overview_area.expect_to_span(3, 1, 4, 3) - members_area.expect_to_span(2, 2, 3, 3) - - # The widgets load their respective contents - within description_area.area do - expect(page) - .to have_content("My custom description") - end - - # within top-left area, add an additional widget - overview_page.add_widget(1, 1, :row, "Work packages table") - # Actually there are two success messages displayed currently. One for the grid getting updated and one - # for the query assigned to the new widget being created. A user will not notice it but the automated - # browser can get confused. Therefore we dismiss it twice. - overview_page.expect_and_dismiss_toaster message: I18n.t("js.notice_successful_update") - - # Fixing flaky spec: for some reason, the second request to load the table is not executed until - # some activity happens on the page. Sending an enter key to trigger the second request. - page.find("body").send_keys(:enter) - - overview_page.expect_and_dismiss_toaster message: I18n.t("js.notice_successful_update") - - table_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(5)") - table_area.expect_to_span(1, 1, 2, 2) - - # A useless resizing shows no message and does not alter the size - table_area.resize_to(1, 1) - - overview_page.expect_no_toaster message: I18n.t("js.notice_successful_update") - - table_area.expect_to_span(1, 1, 2, 2) - - table_area.resize_to(1, 2) - - overview_page.expect_and_dismiss_toaster message: I18n.t("js.notice_successful_update") - - # Resizing leads to the table area now spanning a larger area - table_area.expect_to_span(1, 1, 2, 3) - - within table_area.area do - expect(page) - .to have_content(created_work_package.subject) - expect(page) - .to have_content(assigned_work_package.subject) - end - - sleep(0.1) - - # Reloading kept the user's values - visit home_path - overview_page.visit! - - ## Because of the added column and the resizing the other widgets have moved down - # For unknown, undesired reasons, the project description no longer spans two rows. - # This happens when resizing the table area. - description_area.expect_to_span(2, 1, 3, 2) - status_area.expect_to_span(2, 2, 3, 3) - overview_area.expect_to_span(4, 1, 5, 3) - members_area.expect_to_span(3, 2, 4, 3) - table_area.expect_to_span(1, 1, 2, 3) - end - - it "can add a new widget via a primary button" do - description_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(1)") - status_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(2)") - overview_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(3)") - members_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(4)") - - description_area.expect_to_exist - status_area.expect_to_exist - overview_area.expect_to_exist - members_area.expect_to_exist - - description_area.expect_to_span(1, 1, 3, 2) - status_area.expect_to_span(1, 2, 2, 3) - overview_area.expect_to_span(3, 1, 4, 3) - members_area.expect_to_span(2, 2, 3, 3) - - page.find_test_selector("overview--add-widgets-button").click - - within(".spot-modal") do - expect(page).to have_content(I18n.t("js.grid.add_widget")) - - SeleniumHubWaiter.wait unless using_cuprite? - - page.find('[data-test-selector="op-grid--addable-widget"]', text: "Members").click - end - - second_members_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(5)") - second_members_area.expect_to_span(1, 1, 2, 2) - - description_area.expect_to_span(2, 1, 4, 2) - status_area.expect_to_span(1, 2, 3, 3) - overview_area.expect_to_span(4, 1, 5, 3) - members_area.expect_to_span(3, 2, 4, 3) - end - end - - context "as a user without permission" do - before do - login_as user_without_permission - - overview_page.visit! - end - - it "does not show the option to add widgets" do - # Neither hover effects - overview_page.expect_unable_to_add_widget(1, 1, :column, nil) - overview_page.expect_unable_to_add_widget(1, 1, :row, nil) - - # nor a create button are shown - expect(page).to have_no_test_selector("overview--add-widgets-button") - end - end -end diff --git a/modules/overviews/spec/features/navigation_spec.rb b/modules/overviews/spec/features/navigation_spec.rb index a307a0d1993..4a5680a2e38 100644 --- a/modules/overviews/spec/features/navigation_spec.rb +++ b/modules/overviews/spec/features/navigation_spec.rb @@ -42,14 +42,61 @@ RSpec.describe "Navigate to overview", :js do login_as user end - context "with the feature flag enabled", with_flag: { new_project_overview: true } do - it "can visit the overview page" do - visit project_path(project) + it "can visit the overview page" do + visit project_path(project) - within "#menu-sidebar" do - click_link "Project home" + within "#menu-sidebar" do + click_link "Project home" + end + + within "#content" do + expect(page).to have_heading project.name + end + end + + context "as user with permissions" do + let(:project) { create(:project, enabled_module_names: %i[work_package_tracking]) } + let(:user) { create(:admin) } + let(:query) do + create(:query_with_view_work_packages_table, + project:, + user:, + name: "My important Query") + end + + before do + query + login_as user + end + + it "can navigate to other modules (regression #55024)" do + visit project_overview_path(project.id) + + # Expect page to be loaded + within "#content" do + expect(page).to have_heading project.name end + # Navigate to the WP module + page.find_test_selector("main-menu-toggler--work_packages").click + + # Click on a saved query + query_menu.click_item "My important Query" + + loading_indicator_saveguard + + within "#content" do + # Expect the query content to be shown + expect(page).to have_field("editable-toolbar-title", with: query.name) + + # Expect no page header of the Overview to be shown any more + expect(page).to have_no_heading project.name + end + + # Navigate back to the Overview page + page.execute_script("window.history.back()") + + # Expect page to be loaded within "#content" do expect(page).to have_heading project.name end @@ -104,67 +151,4 @@ RSpec.describe "Navigate to overview", :js do end end end - - context "with the feature flag disabled", with_flag: { new_project_overview: false } do - it "can visit the overview page" do - visit project_path(project) - - within "#menu-sidebar" do - click_link "Overview" - end - - within "#content" do - expect(page).to have_heading "Overview" - end - end - - context "as user with permissions" do - let(:project) { create(:project, enabled_module_names: %i[work_package_tracking]) } - let(:user) { create(:admin) } - let(:query) do - create(:query_with_view_work_packages_table, - project:, - user:, - name: "My important Query") - end - - before do - query - login_as user - end - - it "can navigate to other modules (regression #55024)" do - visit project_overview_path(project.id) - - # Expect page to be loaded - within "#content" do - expect(page).to have_heading "Overview" - end - - # Navigate to the WP module - page.find_test_selector("main-menu-toggler--work_packages").click - - # Click on a saved query - query_menu.click_item "My important Query" - - loading_indicator_saveguard - - within "#content" do - # Expect the query content to be shown - expect(page).to have_field("editable-toolbar-title", with: query.name) - - # Expect no page header of the Overview to be shown any more - expect(page).to have_no_heading "Overview" - end - - # Navigate back to the Overview page - page.execute_script("window.history.back()") - - # Expect page to be loaded - within "#content" do - expect(page).to have_heading "Overview" - end - end - end - end end diff --git a/modules/overviews/spec/features/project_description_widget_spec.rb b/modules/overviews/spec/features/project_description_widget_spec.rb index c4922aad122..becc4b766d8 100644 --- a/modules/overviews/spec/features/project_description_widget_spec.rb +++ b/modules/overviews/spec/features/project_description_widget_spec.rb @@ -32,7 +32,7 @@ require "spec_helper" require_relative "../support/pages/dashboard" -RSpec.describe "Project description widget", :js, with_flag: { new_project_overview: true } do +RSpec.describe "Project description widget", :js do include TestSelectorFinders let!(:type) { create(:type) } diff --git a/modules/overviews/spec/routing/overviews_routing_spec.rb b/modules/overviews/spec/routing/overviews_routing_spec.rb index d7a93b23ecc..0dfb10b12d7 100644 --- a/modules/overviews/spec/routing/overviews_routing_spec.rb +++ b/modules/overviews/spec/routing/overviews_routing_spec.rb @@ -39,19 +39,11 @@ RSpec.describe Overviews::OverviewsController do ) end - context "with the feature flag enabled", with_flag: { new_project_overview: true } do - it do - expect(get("/projects/my-project/dashboard")) - .to route_to( - controller: "overviews/overviews", action: "dashboard", project_id: "my-project" - ) - end - end - - context "with the feature flag disabled", with_flag: { new_project_overview: false } do - it do - expect(get("/projects/my-project/dashboard")).not_to be_routable - end + it do + expect(get("/projects/my-project/dashboard")) + .to route_to( + controller: "overviews/overviews", action: "dashboard", project_id: "my-project" + ) end it do @@ -79,19 +71,11 @@ RSpec.describe Overviews::OverviewsController do ) end - context "with the feature flag enabled", with_flag: { new_project_overview: true } do - it do - expect(get(dashboard_project_overview_path("my-project"))) - .to route_to( - controller: "overviews/overviews", action: "dashboard", project_id: "my-project" - ) - end - end - - context "with the feature flag disabled", with_flag: { new_project_overview: false } do - it do - expect(get(dashboard_project_overview_path("my-project"))).not_to be_routable - end + it do + expect(get(dashboard_project_overview_path("my-project"))) + .to route_to( + controller: "overviews/overviews", action: "dashboard", project_id: "my-project" + ) end it do diff --git a/modules/overviews/spec/support/pages/overview.rb b/modules/overviews/spec/support/pages/overview.rb index f6aafb60071..29f9ae54dfd 100644 --- a/modules/overviews/spec/support/pages/overview.rb +++ b/modules/overviews/spec/support/pages/overview.rb @@ -33,8 +33,7 @@ require "support/pages/page" require_relative "../../../../grids/spec/support/pages/grid" module Pages - # TODO: inherit from `::Pages::Page` when `new_project_overview` feature flag is removed. - class Overview < ::Pages::Grid + class Overview < ::Pages::Page attr_accessor :project def initialize(project) diff --git a/spec/components/custom_fields/details_component_spec.rb b/spec/components/custom_fields/details_component_spec.rb deleted file mode 100644 index 021cc262dfe..00000000000 --- a/spec/components/custom_fields/details_component_spec.rb +++ /dev/null @@ -1,138 +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. -# ++ -require "spec_helper" - -RSpec.describe CustomFields::DetailsComponent, type: :component do - describe ".supported?" do - context "with a bool cf" do - let(:custom_field) { build_stubbed(:boolean_wp_custom_field) } - - it "is supported" do - expect(described_class).to be_supported(custom_field) - end - end - - context "with a string cf" do - let(:custom_field) { build_stubbed(:string_wp_custom_field) } - - it "is not supported" do - expect(described_class).not_to be_supported(custom_field) - end - end - - context "with a text cf" do - let(:custom_field) { build_stubbed(:text_wp_custom_field) } - - it "is not supported" do - expect(described_class).not_to be_supported(custom_field) - end - end - - context "with a link cf" do - let(:custom_field) { build_stubbed(:link_wp_custom_field) } - - it "is not supported" do - expect(described_class).not_to be_supported(custom_field) - end - end - - context "with an int cf" do - let(:custom_field) { build_stubbed(:integer_wp_custom_field) } - - it "is not supported" do - expect(described_class).not_to be_supported(custom_field) - end - end - - context "with a version cf" do - let(:custom_field) { build_stubbed(:version_wp_custom_field) } - - it "is not supported" do - expect(described_class).not_to be_supported(custom_field) - end - end - - context "with a user cf" do - let(:custom_field) { build_stubbed(:user_wp_custom_field) } - - it "is not supported" do - expect(described_class).not_to be_supported(custom_field) - end - end - - context "with a date cf" do - let(:custom_field) { build_stubbed(:date_wp_custom_field) } - - it "is not supported" do - expect(described_class).not_to be_supported(custom_field) - end - end - - context "with a list cf" do - let(:custom_field) { build_stubbed(:list_wp_custom_field) } - - it "is not supported" do - expect(described_class).not_to be_supported(custom_field) - end - end - - context "with a float cf" do - let(:custom_field) { build_stubbed(:float_wp_custom_field) } - - it "is not supported" do - expect(described_class).not_to be_supported(custom_field) - end - end - - context "with a calculated_value cf" do - let(:custom_field) { build_stubbed(:calculated_value_project_custom_field) } - - it "is supported" do - expect(described_class).to be_supported(custom_field) - end - end - - context "with a hierarchy cf" do - let(:custom_field) { build_stubbed(:hierarchy_wp_custom_field) } - - it "is supported" do - expect(described_class).to be_supported(custom_field) - end - end - - context "with a weighted_item_list cf" do - let(:custom_field) { build_stubbed(:weighted_item_list_wp_custom_field) } - - it "is supported" do - expect(described_class).to be_supported(custom_field) - end - end - end -end diff --git a/spec/controllers/work_packages/activities_tab_controller_spec.rb b/spec/controllers/work_packages/activities_tab_controller_spec.rb index a1e874ad355..fc2809e773b 100644 --- a/spec/controllers/work_packages/activities_tab_controller_spec.rb +++ b/spec/controllers/work_packages/activities_tab_controller_spec.rb @@ -680,4 +680,85 @@ RSpec.describe WorkPackages::ActivitiesTabController do end end end + + describe "#toggle_reaction" do + let(:reaction) { :thumbs_up } + let(:journal) { comment_by_user } + + let(:internal_comment) do + create(:work_package_journal, user: commenter, notes: "Secret internal note", + journable: work_package, version: work_package.journals.last.version + 1, + internal: true) + end + + let(:user_with_internal_comments_role) do + create(:project_role, + permissions: %i[view_work_packages add_work_package_comments + view_internal_comments]) + end + let(:user_with_internal_comments_access) do + create(:user, member_with_roles: { project => user_with_internal_comments_role }) + end + + before do + put :toggle_reaction, + params: { work_package_id: work_package.id, project_id: project.id, + id: journal.id, reaction: }, + format: :turbo_stream + end + + it_behaves_like "does not grant access for anonymous users in all cases" + it_behaves_like "does not grant access for users with no access to the project" + + context "when a viewer is logged in" do + let(:user) { viewer } + + subject { response } + + it { is_expected.to be_forbidden } + end + + context "when a commenter is logged in" do + let(:user) { commenter } + + subject { response } + + it { is_expected.to be_successful } + + context "when targeting an internal comment (IDOR attempt)", + with_ee: [:internal_comments] do + let(:project) { create(:project, enabled_internal_comments: true) } + let(:journal) { internal_comment } + + it { is_expected.to be_not_found } + end + end + + context "when a user with full privileges is logged in" do + let(:user) { user_with_full_privileges } + + subject { response } + + it { is_expected.to be_successful } + + context "when targeting an internal comment without view_internal_comments", + with_ee: [:internal_comments] do + let(:project) { create(:project, enabled_internal_comments: true) } + let(:journal) { internal_comment } + + it { is_expected.to be_not_found } + end + end + + context "when a user with view_internal_comments reacts to an internal comment", + with_ee: [:internal_comments] do + let(:project) { create(:project, enabled_internal_comments: true) } + let(:user) { user_with_internal_comments_access } + let(:journal) { internal_comment } + + subject { response } + + it { is_expected.to be_successful } + end + end end diff --git a/spec/features/a11y/external_links_spec.rb b/spec/features/a11y/external_links_spec.rb index 997892d7530..bb3be11fc57 100644 --- a/spec/features/a11y/external_links_spec.rb +++ b/spec/features/a11y/external_links_spec.rb @@ -54,8 +54,7 @@ RSpec.describe "External links", :js do document.body.appendChild(link); JS - href = external_redirect_path(url: "https://example.com/") - link = page.find_link("External Example", href:, match: :first) + link = page.find_link("External Example", href: "https://example.com", match: :first) # Verify accessibility and security attributes expect(link[:target]).to eq("_blank") diff --git a/spec/features/admin/custom_fields/projects/boolean_spec.rb b/spec/features/admin/custom_fields/projects/boolean_spec.rb index 4924a648510..c288d945e64 100644 --- a/spec/features/admin/custom_fields/projects/boolean_spec.rb +++ b/spec/features/admin/custom_fields/projects/boolean_spec.rb @@ -41,7 +41,5 @@ RSpec.describe "Edit project custom fields", :js do it_behaves_like "shows checkboxes for configuration" do let(:required_supported) { false } end - it_behaves_like "editing the field" do - let(:using_primer) { true } - end + it_behaves_like "editing the field" end diff --git a/spec/features/admin/custom_fields/projects/create_in_section_spec.rb b/spec/features/admin/custom_fields/projects/create_in_section_spec.rb index 37986d35042..0662dcf6c85 100644 --- a/spec/features/admin/custom_fields/projects/create_in_section_spec.rb +++ b/spec/features/admin/custom_fields/projects/create_in_section_spec.rb @@ -108,7 +108,7 @@ RSpec.describe "Create project custom fields in sections", :js do it "prevents creating a new project custom field with an empty name" do click_on("Save") - expect(page).to have_field "custom_field_name", validation_message: /Please fill (in|out) this field./ + expect(page).to have_field("custom_field_name", with: "", validation_error: "Name can't be blank") # expect no redirect expect(page).to have_no_current_path(admin_settings_project_custom_fields_path(tab: "ProjectCustomField")) diff --git a/spec/features/admin/custom_fields/projects/shared_context.rb b/spec/features/admin/custom_fields/projects/shared_context.rb index e4033565791..d4b4d37bc8b 100644 --- a/spec/features/admin/custom_fields/projects/shared_context.rb +++ b/spec/features/admin/custom_fields/projects/shared_context.rb @@ -232,12 +232,6 @@ end RSpec.shared_examples "editing the field" do current_user { admin } - let(:using_primer) do - super() - rescue NoMethodError - false - end - before do visit edit_admin_settings_project_custom_field_path(custom_field) end @@ -272,11 +266,7 @@ RSpec.shared_examples "editing the field" do fill_in("Name", with: "") click_on("Save") - if using_primer - expect(page).to have_css(".FormControl-inlineValidation", text: "Name can't be blank") - else - expect(page).to have_field "Name", validation_message: /Please fill (in|out) this field./ - end + expect(page).to have_field("custom_field_name", with: "", validation_error: "Name can't be blank") expect(page).to have_no_text("Successful update") diff --git a/spec/features/admin/custom_fields/shared_custom_field_expectations.rb b/spec/features/admin/custom_fields/shared_custom_field_expectations.rb index 0a818213bc0..6ea6b8db63c 100644 --- a/spec/features/admin/custom_fields/shared_custom_field_expectations.rb +++ b/spec/features/admin/custom_fields/shared_custom_field_expectations.rb @@ -46,7 +46,14 @@ RSpec.shared_examples_for "list custom fields" do |type| cf_page.set_name "Operating System" expect(page).to have_text("Allow multi-select") - check("custom_field_multi_value") + check("multi_value") + + click_on "Save" + cf_page.expect_and_dismiss_flash(message: "Successful creation.") + expect(page).to have_field("multi_value", checked: true) + + click_link "Items" + wait_for_network_idle expect(page).to have_css(".custom-option-row", count: 1) within all(".custom-option-row").last do @@ -73,15 +80,11 @@ RSpec.shared_examples_for "list custom fields" do |type| within all(".custom-option-row").last do find(".custom-option-value input").set "Solaris" - click_on "Move to top" + click_link accessible_name: "Move to top" end click_on "Save" - expect(page).to have_text("Successful creation") - - expect(page).to have_field("custom_field_multi_value", checked: true) - expect(page).to have_css(".custom-option-row", count: 3) expect(page).to have_field("custom_field_custom_options_attributes_0_value", with: "Solaris") expect(page).to have_field("custom_field_custom_options_attributes_1_value", with: "Windows") @@ -271,11 +274,5 @@ RSpec.shared_examples_for "expected fields for the custom field's format", :aggr else expect(page).to have_no_label(label_allow_non_open_versions) end - - if format == "List" - expect(page).to have_fieldset(label_possible_values) - else - expect(page).to have_no_fieldset(label_possible_values) - end end end diff --git a/spec/features/admin/custom_fields/time_entries/list_spec.rb b/spec/features/admin/custom_fields/time_entries/list_spec.rb index f62fbbad607..aa5aff65ed9 100644 --- a/spec/features/admin/custom_fields/time_entries/list_spec.rb +++ b/spec/features/admin/custom_fields/time_entries/list_spec.rb @@ -50,12 +50,17 @@ RSpec.describe "List custom fields edit", :js do fill_in "custom_field_name", with: "My List CF" + click_on "Save" + + index_cf_page.expect_and_dismiss_flash(message: "Successful creation.") + + click_link "Items" + expect(page).to have_field("custom_field_custom_options_attributes_0_value") fill_in "custom_field_custom_options_attributes_0_value", with: "A" click_on "Save" - - index_cf_page.expect_and_dismiss_flash(message: "Successful creation.") + wait_for_network_idle # Expect correct values cf = CustomField.last @@ -63,12 +68,11 @@ RSpec.describe "List custom fields edit", :js do expect(cf.possible_values.map(&:value)).to eq %w(A) # Edit again - find("a", text: "My List CF").click - expect(page).to have_field("custom_field_custom_options_attributes_0_value") fill_in "custom_field_custom_options_attributes_0_value", with: "B" click_on "Save" + wait_for_network_idle index_cf_page.expect_and_dismiss_flash(message: "Successful update.") diff --git a/spec/features/admin/custom_fields/work_packages/list_spec.rb b/spec/features/admin/custom_fields/work_packages/list_spec.rb index aeced2d50b0..ba70aadd1ed 100644 --- a/spec/features/admin/custom_fields/work_packages/list_spec.rb +++ b/spec/features/admin/custom_fields/work_packages/list_spec.rb @@ -53,6 +53,9 @@ RSpec.describe "work package list custom fields", :js do click_on custom_field.name wait_for_reload + + click_link "Items" + wait_for_reload end it "adds new options" do @@ -97,17 +100,15 @@ RSpec.describe "work package list custom fields", :js do fill_in("custom_field_custom_options_attributes_1_value", with: "") fill_in("custom_field_custom_options_attributes_1_value", with: "Sega") - check("custom_field_multi_value") check("custom_field_custom_options_attributes_0_default_value") check("custom_field_custom_options_attributes_2_default_value") within first(".custom-option-row") do - click_on "Move to bottom" + click_link accessible_name: "Move to bottom" end click_on "Save" expect(page).to have_text("Successful update") expect(page).to have_text("Platform") - expect(page).to have_field("custom_field_multi_value", checked: true) %w[Sega Nintendo PC Playstation].each_with_index do |value, i| expect(page).to have_field("custom_field_custom_options_attributes_#{i}_value", with: value) @@ -116,7 +117,7 @@ RSpec.describe "work package list custom fields", :js do expect(page).to have_field("custom_field_custom_options_attributes_0_default_value", checked: false) expect(page).to have_field("custom_field_custom_options_attributes_1_default_value", checked: true) expect(page).to have_field("custom_field_custom_options_attributes_2_default_value", checked: false) - expect(page).to have_field("custom_field_custom_options_attributes_3_default_value", checked: true) + expect(page).to have_field("custom_field_custom_options_attributes_3_default_value", checked: false) end it "shows the correct breadcrumbs" do diff --git a/spec/features/admin/custom_fields/work_packages/multi_value_list_spec.rb b/spec/features/admin/custom_fields/work_packages/multi_value_list_spec.rb index 678b6bace72..122a259b600 100644 --- a/spec/features/admin/custom_fields/work_packages/multi_value_list_spec.rb +++ b/spec/features/admin/custom_fields/work_packages/multi_value_list_spec.rb @@ -48,6 +48,12 @@ RSpec.describe "Multi-value custom fields creation", :js, :selenium do SeleniumHubWaiter.wait fill_in "custom_field_name", with: "My List CF" + click_on "Save" + index_cf_page.expect_and_dismiss_flash(message: "Successful creation.") + + click_link "Items" + wait_for_network_idle + expect(page).to have_css("input#custom_field_custom_options_attributes_0_value") fill_in "custom_field_custom_options_attributes_0_value", with: "A" @@ -65,11 +71,8 @@ RSpec.describe "Multi-value custom fields creation", :js, :selenium do click_on "Save" - index_cf_page.expect_and_dismiss_flash(message: "Successful creation.") - # Edit again SeleniumHubWaiter.wait - page.find("a", text: "My List CF").click expect(page).to have_css("input#custom_field_custom_options_attributes_0_value[value=A]") expect(page).to have_css("input#custom_field_custom_options_attributes_1_value[value=B]") expect(page).to have_css("input#custom_field_custom_options_attributes_2_value[value=C]") diff --git a/spec/features/admin/mcp_configuration_spec.rb b/spec/features/admin/mcp_configuration_spec.rb index de3ccf45906..6dc8df1277d 100644 --- a/spec/features/admin/mcp_configuration_spec.rb +++ b/spec/features/admin/mcp_configuration_spec.rb @@ -142,7 +142,7 @@ RSpec.describe "MCP configuration page", :js do it "hides the entire form, but shows an enterprise banner" do visit mcp_configurations_path - expect(page).to have_enterprise_banner(:corporate) + expect(page).to have_enterprise_banner(:professional) expect(page).to have_no_test_selector("mcp-configuration--server-config-form") expect(page).to have_no_test_selector("mcp-configuration--config-row-name") diff --git a/spec/features/custom_fields/reorder_options_spec.rb b/spec/features/custom_fields/reorder_options_spec.rb index cad5e31000a..8b4ad8ccb75 100644 --- a/spec/features/custom_fields/reorder_options_spec.rb +++ b/spec/features/custom_fields/reorder_options_spec.rb @@ -37,6 +37,10 @@ RSpec.describe "Reordering custom options of a list custom field", :js, :seleniu cf_page.visit! click_link custom_field.name + wait_for_network_idle + click_link "Items" + wait_for_network_idle + click_link "Reorder values alphabetically" cf_page.accept_alert_dialog! expect_flash(message: I18n.t(:notice_successful_update)) diff --git a/spec/features/external_link_capture_spec.rb b/spec/features/external_link_capture_spec.rb index b29a0d817c9..3bde9f62313 100644 --- a/spec/features/external_link_capture_spec.rb +++ b/spec/features/external_link_capture_spec.rb @@ -49,8 +49,7 @@ RSpec.describe "External link capture", :js, :selenium do it "keeps the default external link behaviour" do visit project_wiki_path(project, wiki_page) - href = external_redirect_path(url: external_url) - link = page.find_link("OpenProject", href:) + link = page.find_link("OpenProject", href: external_url) new_window = window_opened_by { link.click } within_window new_window do diff --git a/spec/features/groups/groups_spec.rb b/spec/features/groups/groups_spec.rb index f5a8654f4f8..05d999b2352 100644 --- a/spec/features/groups/groups_spec.rb +++ b/spec/features/groups/groups_spec.rb @@ -71,7 +71,7 @@ RSpec.describe "group memberships through groups page", :js do # Should stay on the form page and show validation error expect(page).to have_text("New group") - expect(page).to have_css(".Banner--error", text: /Department can't be blank./) + expect(page).to have_field(custom_field.name, with: "", validation_error: "Value can't be blank.") fill_in custom_field.name, with: "Engineering" @@ -102,8 +102,8 @@ RSpec.describe "group memberships through groups page", :js do click_on "Save" # Should stay on the form page and show validation error - expect(page).to have_text("Updated Marketing Team") - expect(page).to have_css(".Banner--error", text: /Department can't be blank./) + expect(page).to have_field("Name", with: "Updated Marketing Team") + expect(page).to have_field(custom_field.name, with: "", validation_error: "Value can't be blank.") # Now provide a valid value fill_in custom_field.name, with: "Marketing & Sales" diff --git a/spec/features/projects/attribute_help_texts_spec.rb b/spec/features/projects/attribute_help_texts_spec.rb index 3e5a3316974..55b97913982 100644 --- a/spec/features/projects/attribute_help_texts_spec.rb +++ b/spec/features/projects/attribute_help_texts_spec.rb @@ -30,7 +30,7 @@ require "spec_helper" -RSpec.describe "Project attribute help texts", :js, with_flag: { new_project_overview: true } do +RSpec.describe "Project attribute help texts", :js do let!(:project) { create(:project) } let!(:name_help_text) do diff --git a/spec/features/projects/favorite_spec.rb b/spec/features/projects/favorite_spec.rb index da02e4fb5cb..33b85b4a4da 100644 --- a/spec/features/projects/favorite_spec.rb +++ b/spec/features/projects/favorite_spec.rb @@ -63,7 +63,7 @@ RSpec.describe "Favorite projects", :js do click_link_or_button(accessible_name: "Add to favorites") - expect(page).to have_css "a", accessible_name: "Remove from favorite" + expect(page).to have_css "a", accessible_name: "Remove from favorites" project.reload expect(project).to be_favorited_by(user) diff --git a/spec/features/projects/project_custom_fields/overview_page/dialog/inputs_spec.rb b/spec/features/projects/project_custom_fields/overview_page/dialog/inputs_spec.rb index 62e18063836..3b51779c5fb 100644 --- a/spec/features/projects/project_custom_fields/overview_page/dialog/inputs_spec.rb +++ b/spec/features/projects/project_custom_fields/overview_page/dialog/inputs_spec.rb @@ -203,8 +203,7 @@ RSpec.describe "Edit project custom fields on project overview page", :js do it "renders the custom field as a link" do page.within_test_selector "project-custom-field-#{link_project_custom_field.id}" do - href = external_redirect_path(url: "https://www.openproject.org/") - expect(page).to have_link("https://www.openproject.org", href:) + expect(page).to have_link("https://www.openproject.org", href: "https://www.openproject.org") end end end diff --git a/spec/features/projects/project_custom_fields/overview_page/widget_spec.rb b/spec/features/projects/project_custom_fields/overview_page/widget_spec.rb index ecc1fe273ff..b5b8dba499e 100644 --- a/spec/features/projects/project_custom_fields/overview_page/widget_spec.rb +++ b/spec/features/projects/project_custom_fields/overview_page/widget_spec.rb @@ -31,7 +31,7 @@ require "spec_helper" require_relative "shared_context" -RSpec.describe "Show project custom fields on project overview page", :js, with_flag: { new_project_overview: true } do +RSpec.describe "Show project custom fields on project overview page", :js do include TestSelectorFinders include_context "with seeded projects, members and project custom fields" diff --git a/spec/features/versions/create_spec.rb b/spec/features/versions/create_spec.rb index f83e318324d..2ffc8261b9a 100644 --- a/spec/features/versions/create_spec.rb +++ b/spec/features/versions/create_spec.rb @@ -86,7 +86,7 @@ RSpec.describe "version create", :js do # Should stay on the form page and show validation error expect(page).to have_text("New version") - expect(page).to have_css(".Banner--error", text: /Release Notes can't be blank./) + expect(page).to have_field(custom_field.name, with: "", validation_error: "Value can't be blank") fill_in custom_field.name, with: "Bug fixes and improvements" diff --git a/spec/features/versions/edit_spec.rb b/spec/features/versions/edit_spec.rb index 8f8ccb95b89..dfdab37ba8e 100644 --- a/spec/features/versions/edit_spec.rb +++ b/spec/features/versions/edit_spec.rb @@ -30,7 +30,7 @@ require "spec_helper" -RSpec.describe "version edit" do +RSpec.describe "version edit", :js do let(:user) do create(:user, member_with_permissions: { version.project => %i[manage_versions view_work_packages] }) @@ -85,7 +85,7 @@ RSpec.describe "version edit" do # Should stay on the form page and show validation error expect(page).to have_text("Version 2.1") - expect(page).to have_css(".Banner--error", text: /Release Notes can't be blank./) + expect(page).to have_field(custom_field.name, with: "", validation_error: "Value can't be blank") # Now provide a valid value fill_in custom_field.name, with: "Security updates and bug fixes" diff --git a/spec/features/wiki/wiki_page_external_link_spec.rb b/spec/features/wiki/wiki_page_external_link_spec.rb index 5721c875b3c..97b24bc7e63 100644 --- a/spec/features/wiki/wiki_page_external_link_spec.rb +++ b/spec/features/wiki/wiki_page_external_link_spec.rb @@ -47,8 +47,7 @@ RSpec.describe "Wiki page external link", :js, :selenium do it "opens that link in a new window or tab" do visit project_wiki_path(project, wiki_page) - href = external_redirect_path(url: external_url) - link = page.find_link("OpenProject", href:) + link = page.find_link("OpenProject", href: external_url) new_window = window_opened_by { link.click } within_window new_window do expect(page.current_url).to start_with external_url diff --git a/spec/features/wiki/wiki_page_navigation_spec.rb b/spec/features/wiki/wiki_page_navigation_spec.rb index 8d2e21bf1c8..8a0f5b06d39 100644 --- a/spec/features/wiki/wiki_page_navigation_spec.rb +++ b/spec/features/wiki/wiki_page_navigation_spec.rb @@ -61,7 +61,7 @@ RSpec.describe "Wiki page navigation spec", :js do expect_element_in_view page.find(".tree-menu--item.-selected", text: "Wiki Page No. 55") # Expect permalink being correct (Regression #46351) - permalink = page.all(".op-uc-link_permalink", visible: :all).first - expect(permalink["href"]).to include "/projects/#{project.identifier}/wiki/wiki-page-no-55#wiki-page-no-55" + permalink = page.first(".op-uc-link_permalink", visible: :all) + expect(permalink["href"]).to include "/projects/#{project.identifier}/wiki/wiki-page-no-55#op-frag-wiki-page-no-55" end end diff --git a/spec/features/work_packages/details/custom_fields/custom_field_spec.rb b/spec/features/work_packages/details/custom_fields/custom_field_spec.rb index 9858505b795..c8c6dabebd8 100644 --- a/spec/features/work_packages/details/custom_fields/custom_field_spec.rb +++ b/spec/features/work_packages/details/custom_fields/custom_field_spec.rb @@ -251,8 +251,7 @@ RSpec.describe "custom field inplace editor", :js do field.update "http://example.com" field.expect_state_text "http://example.com" - href = external_redirect_path(url: "http://example.com/") - expect(field.display_element).to have_link("http://example.com", href:) + expect(field.display_element).to have_link("http://example.com", href: "http://example.com") field.update "bogus", expect_failure: true @@ -262,8 +261,8 @@ RSpec.describe "custom field inplace editor", :js do field.save! field.expect_state_text "http://community.openproject.org" - href = external_redirect_path(url: "http://community.openproject.org/") - expect(field.display_element).to have_link("http://community.openproject.org", href:) + expect(field.display_element).to have_link("http://community.openproject.org", + href: "http://community.openproject.org") end end end diff --git a/spec/features/work_packages/details/markdown/todolist_spec.rb b/spec/features/work_packages/details/markdown/todolist_spec.rb index 6122708975c..3aead65ebfa 100644 --- a/spec/features/work_packages/details/markdown/todolist_spec.rb +++ b/spec/features/work_packages/details/markdown/todolist_spec.rb @@ -186,11 +186,9 @@ RSpec.describe "Todolists in CKEditor", :js, :selenium do expect(page).to have_css(".op-uc-list--task-checkbox", count: 3) expect(page).to have_css(".op-uc-list--task-checkbox[checked]", count: 1) - link = external_redirect_path(url: "https://community.openproject.com/") - expect(page).to have_css(".op-uc-list--item a[href='#{link}']") + expect(page).to have_css(".op-uc-list--item a[href='https://community.openproject.com/']") - link = external_redirect_path(url: "https://community.openproject.com/nested") - nested_link = page.find(".op-uc-list--item .op-uc-list--item a[href='#{link}']") + nested_link = page.find(".op-uc-list--item .op-uc-list--item a[href='https://community.openproject.com/nested']") expect(nested_link.text).to eq "This is a link" description = WorkPackage.last.description diff --git a/spec/features/work_packages/work_package_index_spec.rb b/spec/features/work_packages/work_package_index_spec.rb index 78bc0c8f6bc..da3f57f3c1f 100644 --- a/spec/features/work_packages/work_package_index_spec.rb +++ b/spec/features/work_packages/work_package_index_spec.rb @@ -62,8 +62,7 @@ RSpec.describe "Work Packages", "index view", :js do visit project_path(project) within("#content") do - # TODO: change to `have_heading "Project home"` when `new_project_overview` feature flag is removed. - expect(page).to have_heading "Overview" + expect(page).to have_heading project.name end within("#main-menu") do diff --git a/spec/features/wysiwyg/linking_spec.rb b/spec/features/wysiwyg/linking_spec.rb index 9025b6e0949..ea8e67383cb 100644 --- a/spec/features/wysiwyg/linking_spec.rb +++ b/spec/features/wysiwyg/linking_spec.rb @@ -59,7 +59,7 @@ RSpec.describe "Wysiwyg linking", :js do "[http://example.org/link with spaces](http://example.org/link%20with%20spaces)" ) - expect(page).to have_link(href: external_redirect_path(url: "http://example.org/link%20with%20spaces")) + expect(page).to have_link(href: "http://example.org/link%20with%20spaces") end end @@ -76,7 +76,7 @@ RSpec.describe "Wysiwyg linking", :js do editor.insert_link "https://example.org/path" click_on "Save" expect_flash(message: "Successful update.") - expect(page).to have_link(href: external_redirect_path(url: "https://example.org/path")) + expect(page).to have_link(href: "https://example.org/path") end it "allows linking the custom protocol" do diff --git a/spec/lib/open_project/text_formatting/filters/sanitization_filter_spec.rb b/spec/lib/open_project/text_formatting/filters/sanitization_filter_spec.rb new file mode 100644 index 00000000000..678aaa93f3a --- /dev/null +++ b/spec/lib/open_project/text_formatting/filters/sanitization_filter_spec.rb @@ -0,0 +1,104 @@ +# 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 GNU General Public +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require "spec_helper" + +RSpec.describe OpenProject::TextFormatting::Filters::SanitizationFilter do + let(:context) { {} } + + def sanitize(html) + filter = described_class.new(html, context) + result = filter.call + result.respond_to?(:to_html) ? result.to_html : result.to_s + end + + describe "DOM clobbering prevention via fragment id prefix" do + # id/name are prefixed with op-frag- so they cannot clobber document/window. + # Anchors still work because fragment links are rewritten to use the same prefix. + let(:prefix) { described_class::FRAGMENT_ID_PREFIX } + + context "when HTML contains id and name attributes" do + it "prefixes name attribute so it cannot clobber" do + html = '

' + output = sanitize(html) + expect(output).not_to include('name="constructor"') + expect(output).to include("name=\"#{prefix}constructor\"") + end + + it "prefixes name on multiple elements" do + html = '

' + output = sanitize(html) + expect(output).to include("name=\"#{prefix}adoptNode\"") + expect(output).to include("name=\"#{prefix}getElementById\"") + end + + it "prefixes id attribute so it cannot clobber" do + html = '

text

' + output = sanitize(html) + expect(output).not_to include('id="constructor"') + expect(output).to include("id=\"#{prefix}constructor\"") + end + + it "does not double-prefix id or name" do + html = "

x

" + output = sanitize(html) + expect(output).to include("id=\"#{prefix}already\"") + expect(output).not_to include("id=\"#{prefix}#{prefix}") + end + end + + context "when HTML contains same-document fragment links" do + it "rewrites href to use prefix so anchors match" do + html = '

Jump

' + output = sanitize(html) + expect(output).to include("href=\"##{prefix}section\"") + end + + it "does not rewrite empty fragment or full URLs" do + html = '

Top External

' + output = sanitize(html) + expect(output).to include('href="#"') + expect(output).to include('href="https://example.com#anchor"') + end + end + + context "when markdown produces a link with injected img tags (real-world payload)" do + it "prefixes name attributes so they cannot clobber" do + html = <<~HTML +

foobar

+ HTML + output = sanitize(html) + expect(output).not_to match(/name=["']constructor["']/) + expect(output).to include("name=\"#{prefix}constructor\"") + expect(output).to include("name=\"#{prefix}appendChild\"") + end + end + end +end diff --git a/spec/lib/open_project/text_formatting/markdown/attribute_macros_spec.rb b/spec/lib/open_project/text_formatting/markdown/attribute_macros_spec.rb index fb5ae3f17fc..cdb977aee1c 100644 --- a/spec/lib/open_project/text_formatting/markdown/attribute_macros_spec.rb +++ b/spec/lib/open_project/text_formatting/markdown/attribute_macros_spec.rb @@ -56,9 +56,9 @@ RSpec.shared_examples_for "resolving macros" do let(:expected) do <<~EXPECTED -

+

My headline - +

Inline reference to WP: @@ -108,9 +108,9 @@ RSpec.shared_examples_for "resolving macros" do let(:expected) do <<~EXPECTED -

+

My headline - +

Inline reference to WP: diff --git a/spec/lib/open_project/text_formatting/markdown/headings_spec.rb b/spec/lib/open_project/text_formatting/markdown/headings_spec.rb index 4a57c74d524..82e1c530f03 100644 --- a/spec/lib/open_project/text_formatting/markdown/headings_spec.rb +++ b/spec/lib/open_project/text_formatting/markdown/headings_spec.rb @@ -51,11 +51,11 @@ RSpec.describe OpenProject::TextFormatting, let(:expected) do <<~EXPECTED

Some text before

- + the heading

more text

@@ -118,10 +118,10 @@ RSpec.describe OpenProject::TextFormatting, let(:expected) do <<~EXPECTED -

+

2009\\02\\09

diff --git a/spec/lib/open_project/text_formatting/markdown/toc_macro_spec.rb b/spec/lib/open_project/text_formatting/markdown/toc_macro_spec.rb index e3cd8f39253..b2aeb3ce26e 100644 --- a/spec/lib/open_project/text_formatting/markdown/toc_macro_spec.rb +++ b/spec/lib/open_project/text_formatting/markdown/toc_macro_spec.rb @@ -72,21 +72,21 @@ RSpec.describe OpenProject::TextFormatting,

-

+

The first h1 heading

Some text after the first h1 heading

-

+

The first h2 heading

Some text after the first h2 heading

-

+

The first h3 heading

Some text after the first h3 heading

-

+

The second h1 heading

Some text after the second h1 heading

-

+

The second h2 heading

Some text after the second h2 heading

-

+

The second h3 heading

@@ -208,21 +208,21 @@ RSpec.describe OpenProject::TextFormatting,

-

+

1 The first h1 heading

Some text after the first h1 heading

-

+

1.1 The first h2 heading

Some text after the first h2 heading

-

+

1.1.1. The first h3 heading

Some text after the first h3 heading

-

+

2) The second h1 heading

Some text after the second h1 heading

-

+

2.1) The second h2 heading

Some text after the second h2 heading

-

+

2.1.1 - The second h3 heading

diff --git a/spec/migrations/add_view_budgets_to_roles_with_edit_budgets_spec.rb b/spec/migrations/add_view_budgets_to_roles_with_edit_budgets_spec.rb new file mode 100644 index 00000000000..0b8645ba3bd --- /dev/null +++ b/spec/migrations/add_view_budgets_to_roles_with_edit_budgets_spec.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require "spec_helper" +require Rails.root.join("db/migrate/20260223142025_add_view_budgets_to_roles_with_edit_budgets") + +RSpec.describe AddViewBudgetsToRolesWithEditBudgets, type: :model do + describe "up migration" do + context "when a role has edit_budgets but not view_budgets" do + let!(:role) { create(:project_role, permissions: [:edit_budgets]) } + + it "adds view_budgets to the role" do + expect(role.permissions).not_to include(:view_budgets) + + ActiveRecord::Migration.suppress_messages { described_class.migrate(:up) } + + role.reload + expect(role.permissions).to include(:view_budgets) + expect(role.permissions).to include(:edit_budgets) + end + end + + context "when a role already has both edit_budgets and view_budgets" do + let!(:role) { create(:project_role, permissions: %i[edit_budgets view_budgets]) } + + it "does not duplicate view_budgets" do + ActiveRecord::Migration.suppress_messages { described_class.migrate(:up) } + + role.reload + expect(role.permissions.count(:view_budgets)).to eq(1) + end + end + + context "when a role has view_budgets but not edit_budgets" do + let!(:role) { create(:project_role, permissions: [:view_budgets]) } + + it "does not modify the role" do + ActiveRecord::Migration.suppress_messages { described_class.migrate(:up) } + + role.reload + expect(role.permissions).to include(:view_budgets) + expect(role.permissions).not_to include(:edit_budgets) + end + end + + context "when a role has no budget permissions" do + let!(:role) { create(:project_role, permissions: [:view_work_packages]) } + + it "does not modify the role" do + ActiveRecord::Migration.suppress_messages { described_class.migrate(:up) } + + role.reload + expect(role.permissions).not_to include(:view_budgets) + expect(role.permissions).not_to include(:edit_budgets) + end + end + end +end diff --git a/spec/models/custom_field_spec.rb b/spec/models/custom_field_spec.rb index 40f5b15b57b..32b7966708e 100644 --- a/spec/models/custom_field_spec.rb +++ b/spec/models/custom_field_spec.rb @@ -153,17 +153,6 @@ RSpec.describe CustomField do end end - describe "WITH a list field WITHOUT a custom option" do - before do - field.field_format = "list" - end - - it "is not valid" do - expect(field) - .not_to be_valid - end - end - describe "WITH a list field WITH a custom option" do before do field.field_format = "list" diff --git a/spec/requests/api/v3/support/mcp_examples.rb b/spec/requests/api/v3/support/mcp_examples.rb index 548c5845c06..2af1cc7e2d7 100644 --- a/spec/requests/api/v3/support/mcp_examples.rb +++ b/spec/requests/api/v3/support/mcp_examples.rb @@ -58,6 +58,36 @@ RSpec.shared_examples_for "MCP result response" do end end +RSpec.shared_examples_for "MCP tool execution error response" do + let(:result_schema) do + { + required: %w[result], + properties: { + result: { + required: %w[isError content], + properties: { + isError: { type: "boolean" }, + content: { type: "array" }, + structuredContent: { type: ["object"] } + } + } + } + } + end + + include_context "MCP result response" + + it "fulfills the schema of an MCP response" do + subject + expect(last_response.body).to match_json_schema(result_schema) + end + + it "is an error" do + subject + expect(JSON(last_response.body).dig("result", "isError")).to be true + end +end + RSpec.shared_examples_for "MCP response with structured content" do let(:result_schema) do { @@ -158,7 +188,7 @@ RSpec.shared_examples_for "MCP error response" do expect(last_response.headers["WWW-Authenticate"]).to be_nil end - it "fulfills the schema of a JSON RPC response" do + it "fulfills the schema of a JSON RPC error response" do subject expect(last_response.body).to match_json_schema(json_rpc_response_schema) end diff --git a/spec/requests/mcp/mcp_tools/search_portfolios_spec.rb b/spec/requests/mcp/mcp_tools/search_portfolios_spec.rb index 4f81504d87d..e59271369f6 100644 --- a/spec/requests/mcp/mcp_tools/search_portfolios_spec.rb +++ b/spec/requests/mcp/mcp_tools/search_portfolios_spec.rb @@ -179,7 +179,7 @@ RSpec.describe McpTools::SearchPortfolios, with_flag: { mcp_server: true } do context "when passing an invalid portfolio status" do let(:call_args) { { status_code: "blubb" } } - it_behaves_like "MCP error response" + it_behaves_like "MCP tool execution error response" end context "when user can't see portfolios" do diff --git a/spec/requests/mcp/mcp_tools/search_programs_spec.rb b/spec/requests/mcp/mcp_tools/search_programs_spec.rb index 409db88967e..e59b3ad50c2 100644 --- a/spec/requests/mcp/mcp_tools/search_programs_spec.rb +++ b/spec/requests/mcp/mcp_tools/search_programs_spec.rb @@ -179,7 +179,7 @@ RSpec.describe McpTools::SearchPrograms, with_flag: { mcp_server: true } do context "when passing an invalid program status" do let(:call_args) { { status_code: "blubb" } } - it_behaves_like "MCP error response" + it_behaves_like "MCP tool execution error response" end context "when user can't see programs" do diff --git a/spec/requests/mcp/mcp_tools/search_projects_spec.rb b/spec/requests/mcp/mcp_tools/search_projects_spec.rb index 18d59fd610b..86865edf129 100644 --- a/spec/requests/mcp/mcp_tools/search_projects_spec.rb +++ b/spec/requests/mcp/mcp_tools/search_projects_spec.rb @@ -178,7 +178,7 @@ RSpec.describe McpTools::SearchProjects, with_flag: { mcp_server: true } do context "when passing an invalid project status" do let(:call_args) { { status_code: "blubb" } } - it_behaves_like "MCP error response" + it_behaves_like "MCP tool execution error response" end context "when user can't see projects" do diff --git a/spec/support/components/grids/grid_area.rb b/spec/support/components/grids/grid_area.rb index a8a47b8b2e7..ef59dcc5dec 100644 --- a/spec/support/components/grids/grid_area.rb +++ b/spec/support/components/grids/grid_area.rb @@ -7,16 +7,40 @@ module Components include Capybara::RSpecMatchers include RSpec::Matchers + # The CSS grid uses doubled line numbers so that empty placeholder cells + # (used as drag-and-drop targets) can be placed between every content cell. + # Logical position 1 → CSS line 2, position 2 → CSS line 4, etc. + # Use css_line and logical_position helpers to convert between the two. + CSS_LINES_PER_LOGICAL_UNIT = 2 + attr_accessor :area_selector def initialize(*selector) self.area_selector = selector end - def resize_to(row, column) + def grid_value(style_name) + area.style(style_name)[style_name].to_i + end + + def logical_start_row + grid_value("grid-row-start") / CSS_LINES_PER_LOGICAL_UNIT + end + + def logical_start_col + grid_value("grid-column-start") / CSS_LINES_PER_LOGICAL_UNIT + end + + def resize_to(rows, cols) area.hover - area.find(".grid--resizer").drag_to self.class.of(row * 2, column * 2).area + # rows/cols are the desired SIZE of the widget in logical grid units, + # e.g. resize_to(1, 2) → 1 row tall, 2 columns wide. + target_row = logical_start_row + rows - 1 + target_col = logical_start_col + cols - 1 + + area.find(".grid--resizer").drag_to self.class.of(target_row * CSS_LINES_PER_LOGICAL_UNIT, + target_col * CSS_LINES_PER_LOGICAL_UNIT).area end def open_menu diff --git a/spec/support/pages/custom_fields/index.rb b/spec/support/pages/custom_fields/index.rb index 118e8df099e..19baafd47e8 100644 --- a/spec/support/pages/custom_fields/index.rb +++ b/spec/support/pages/custom_fields/index.rb @@ -53,7 +53,7 @@ module Pages end def set_all_projects(value) - find_by_id("custom_field_is_for_all").set value + find_by_id("is_for_all").set value end def has_form_element?(name) diff --git a/spec/views/layouts/base.html.erb_spec.rb b/spec/views/layouts/base.html.erb_spec.rb index b3cd26a4760..3633476b36c 100644 --- a/spec/views/layouts/base.html.erb_spec.rb +++ b/spec/views/layouts/base.html.erb_spec.rb @@ -38,6 +38,7 @@ RSpec.describe "layouts/base" do include Capybara::RSpecMatchers include Redmine::MenuManager::MenuHelper + helper Redmine::MenuManager::MenuHelper let(:user) { build_stubbed(:user) } let(:anonymous) { build_stubbed(:anonymous) } @@ -104,7 +105,7 @@ RSpec.describe "layouts/base" do describe "icons" do let(:current_user) { anonymous } - context "not in development environment" do + context "when not in development environment" do before do render end @@ -189,12 +190,11 @@ RSpec.describe "layouts/base" do let(:a_token) { EnterpriseToken.new } let(:current_user) { anonymous } - context "EE is active and styles are present" do + context "when EE is active and styles are present", with_ee: %i[define_custom_style capture_external_links] do let(:custom_style) { create(:custom_style) } let(:primary_color) { create(:"design_color_primary-button-color") } before do - allow(EnterpriseToken).to receive(:allows_to?).with(:define_custom_style).and_return(true) allow(CustomStyle).to receive(:current).and_return(custom_style) end