From 9819eb1cca7ad7f552850891977056ec3f39693d Mon Sep 17 00:00:00 2001 From: Alexander Brandon Coles Date: Mon, 3 Nov 2025 13:24:32 +0000 Subject: [PATCH 01/71] [#68481] Remove new project overview feature flag --- .../show_component.html.erb | 58 +++-- config/initializers/feature_decisions.rb | 4 - .../spec/features/calendar_widget_spec.rb | 2 +- .../grids/project_attribute_widgets.rb | 14 +- .../grids/overviews_controller_spec.rb | 2 +- .../overviews/page_header_component.html.erb | 36 ++- .../overviews/page_header_component.rb | 6 +- .../side_panel_component.rb | 14 +- .../overviews/show_component.html.erb | 6 +- modules/overviews/config/routes.rb | 4 +- modules/overviews/lib/overviews/engine.rb | 8 +- .../overviews/page_header_component_spec.rb | 21 +- .../overviews/show_component_spec.rb | 20 +- .../low_permissions_page_creation_spec.rb | 4 +- .../features/managing_dashboard_page_spec.rb | 2 +- .../features/managing_overview_page_spec.rb | 216 ------------------ .../spec/features/navigation_spec.rb | 120 +++++----- .../project_description_widget_spec.rb | 2 +- .../spec/routing/overviews_routing_spec.rb | 36 +-- .../overviews/spec/support/pages/overview.rb | 3 +- .../projects/attribute_help_texts_spec.rb | 2 +- .../overview_page/widget_spec.rb | 2 +- .../work_packages/work_package_index_spec.rb | 3 +- 23 files changed, 135 insertions(+), 450 deletions(-) delete mode 100644 modules/overviews/spec/features/managing_overview_page_spec.rb 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/config/initializers/feature_decisions.rb b/config/initializers/feature_decisions.rb index c688b2eecaa..2c26b20015a 100644 --- a/config/initializers/feature_decisions.rb +++ b/config/initializers/feature_decisions.rb @@ -63,10 +63,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 :wp_activity_tab_lazy_pagination, description: "Enables lazy pagination for the activity tab." 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/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/spec/controllers/grids/overviews_controller_spec.rb b/modules/grids/spec/controllers/grids/overviews_controller_spec.rb index 6574480cf90..14465973698 100644 --- a/modules/grids/spec/controllers/grids/overviews_controller_spec.rb +++ b/modules/grids/spec/controllers/grids/overviews_controller_spec.rb @@ -79,7 +79,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/overviews/app/components/overviews/page_header_component.html.erb b/modules/overviews/app/components/overviews/page_header_component.html.erb index d822244147e..d3ef2d69db6 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 d18b6f9bdac..7a5af7c6ee2 100644 --- a/modules/overviews/app/components/overviews/page_header_component.rb +++ b/modules/overviews/app/components/overviews/page_header_component.rb @@ -49,11 +49,7 @@ module Overviews 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/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 6041e0862ca..9d0e12ec101 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 5758eb2ef71..f68d9b847df 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 @@ -95,16 +90,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" @@ -188,7 +173,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) } 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..5109a3cb089 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) } 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..8d63417c886 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 home" + 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 home" 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 home" + 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 3f0ef771da4..703da77c774 100644 --- a/modules/overviews/spec/features/project_description_widget_spec.rb +++ b/modules/overviews/spec/features/project_description_widget_spec.rb @@ -30,7 +30,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 let!(:type) { create(:type) } let!(:portfolio) { create(:portfolio, description: "") } let!(:open_status) { create(:default_status) } 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/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/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/work_packages/work_package_index_spec.rb b/spec/features/work_packages/work_package_index_spec.rb index 78bc0c8f6bc..529ba76db61 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 home" end within("#main-menu") do From 2a5bfe174569e93dfdfa1934bf8afdb414ffafd6 Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Tue, 10 Feb 2026 16:47:55 +0100 Subject: [PATCH 02/71] Sort widgets after drag and drop --- .../components/grids/grid/area.service.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/shared/components/grids/grid/area.service.ts b/frontend/src/app/shared/components/grids/grid/area.service.ts index 4875ebeb5d2..554b5cd3d4e 100644 --- a/frontend/src/app/shared/components/grids/grid/area.service.ts +++ b/frontend/src/app/shared/components/grids/grid/area.service.ts @@ -104,7 +104,7 @@ export class GridAreaService { public async rebuildAndPersist():Promise { const resource = await this.persist(); - this.buildAreas(false); + this.buildAreas(true); return resource; } @@ -261,7 +261,21 @@ export class GridAreaService { } private buildGridWidgetAreas() { - return this.widgetResources.map((widget) => new GridWidgetArea(widget)); + const rowMajorIndex = (w:GridWidgetResource) => + (w.startRow - 1) * this.numColumns + w.startColumn; + + return [...this.widgetResources] + .sort((a, b) => { + const ai = rowMajorIndex(a); + const bi = rowMajorIndex(b); + + if (ai !== bi) return ai - bi; + + const aKey = (a.id ?? `${a.identifier}:${a.startRow}:${a.startColumn}:${a.endRow}:${a.endColumn}`).toString(); + const bKey = (b.id ?? `${b.identifier}:${b.startRow}:${b.startColumn}:${b.endRow}:${b.endColumn}`).toString(); + return aKey.localeCompare(bKey); + }) + .map((widget) => new GridWidgetArea(widget)); } // persist all changes to the areas caused by dragging/resizing From 08fcdb0f78b84a475f78762d2b85f799ced2b02d Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Tue, 10 Feb 2026 17:05:12 +0100 Subject: [PATCH 03/71] Simplify sorting --- .../components/grids/grid/area.service.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/shared/components/grids/grid/area.service.ts b/frontend/src/app/shared/components/grids/grid/area.service.ts index 554b5cd3d4e..bce93234cee 100644 --- a/frontend/src/app/shared/components/grids/grid/area.service.ts +++ b/frontend/src/app/shared/components/grids/grid/area.service.ts @@ -261,21 +261,18 @@ export class GridAreaService { } private buildGridWidgetAreas() { - const rowMajorIndex = (w:GridWidgetResource) => + const index = (w:GridWidgetResource) => (w.startRow - 1) * this.numColumns + w.startColumn; + const key = (w:GridWidgetResource) => + w.id?.toString() ?? `${w.identifier}:${w.startRow}:${w.startColumn}:${w.endRow}:${w.endColumn}`; + return [...this.widgetResources] .sort((a, b) => { - const ai = rowMajorIndex(a); - const bi = rowMajorIndex(b); - - if (ai !== bi) return ai - bi; - - const aKey = (a.id ?? `${a.identifier}:${a.startRow}:${a.startColumn}:${a.endRow}:${a.endColumn}`).toString(); - const bKey = (b.id ?? `${b.identifier}:${b.startRow}:${b.startColumn}:${b.endRow}:${b.endColumn}`).toString(); - return aKey.localeCompare(bKey); + const d = index(a) - index(b); + return d !== 0 ? d : key(a).localeCompare(key(b)); }) - .map((widget) => new GridWidgetArea(widget)); + .map((w) => new GridWidgetArea(w)); } // persist all changes to the areas caused by dragging/resizing From 334f88a2866bf44b9957ea0248356aaddf54a401 Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Wed, 11 Feb 2026 10:43:08 +0100 Subject: [PATCH 04/71] Fix widget tab order and prevent scroll jump after drag & drop --- .../components/grids/grid/area.service.ts | 21 ++++++++++++++++++- .../grids/grid/drag-and-drop.service.ts | 3 ++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/shared/components/grids/grid/area.service.ts b/frontend/src/app/shared/components/grids/grid/area.service.ts index bce93234cee..3afb3ea6eaa 100644 --- a/frontend/src/app/shared/components/grids/grid/area.service.ts +++ b/frontend/src/app/shared/components/grids/grid/area.service.ts @@ -104,7 +104,7 @@ export class GridAreaService { public async rebuildAndPersist():Promise { const resource = await this.persist(); - this.buildAreas(true); + this.buildAreas(false); return resource; } @@ -193,6 +193,7 @@ export class GridAreaService { area.widget = newWidget!; }); + this.sortWidgetAreasRowMajor(); } private buildGridAreas() { @@ -283,6 +284,24 @@ export class GridAreaService { }); } + public sortWidgetAreasRowMajor():void { + const index = (area:GridWidgetArea) => + (area.startRow - 1) * this.numColumns + area.startColumn; + + this.widgetAreas.sort((a, b) => { + const diff = index(a) - index(b); + + if (diff !== 0) return diff; + + // stable fallback + const aKey = (a.widget.id ?? a.guid).toString(); + const bKey = (b.widget.id ?? b.guid).toString(); + + return aKey.localeCompare(bKey); + }); + } + + public addColumn(column:number, excludeRow:number) { this.numColumns++; diff --git a/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts b/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts index 7c130915d36..ce12217ee3f 100644 --- a/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts +++ b/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts @@ -107,7 +107,8 @@ export class GridDragAndDropService implements OnDestroy { if (!this.draggedArea.unchangedSize) { this.layout.writeAreaChangesToWidgets(); this.layout.cleanupUnusedAreas(); - this.layout.rebuildAndPersist(); + this.layout.sortWidgetAreasRowMajor(); + this.layout.persist(); } this.draggedArea = null; From 1315195cec2f951f77b2802089d667f0361d7575 Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Wed, 11 Feb 2026 17:02:49 +0100 Subject: [PATCH 05/71] Add an aria label for action menu in widget title --- .../components/grids/widgets/wp-graph/wp-graph.component.html | 4 +++- .../components/grids/widgets/wp-graph/wp-graph.component.ts | 4 ++++ modules/grids/config/locales/js-en.yml | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) 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..679debac91b 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,9 @@ [name]="widgetName" (onRenamed)="renameWidget($event)"> - diff --git a/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.ts b/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.ts index 2ff6bebd2e7..a42fc743f6c 100644 --- a/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.ts @@ -85,4 +85,8 @@ export class WidgetWpGraphComponent extends AbstractWidgetComponent implements O public get chartType() { return this.graphConfiguration.chartType; } + + public get widgetMenuLabel():string { + return this.i18n.t('js.grid.widget_menu_label', { widgetName: this.widgetName }); + } } diff --git a/modules/grids/config/locales/js-en.yml b/modules/grids/config/locales/js-en.yml index 687b4ded2fc..fed190114d4 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: From d77d4f78bb58953ffa1cc82665cd6b2d7285d143 Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Fri, 13 Feb 2026 09:40:54 +0100 Subject: [PATCH 06/71] Add role note for the focus elemnt of drop modal --- .../src/app/spot/components/drop-modal/drop-modal.component.html | 1 + 1 file changed, 1 insertion(+) 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 4ba47d58029..e955592791b 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 @@ From 0b19144fabb053ba17a6d735b91502c5caae380d Mon Sep 17 00:00:00 2001 From: Alexander Brandon Coles Date: Fri, 12 Dec 2025 12:15:30 -0300 Subject: [PATCH 07/71] [#70292] Primerize Custom Field forms https://community.openproject.org/wp/70292 --- .../custom_fields/details/admin_only_form.rb | 43 ++++ .../details/allow_non_open_versions_form.rb | 48 ++++ app/forms/custom_fields/details/base_form.rb | 71 ++++++ .../details/default_bool_form.rb | 44 ++++ .../details/default_long_text_form.rb | 45 ++++ .../details/default_text_form.rb | 44 ++++ .../custom_fields/details/length_form.rb | 57 +++++ .../details/multi_select_form.rb | 50 ++++ app/forms/custom_fields/details/name_form.rb | 44 ++++ .../details/possible_values_form.rb | 40 +++ .../details/project_attribute_section_form.rb | 57 +++++ .../custom_fields/details/regexp_form.rb | 46 ++++ .../custom_fields/details/required_form.rb | 47 ++++ .../details/right_to_left_form.rb | 44 ++++ .../custom_fields/details/searchable_form.rb | 49 ++++ .../details/user_custom_field_form.rb | 49 ++++ .../details/work_package_custom_field_form.rb | 49 ++++ app/forms/custom_fields/save.rb | 46 ++++ .../project_custom_fields/edit.html.erb | 18 +- .../project_custom_fields/new.html.erb | 18 +- app/views/custom_fields/_form.html.erb | 237 +++++------------- app/views/custom_fields/edit.html.erb | 17 +- app/views/custom_fields/new.html.erb | 21 +- .../dynamic/admin/custom-fields.controller.ts | 42 +--- .../custom_field_format_dependent.rb | 27 +- .../custom_field_format_dependent_spec.rb | 80 ------ 26 files changed, 982 insertions(+), 351 deletions(-) create mode 100644 app/forms/custom_fields/details/admin_only_form.rb create mode 100644 app/forms/custom_fields/details/allow_non_open_versions_form.rb create mode 100644 app/forms/custom_fields/details/base_form.rb create mode 100644 app/forms/custom_fields/details/default_bool_form.rb create mode 100644 app/forms/custom_fields/details/default_long_text_form.rb create mode 100644 app/forms/custom_fields/details/default_text_form.rb create mode 100644 app/forms/custom_fields/details/length_form.rb create mode 100644 app/forms/custom_fields/details/multi_select_form.rb create mode 100644 app/forms/custom_fields/details/name_form.rb create mode 100644 app/forms/custom_fields/details/possible_values_form.rb create mode 100644 app/forms/custom_fields/details/project_attribute_section_form.rb create mode 100644 app/forms/custom_fields/details/regexp_form.rb create mode 100644 app/forms/custom_fields/details/required_form.rb create mode 100644 app/forms/custom_fields/details/right_to_left_form.rb create mode 100644 app/forms/custom_fields/details/searchable_form.rb create mode 100644 app/forms/custom_fields/details/user_custom_field_form.rb create mode 100644 app/forms/custom_fields/details/work_package_custom_field_form.rb create mode 100644 app/forms/custom_fields/save.rb delete mode 100644 spec/lib/open_project/custom_field_format_dependent_spec.rb diff --git a/app/forms/custom_fields/details/admin_only_form.rb b/app/forms/custom_fields/details/admin_only_form.rb new file mode 100644 index 00000000000..aa208f4bccb --- /dev/null +++ b/app/forms/custom_fields/details/admin_only_form.rb @@ -0,0 +1,43 @@ +# 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 CustomFields + module Details + class AdminOnlyForm < BaseForm + form do |f| + f.check_box( + name: :admin_only, + label: attribute_name(:admin_only), + caption: I18n.t("custom_fields.instructions.admin_only") + ) + end + end + end +end diff --git a/app/forms/custom_fields/details/allow_non_open_versions_form.rb b/app/forms/custom_fields/details/allow_non_open_versions_form.rb new file mode 100644 index 00000000000..5295758f2ab --- /dev/null +++ b/app/forms/custom_fields/details/allow_non_open_versions_form.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module CustomFields + module Details + class AllowNonOpenVersionsForm < BaseForm + supports_formats only: %i[version] + + form do |f| + f.check_box( + name: :allow_non_open_versions, + label: attribute_name(:allow_non_open_versions) + ) + end + + def render? + super && model.allow_non_open_versions_possible? + end + end + end +end diff --git a/app/forms/custom_fields/details/base_form.rb b/app/forms/custom_fields/details/base_form.rb new file mode 100644 index 00000000000..b6b7bf4bf8a --- /dev/null +++ b/app/forms/custom_fields/details/base_form.rb @@ -0,0 +1,71 @@ +# 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 CustomFields + module Details + class BaseForm < ApplicationForm + ALLOWED_SUPPORTED_FORMATS_OPERATORS = %i[only except].freeze + private_constant :ALLOWED_SUPPORTED_FORMATS_OPERATORS + + delegate :supported_formats_config, to: :class, private: true + + class << self + def supports_formats(config = {}) + @supported_formats_config = config.transform_values { |formats| Array(formats).map(&:to_sym) } + end + + def supported_formats_config + @supported_formats_config ||= {} + end + end + + def render? + supported_format? + end + + private + + def instructions_for(attribute) + I18n.t(attribute, scope: %i[custom_fields instructions]) + end + + def supported_format? + @supported_format ||= begin + field_format = model.field_format.to_sym + case supported_formats_config + in { only: } then only.include?(field_format) + in { except: } then except.exclude?(field_format) + else true + end + end + end + end + end +end diff --git a/app/forms/custom_fields/details/default_bool_form.rb b/app/forms/custom_fields/details/default_bool_form.rb new file mode 100644 index 00000000000..b039ca0bb22 --- /dev/null +++ b/app/forms/custom_fields/details/default_bool_form.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module CustomFields + module Details + class DefaultBoolForm < BaseForm + supports_formats only: %i[bool] + + form do |f| + f.check_box( + name: :default_value, + label: attribute_name(:default_value) + ) + end + end + end +end diff --git a/app/forms/custom_fields/details/default_long_text_form.rb b/app/forms/custom_fields/details/default_long_text_form.rb new file mode 100644 index 00000000000..e5604257e00 --- /dev/null +++ b/app/forms/custom_fields/details/default_long_text_form.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module CustomFields + module Details + class DefaultLongTextForm < BaseForm + supports_formats only: %i[text] + + form do |f| + f.rich_text_area( + name: :default_value, + label: attribute_name(:default_value), + rich_text_options: { resource: nil, macros: :none } + ) + end + end + end +end diff --git a/app/forms/custom_fields/details/default_text_form.rb b/app/forms/custom_fields/details/default_text_form.rb new file mode 100644 index 00000000000..20fb1aecdc1 --- /dev/null +++ b/app/forms/custom_fields/details/default_text_form.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module CustomFields + module Details + class DefaultTextForm < BaseForm + supports_formats except: %i[list bool date text user version hierarchy calculated_value] + + form do |f| + f.text_field( + name: :default_value, + label: attribute_name(:default_value) + ) + end + end + end +end diff --git a/app/forms/custom_fields/details/length_form.rb b/app/forms/custom_fields/details/length_form.rb new file mode 100644 index 00000000000..61bb40f4438 --- /dev/null +++ b/app/forms/custom_fields/details/length_form.rb @@ -0,0 +1,57 @@ +# 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 CustomFields + module Details + class LengthForm < BaseForm + supports_formats except: %i[list bool date user version link hierarchy calculated_value] + + form do |f| + f.group(layout: :horizontal) do |g| + g.text_field( + name: :min_length, + type: :number, + label: attribute_name(:min_length), + caption: I18n.t(:text_min_max_length_info), + input_width: :xsmall + ) + + g.text_field( + name: :max_length, + type: :number, + label: attribute_name(:max_length), + caption: I18n.t(:text_min_max_length_info), + input_width: :xsmall + ) + end + end + end + end +end diff --git a/app/forms/custom_fields/details/multi_select_form.rb b/app/forms/custom_fields/details/multi_select_form.rb new file mode 100644 index 00000000000..f7a6bcfc339 --- /dev/null +++ b/app/forms/custom_fields/details/multi_select_form.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module CustomFields + module Details + class MultiSelectForm < BaseForm + supports_formats only: %i[list user version hierarchy] + + form do |f| + f.check_box( + name: :multi_value, + label: attribute_name(:multi_value), + caption: instructions_for(:multi_select), + data: { action: "admin--custom-fields#checkOnlyOne" } + ) + end + + def render? + super && model.multi_value_possible? + end + end + end +end diff --git a/app/forms/custom_fields/details/name_form.rb b/app/forms/custom_fields/details/name_form.rb new file mode 100644 index 00000000000..259ee108389 --- /dev/null +++ b/app/forms/custom_fields/details/name_form.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module CustomFields + module Details + class NameForm < BaseForm + form do |f| + f.text_field( + name: :name, + label: attribute_name(:name), + required: true, + data: { test_selector: "op-custom-fields--new-custom-field-name" } + ) + end + end + end +end diff --git a/app/forms/custom_fields/details/possible_values_form.rb b/app/forms/custom_fields/details/possible_values_form.rb new file mode 100644 index 00000000000..764502ed370 --- /dev/null +++ b/app/forms/custom_fields/details/possible_values_form.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module CustomFields + module Details + class PossibleValuesForm < BaseForm + # supports_formats only: %i[list user version hierarchy] + + form do |f| + end + end + end +end diff --git a/app/forms/custom_fields/details/project_attribute_section_form.rb b/app/forms/custom_fields/details/project_attribute_section_form.rb new file mode 100644 index 00000000000..21891904d5a --- /dev/null +++ b/app/forms/custom_fields/details/project_attribute_section_form.rb @@ -0,0 +1,57 @@ +# 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 CustomFields + module Details + class ProjectAttributeSectionForm < BaseForm + form do |f| + f.select_list( + name: :custom_field_section_id, + label: attribute_name(:custom_field_section), + required: true + ) do |list| + available_attribute_sections.each do |label, value| + list.option(label:, value:) + end + end + end + + def render? + super && model.is_a?(ProjectCustomField) + end + + private + + def available_attribute_sections + ProjectCustomFieldSection.pluck(:name, :id) + end + end + end +end diff --git a/app/forms/custom_fields/details/regexp_form.rb b/app/forms/custom_fields/details/regexp_form.rb new file mode 100644 index 00000000000..e7843c17bfa --- /dev/null +++ b/app/forms/custom_fields/details/regexp_form.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module CustomFields + module Details + class RegexpForm < BaseForm + supports_formats except: %i[list bool date user version hierarchy calculated_value] + + form do |f| + f.text_field( + name: :regexp, + label: attribute_name(:regexp), + size: 50, + caption: I18n.t(:text_regexp_info) + ) + end + end + end +end diff --git a/app/forms/custom_fields/details/required_form.rb b/app/forms/custom_fields/details/required_form.rb new file mode 100644 index 00000000000..f77541b3c66 --- /dev/null +++ b/app/forms/custom_fields/details/required_form.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module CustomFields + module Details + class RequiredForm < BaseForm + extend Dry::Initializer + + option :for_project, default: -> { false } + + form do |f| + f.check_box( + name: :is_required, + label: attribute_name(:is_required), + caption: for_project ? instructions_for(:is_required_for_project) : instructions_for(:is_required) + ) + end + end + end +end diff --git a/app/forms/custom_fields/details/right_to_left_form.rb b/app/forms/custom_fields/details/right_to_left_form.rb new file mode 100644 index 00000000000..5c375ba2067 --- /dev/null +++ b/app/forms/custom_fields/details/right_to_left_form.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module CustomFields + module Details + class RightToLeftForm < BaseForm + supports_formats only: %i[text] + + form do |f| + f.check_box( + name: :content_right_to_left, + label: attribute_name(:content_right_to_left) + ) + end + end + end +end diff --git a/app/forms/custom_fields/details/searchable_form.rb b/app/forms/custom_fields/details/searchable_form.rb new file mode 100644 index 00000000000..301f94d2af8 --- /dev/null +++ b/app/forms/custom_fields/details/searchable_form.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 CustomFields + module Details + class SearchableForm < BaseForm + extend Dry::Initializer + + supports_formats except: %i[bool date float int user version hierarchy calculated_value] + + option :for_project, default: -> { false } + + form do |f| + f.check_box( + name: :searchable, + label: attribute_name(:searchable), + caption: for_project ? instructions_for(:searchable_for_project) : instructions_for(:searchable) + ) + end + end + end +end diff --git a/app/forms/custom_fields/details/user_custom_field_form.rb b/app/forms/custom_fields/details/user_custom_field_form.rb new file mode 100644 index 00000000000..13aff78d79d --- /dev/null +++ b/app/forms/custom_fields/details/user_custom_field_form.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 CustomFields + module Details + class UserCustomFieldForm < BaseForm + form do |f| + f.check_box( + name: :admin_only, + label: attribute_name(:admin_only), + caption: instructions_for(:admin_only) + ) + + f.check_box( + name: :editable, + label: attribute_name(:editable), + caption: instructions_for(:admin_only) + ) + end + end + end +end diff --git a/app/forms/custom_fields/details/work_package_custom_field_form.rb b/app/forms/custom_fields/details/work_package_custom_field_form.rb new file mode 100644 index 00000000000..eee72314fac --- /dev/null +++ b/app/forms/custom_fields/details/work_package_custom_field_form.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 CustomFields + module Details + class WorkPackageCustomFieldForm < BaseForm + form do |f| + f.check_box( + name: :is_for_all, + label: attribute_name(:is_for_all), + caption: instructions_for(:is_for_all) + ) + + f.check_box( + name: :is_filter, + label: attribute_name(:is_filter), + caption: instructions_for(:is_filter) + ) + end + end + end +end diff --git a/app/forms/custom_fields/save.rb b/app/forms/custom_fields/save.rb new file mode 100644 index 00000000000..f31f5147381 --- /dev/null +++ b/app/forms/custom_fields/save.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module CustomFields + class Save < ApplicationForm + attr_reader :label, :system_arguments + + form do |f| + f.submit(scheme: :primary, name: :submit, label:, **system_arguments) + end + + def initialize(label: I18n.t(:button_save), **system_arguments) + super() + + @label = label + @system_arguments = system_arguments + end + end +end 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..c5b653b9bd6 100644 --- a/app/views/admin/settings/project_custom_fields/edit.html.erb +++ b/app/views/admin/settings/project_custom_fields/edit.html.erb @@ -44,16 +44,20 @@ See COPYRIGHT and LICENSE files for more details. <%= 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 %> + "admin--custom-fields-format-value": @custom_field.field_format %> - <%= 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 } %> + <%= + 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/form", locals: { f: } %> <% 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" %> + <%= render CustomFields::Save.new(f) %> <% 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..6193d296ab0 100644 --- a/app/views/admin/settings/project_custom_fields/new.html.erb +++ b/app/views/admin/settings/project_custom_fields/new.html.erb @@ -37,16 +37,20 @@ See COPYRIGHT and LICENSE files for more details. <%= 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 %> + "admin--custom-fields-format-value": @custom_field.field_format %> - <%= 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 } %> + <%= + settings_primer_form_with( + model: @custom_field, + scope: :custom_field, + url: admin_settings_project_custom_fields_path, + html: { id: "custom_field_form" } + ) do |f| + %> + <%= render partial: "custom_fields/form", locals: { f: } %> <% 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" %> + <%= render CustomFields::Save.new(f) %> <% end %> <% end %> diff --git a/app/views/custom_fields/_form.html.erb b/app/views/custom_fields/_form.html.erb index bab5ef3c4a8..d383953d628 100644 --- a/app/views/custom_fields/_form.html.erb +++ b/app/views/custom_fields/_form.html.erb @@ -25,73 +25,32 @@ 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) %> - -
+++#%> +<%# locals: (f:) %> +
<%= 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 %> + <%= + render( + Primer::Forms::FormList.new( + CustomFields::Details::NameForm.new(f), + CustomFields::Details::ProjectAttributeSectionForm.new(f), + CustomFields::Details::LengthForm.new(f), + CustomFields::Details::RegexpForm.new(f), + CustomFields::Details::MultiSelectForm.new(f), + CustomFields::Details::PossibleValuesForm.new(f), + CustomFields::Details::AllowNonOpenVersionsForm.new(f), + CustomFields::Details::DefaultBoolForm.new(f), + CustomFields::Details::DefaultLongTextForm.new(f), + CustomFields::Details::DefaultTextForm.new(f) + ) + ) + %> -
> -
- <%= 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 %> -

-
-
- -
> +
<%# format_dependent.attributes(:possibleValues) %> <%= I18n.t("activerecord.attributes.custom_field.possible_values") %> <% if @custom_field.persisted? %>
@@ -119,133 +78,53 @@ See COPYRIGHT and LICENSE files for more details. 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 %> +
+ <% case @custom_field.type %> <% 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 %> -
+ <%= + render( + Primer::Forms::FormList.new( + CustomFields::Details::RequiredForm.new(f), + CustomFields::Details::WorkPackageCustomFieldForm.new(f), + CustomFields::Details::SearchableForm.new(f), + CustomFields::Details::RightToLeftForm.new(f) + ) + ) + %> <% 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") %>

-
-
+ <%= + render( + Primer::Forms::FormList.new( + CustomFields::Details::RequiredForm.new(f), + CustomFields::Details::UserCustomFieldForm.new(f) + ) + ) + %> <% 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") %>

-
-
+ <%= + render( + Primer::Forms::FormList.new( + CustomFields::Details::RequiredForm.new(f, for_project: true), + CustomFields::Details::AdminOnlyForm.new(f), + CustomFields::Details::SearchableForm.new(f, for_project: true) + ) + ) + %> <% else %> -
- <%= f.check_box :is_required %> -
-

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

-
-
+ <%= + render( + Primer::Forms::FormList.new( + CustomFields::Details::RequiredForm.new(f) + ) + ) + %> <% 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..cbf7fa9cb55 100644 --- a/app/views/custom_fields/edit.html.erb +++ b/app/views/custom_fields/edit.html.erb @@ -37,14 +37,17 @@ See COPYRIGHT and LICENSE files for more details. <%= 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" %> + <%= + 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: "form", locals: { f: } %> + <%= render CustomFields::Save.new(f) %> <% end %> <% end %> diff --git a/app/views/custom_fields/new.html.erb b/app/views/custom_fields/new.html.erb index 47f84a9c93d..e508d4cfb9a 100644 --- a/app/views/custom_fields/new.html.erb +++ b/app/views/custom_fields/new.html.erb @@ -51,20 +51,19 @@ See COPYRIGHT and LICENSE files for more details. <%= 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 } %> + <%= + settings_primer_form_with( + model: @custom_field, + scope: :custom_field, + url: custom_fields_path, + html: { id: "custom_field_form" } + ) do |f| + %> + <%= render partial: "form", locals: { 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" - } %> + <%= render CustomFields::Save.new(f, data: { admin__custom_fields_target: "submitButton" }) %> <% end %> <% end %> 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..39f8dbecee5 100644 --- a/frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts +++ b/frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts @@ -40,27 +40,14 @@ export default class CustomFieldsController extends Controller { 'customOptionDefaults', 'customOptionRow', - 'allowNonOpenVersions', - 'defaultBool', - 'defaultLongText', - 'defaultText', - 'length', - 'multiSelect', - 'possibleValues', - 'regexp', - 'searchable', - 'textOrientation', - 'enterpriseBanner', ]; static values = { - formatConfig: Array, hierarchyEnabled: Boolean, format: String, }; - declare readonly formatConfigValue:[string, string, string[]][]; declare readonly formatValue:string; declare readonly hierarchyEnabledValue:boolean; @@ -72,18 +59,6 @@ export default class CustomFieldsController extends Controller { 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() { @@ -184,12 +159,12 @@ export default class CustomFieldsController extends Controller { const cb = event.target as HTMLInputElement; if (cb.checked) { - const multi = this.multiSelectTargets[0] as HTMLInputElement|undefined; + const multi = undefined; // FIXME this.multiSelectTargets[0] as HTMLInputElement|undefined; - if (multi?.checked === false) { - this.customOptionDefaultsTargets.forEach((el) => (el.checked = false)); - cb.checked = true; - } + // if (multi?.checked === false) { + // this.customOptionDefaultsTargets.forEach((el) => (el.checked = false)); + // cb.checked = true; + // } } } @@ -252,13 +227,6 @@ export default class CustomFieldsController extends Controller { 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/lib/open_project/custom_field_format_dependent.rb b/lib/open_project/custom_field_format_dependent.rb index 6b29c3da5bd..1ba4ff5a6d9 100644 --- a/lib/open_project/custom_field_format_dependent.rb +++ b/lib/open_project/custom_field_format_dependent.rb @@ -29,42 +29,23 @@ module OpenProject class CustomFieldFormatDependent CONFIG = { - allowNonOpenVersions: [:only, %w[version]], - defaultBool: [:only, %w[bool]], - defaultLongText: [:only, %w[text]], - defaultText: [:except, %w[list bool date text user version hierarchy calculated_value]], - enterpriseBanner: [:only, %w[hierarchy]], - formula: [:only, %w[calculated_value]], - length: [:except, %w[list bool date user version link hierarchy calculated_value]], - multiSelect: [:only, %w[list user version hierarchy]], possibleValues: [:only, %w[list]], - regexp: [:except, %w[list bool date user version hierarchy calculated_value]], - searchable: [:except, %w[bool date float int user version hierarchy calculated_value]], - textOrientation: [:only, %w[text]] + formula: [:only, %w[calculated_value]], + enterpriseBanner: [:only, %w[hierarchy]] }.freeze - def self.stimulus_config - CONFIG - .map { |target_name, (operator, formats)| [target_name, operator, formats] }.to_json - end - attr_reader :format def initialize(format) @format = format end - def attributes(target_name) + def visible?(target_name) operator, formats = CONFIG[target_name.to_sym] fail ArgumentError, "Unknown target name #{target_name}" unless formats - visible = operator == :only ? format.in?(formats) : !format.in?(formats) - - ApplicationController.helpers.tag.attributes( - data: { "admin--custom-fields-target": target_name }, - hidden: !visible - ) + operator == :only ? format.in?(formats) : !format.in?(formats) end end end diff --git a/spec/lib/open_project/custom_field_format_dependent_spec.rb b/spec/lib/open_project/custom_field_format_dependent_spec.rb deleted file mode 100644 index 79ff275d8a9..00000000000 --- a/spec/lib/open_project/custom_field_format_dependent_spec.rb +++ /dev/null @@ -1,80 +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 OpenProject::CustomFieldFormatDependent do - describe ".stimulus_config" do - it "returns a json with expected structure" do - expect(JSON.parse(described_class.stimulus_config)).to all match([be_a(String), be_a(String), all(be_a(String))]) - end - end - - describe "#attributes" do - let(:instance) { described_class.new(format) } - let(:format) { "string" } - - subject(:call) { instance.attributes(target_name) } - - context "for targets using operator only" do - let(:target_name) { :defaultLongText } - - context "for matching format" do - let(:format) { "text" } - - it { is_expected.to be_html_safe & eq('data-admin--custom-fields-target="defaultLongText"') } - end - - context "for non matching format" do - it { is_expected.to be_html_safe & eq('data-admin--custom-fields-target="defaultLongText" hidden="hidden"') } - end - end - - context "for targets using operator except" do - let(:target_name) { :defaultText } - - context "for matching format" do - let(:format) { "text" } - - it { is_expected.to be_html_safe & eq('data-admin--custom-fields-target="defaultText" hidden="hidden"') } - end - - context "for non matching format" do - it { is_expected.to be_html_safe & eq('data-admin--custom-fields-target="defaultText"') } - end - end - - context "for unknown target" do - let(:target_name) { :foo } - - it { expect { call }.to raise_error(ArgumentError) } - end - end -end From 102e333aa7db37fb05c7bfebaf3974ab59f2de94 Mon Sep 17 00:00:00 2001 From: Alexander Brandon Coles Date: Fri, 24 Oct 2025 17:58:40 +0100 Subject: [PATCH 08/71] Teach TableComponent to handle :table_arguments --- app/components/table_component.html.erb | 4 ++-- app/components/table_component.rb | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) 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..a5ab35781ee 100644 --- a/app/components/table_component.rb +++ b/app/components/table_component.rb @@ -31,8 +31,17 @@ ## # Abstract view component. Subclass this for a concrete table. class TableComponent < ApplicationComponent - def initialize(rows: [], **) + 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][:controller] = "table-highlighting" end class << self From 9368247f13387b385d0f8bea48b4b9bf873509ab Mon Sep 17 00:00:00 2001 From: Alexander Brandon Coles Date: Fri, 12 Dec 2025 18:46:38 -0300 Subject: [PATCH 09/71] Possible values table --- .../custom_options/base_row_component.rb | 128 ++++++++++++++ .../custom_options/row_component.rb | 55 ++++++ .../custom_options/table_component.rb | 69 ++++++++ .../custom_options_component.html.erb | 48 ++++++ .../custom_fields/custom_options_component.rb | 65 +++++++ .../details/project_attribute_section_form.rb | 2 +- .../project_custom_fields/edit.html.erb | 3 +- .../project_custom_fields/new.html.erb | 3 +- .../custom_fields/_custom_options.html.erb | 159 ------------------ app/views/custom_fields/_form.html.erb | 35 +--- app/views/custom_fields/edit.html.erb | 3 +- app/views/custom_fields/new.html.erb | 3 +- .../dynamic/admin/custom-fields.controller.ts | 73 ++++---- .../custom_options/row_component_spec.rb | 112 ++++++++++++ .../custom_options/table_component_spec.rb | 65 +++++++ .../custom_options_component_spec.rb | 68 ++++++++ 16 files changed, 654 insertions(+), 237 deletions(-) create mode 100644 app/components/custom_fields/custom_options/base_row_component.rb create mode 100644 app/components/custom_fields/custom_options/row_component.rb create mode 100644 app/components/custom_fields/custom_options/table_component.rb create mode 100644 app/components/custom_fields/custom_options_component.html.erb create mode 100644 app/components/custom_fields/custom_options_component.rb delete mode 100644 app/views/custom_fields/_custom_options.html.erb create mode 100644 spec/components/custom_fields/custom_options/row_component_spec.rb create mode 100644 spec/components/custom_fields/custom_options/table_component_spec.rb create mode 100644 spec/components/custom_fields/custom_options_component_spec.rb diff --git a/app/components/custom_fields/custom_options/base_row_component.rb b/app/components/custom_fields/custom_options/base_row_component.rb new file mode 100644 index 00000000000..cd20942429d --- /dev/null +++ b/app/components/custom_fields/custom_options/base_row_component.rb @@ -0,0 +1,128 @@ +# 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 CustomFields + module CustomOptions + class BaseRowComponent < ::RowComponent + with_collection_parameter :row + + alias :custom_option :model + + delegate :form, :custom_field, to: :table + + attr_reader :index, :templated + + def initialize(row_counter:, templated: false, **) + @index = row_counter || "INDEX" + @templated = templated + + super + end + + def button_links + [ + primer_action_button( + icon: :"move-to-top", + label: t(:label_sort_highest), + data: { action: "admin--custom-fields#moveRowToTheTop" } + ), + + primer_action_button( + icon: :"arrow-up", + label: t(:label_sort_higher), + data: { action: "admin--custom-fields#moveRowUp" } + ), + + primer_action_button( + icon: :"arrow-down", + label: t(:label_sort_lower), + data: { action: "admin--custom-fields#moveRowDown" } + ), + + primer_action_button( + icon: :"move-to-bottom", + label: t(:label_sort_lowest), + data: { action: "admin--custom-fields#moveRowToTheBottom" } + ), + + primer_action_button( + icon: :trash, + label: t(:button_delete), + data: { + action: "admin--custom-fields#removeOption", + turbo_method: :delete, + turbo_confirm: t(:"custom_fields.confirm_destroy_option") + } + ) + ] + end + + def row_css_class + "dragula-element custom-option-row" + end + + def prefix + raise NotImplementedError + end + + private + + def primer_text_field(name:, **) + with_form do |f| + f.text_field(name:, visually_hide_label: true, **) + end + end + + def primer_check_box(name:, **, &) + with_form do |f| + f.check_box(name:, visually_hide_label: true, **, &) + end + end + + def primer_action_button(icon:, label:, **system_arguments) + render( + Primer::Beta::IconButton.new( + scheme: :invisible, + icon:, + tooltip_direction: :se, + aria: { label: }, + **system_arguments + ) + ) + end + + def with_form(&) + form.fields_for(prefix, custom_option) do |fields| + render_inline_form(fields, &) + end + end + end + end +end diff --git a/app/components/custom_fields/custom_options/row_component.rb b/app/components/custom_fields/custom_options/row_component.rb new file mode 100644 index 00000000000..a79c1957408 --- /dev/null +++ b/app/components/custom_fields/custom_options/row_component.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module CustomFields + module CustomOptions + class RowComponent < BaseRowComponent + def value + # safe_join([ + # hidden_field :id, disabled: true + # ]) + primer_text_field(name: :value, label: CustomOption.human_attribute_name(:value)) + end + + def default_value + primer_check_box( + name: :default_value, + label: t(:label_default), + data: { + "admin--custom-fields-target": "customOptionDefaults", + action: "admin--custom-fields#uncheckOtherDefaults" + } + ) + end + + def prefix = :custom_options + end + end +end diff --git a/app/components/custom_fields/custom_options/table_component.rb b/app/components/custom_fields/custom_options/table_component.rb new file mode 100644 index 00000000000..358a2ce5f02 --- /dev/null +++ b/app/components/custom_fields/custom_options/table_component.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module CustomFields + module CustomOptions + class TableComponent < ::TableComponent + options :form + options :custom_field + + columns :value, :default_value + + def row_class + RowComponent + end + + def headers + [ + ["value", { caption: CustomOption.human_attribute_name(:value) }], + ["default_value", { caption: t(:label_default) }] + ] + end + + def sortable? + false + end + + def inline_create_link + render( + Primer::Beta::IconButton.new( + scheme: :invisible, + icon: :plus, + tooltip_direction: :e, + aria: { label: t(:button_add) }, + data: { action: "admin--custom-fields#addOption" }, + test_selector: "add-custom-option", + mt: 2 + ) + ) + end + end + end +end diff --git a/app/components/custom_fields/custom_options_component.html.erb b/app/components/custom_fields/custom_options_component.html.erb new file mode 100644 index 00000000000..98689d88377 --- /dev/null +++ b/app/components/custom_fields/custom_options_component.html.erb @@ -0,0 +1,48 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + + +<% 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(table) %> + + + <%= render(template_row) %> +
diff --git a/app/components/custom_fields/custom_options_component.rb b/app/components/custom_fields/custom_options_component.rb new file mode 100644 index 00000000000..b8dc06f37ba --- /dev/null +++ b/app/components/custom_fields/custom_options_component.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module CustomFields + class CustomOptionsComponent < ApplicationComponent + extend Dry::Initializer + + option :form + option :custom_field + + attr_reader :custom_options, :template_object + + def initialize(...) + super + + @custom_options = @custom_field.custom_options + @template_object = @custom_options.build + end + + private + + def table + @table ||= CustomOptions::TableComponent.new( + rows: custom_options, + form:, + custom_field:, + table_arguments: { + id: "custom_options", + data: { admin__custom_fields_target: "table" } + } + ) + end + + def template_row + CustomOptions::RowComponent.new(row: template_object, row_counter: nil, table:, templated: true) + end + end +end diff --git a/app/forms/custom_fields/details/project_attribute_section_form.rb b/app/forms/custom_fields/details/project_attribute_section_form.rb index 21891904d5a..b169a103aac 100644 --- a/app/forms/custom_fields/details/project_attribute_section_form.rb +++ b/app/forms/custom_fields/details/project_attribute_section_form.rb @@ -34,7 +34,7 @@ module CustomFields form do |f| f.select_list( name: :custom_field_section_id, - label: attribute_name(:custom_field_section), + label: ProjectCustomField.human_attribute_name(:custom_field_section), required: true ) do |list| available_attribute_sections.each do |label, value| 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 c5b653b9bd6..367c86f153d 100644 --- a/app/views/admin/settings/project_custom_fields/edit.html.erb +++ b/app/views/admin/settings/project_custom_fields/edit.html.erb @@ -44,7 +44,8 @@ See COPYRIGHT and LICENSE files for more details. <%= error_messages_for "custom_field" %> <% content_controller "admin--custom-fields", - "admin--custom-fields-format-value": @custom_field.field_format %> + "admin--custom-fields-format-value": @custom_field.field_format, + "admin--custom-fields-item-count-value": @custom_field.custom_options.size %> <%= settings_primer_form_with( 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 6193d296ab0..a4197cac852 100644 --- a/app/views/admin/settings/project_custom_fields/new.html.erb +++ b/app/views/admin/settings/project_custom_fields/new.html.erb @@ -37,7 +37,8 @@ See COPYRIGHT and LICENSE files for more details. <%= error_messages_for "custom_field" %> <% content_controller "admin--custom-fields", - "admin--custom-fields-format-value": @custom_field.field_format %> + "admin--custom-fields-format-value": @custom_field.field_format, + "admin--custom-fields-item-count-value": @custom_field.custom_options.size %> <%= settings_primer_form_with( diff --git a/app/views/custom_fields/_custom_options.html.erb b/app/views/custom_fields/_custom_options.html.erb deleted file mode 100644 index 4684985f9b5..00000000000 --- a/app/views/custom_fields/_custom_options.html.erb +++ /dev/null @@ -1,159 +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. - -++#%> - -<% custom_field = f.object %> -<% custom_field.custom_options.build if custom_field.custom_options.empty? %> - -
-
- - - - - - - - - - - - - - - - - - <% custom_field.custom_options.each_with_index do |custom_option, i| %> - <%= f.fields_for :custom_options, custom_option do |co_f| %> - - - - - - - <% end %> - <% end %> - -
-
-
- - <%= t("activerecord.attributes.custom_value.value") %> - -
-
-
-
-
- - <%= t(:label_default) %> - -
-
-
-
-
- - <%= t(:button_sort) %> - -
-
-
-
-
-
- - <%= 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.check_box :default_value, - disabled: true, - container_class: "custom-option-default-value", - data: { - "admin--custom-fields-target": "customOptionDefaults", - action: "admin--custom-fields#uncheckOtherDefaults" - }, - no_label: true %> - - - - <%= op_icon("icon-context icon-sort-up icon-small") %> - - - <%= op_icon("icon-context icon-arrow-up2 icon-small") %> - - - <%= op_icon("icon-context icon-arrow-down2 icon-small") %> - - - <%= op_icon("icon-context icon-sort-down icon-small") %> - - - - <%= link_to "", - delete_option_of_custom_field_path(id: custom_field.id || 0, option_id: custom_option.id || 0), - data: { - turbo_method: :delete, - action: "admin--custom-fields#removeOption", - turbo_confirm: t(:"custom_fields.confirm_destroy_option") - }, - class: "icon icon-delete delete-custom-option", - title: t(:button_delete) %> -
- -
-
diff --git a/app/views/custom_fields/_form.html.erb b/app/views/custom_fields/_form.html.erb index d383953d628..7d1da90ea0e 100644 --- a/app/views/custom_fields/_form.html.erb +++ b/app/views/custom_fields/_form.html.erb @@ -48,38 +48,9 @@ See COPYRIGHT and LICENSE files for more details. ) %> - - -
<%# format_dependent.attributes(:possibleValues) %> - <%= 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 - %> -
-
- + <%= I18n.t("activerecord.attributes.custom_field.possible_values") %> + <%# format_dependent.attributes(:possibleValues) %> + <%= render(CustomFields::CustomOptionsComponent.new(form: f, custom_field: @custom_field)) %> <%= call_hook(:view_custom_fields_form_upper_box, 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 cbf7fa9cb55..4a2623545d0 100644 --- a/app/views/custom_fields/edit.html.erb +++ b/app/views/custom_fields/edit.html.erb @@ -37,7 +37,8 @@ See COPYRIGHT and LICENSE files for more details. <%= error_messages_for "custom_field" %> <% content_controller "admin--custom-fields", - "admin--custom-fields-format-value": @custom_field.field_format %> + "admin--custom-fields-format-value": @custom_field.field_format, + "admin--custom-fields-item-count-value": @custom_field.custom_options.size %> <%= settings_primer_form_with( diff --git a/app/views/custom_fields/new.html.erb b/app/views/custom_fields/new.html.erb index e508d4cfb9a..a39af02320c 100644 --- a/app/views/custom_fields/new.html.erb +++ b/app/views/custom_fields/new.html.erb @@ -52,7 +52,8 @@ See COPYRIGHT and LICENSE files for more details. <% content_controller "admin--custom-fields", "admin--custom-fields-format-value": @custom_field.field_format, - "admin--custom-fields-hierarchy-enabled-value": EnterpriseToken.allows_to?(:custom_field_hierarchies) %> + "admin--custom-fields-hierarchy-enabled-value": EnterpriseToken.allows_to?(:custom_field_hierarchies), + "admin--custom-fields-item-count-value": @custom_field.custom_options.size %> <%= settings_primer_form_with( 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 39f8dbecee5..4b6c364ac0e 100644 --- a/frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts +++ b/frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts @@ -37,6 +37,8 @@ export default class CustomFieldsController extends Controller { 'dragContainer', 'submitButton', + 'template', + 'table', 'customOptionDefaults', 'customOptionRow', @@ -44,10 +46,12 @@ export default class CustomFieldsController extends Controller { ]; static values = { + itemCount: Number, hierarchyEnabled: Boolean, format: String, }; + declare itemCountValue:number; declare readonly formatValue:string; declare readonly hierarchyEnabledValue:boolean; @@ -57,10 +61,16 @@ export default class CustomFieldsController extends Controller { declare readonly submitButtonTarget:HTMLButtonElement; declare readonly hasSubmitButtonTarget:boolean; + declare readonly templateTarget:HTMLElement; + declare readonly tableTarget:HTMLTableElement; + declare readonly customOptionDefaultsTargets:HTMLInputElement[]; - declare readonly customOptionRowTargets:HTMLTableRowElement[]; declare readonly enterpriseBannerTarget:HTMLElement; + get customOptionRows() { + return [...this.tableTarget.tBodies[0].rows]; + } + connect() { if (this.hasDragContainerTarget) { this.setupDragAndDrop(); @@ -71,9 +81,9 @@ export default class CustomFieldsController extends Controller { moveRowUp(event:{ target:HTMLElement }) { const row = event.target.closest('tr')!; - const idx = this.customOptionRowTargets.indexOf(row); + const idx = this.customOptionRows.indexOf(row); if (idx > 0) { - this.customOptionRowTargets[idx - 1].before(row); + this.customOptionRows[idx - 1].before(row); } return false; @@ -81,9 +91,10 @@ export default class CustomFieldsController extends Controller { moveRowDown(event:{ target:HTMLElement }) { const row = event.target.closest('tr')!; - const idx = this.customOptionRowTargets.indexOf(row); - if (idx < this.customOptionRowTargets.length - 1) { - this.customOptionRowTargets[idx + 1].after(row); + const idx = this.customOptionRows.indexOf(row); + + if (idx < this.customOptionRows.length - 1) { + this.customOptionRows[idx + 1].after(row); } return false; @@ -91,7 +102,7 @@ export default class CustomFieldsController extends Controller { moveRowToTheTop(event:{ target:HTMLElement }) { const row = event.target.closest('tr')!; - const first = this.customOptionRowTargets[0]; + const first = this.customOptionRows[0]; if (first && first !== row) { first.before(row); @@ -102,7 +113,7 @@ export default class CustomFieldsController extends Controller { moveRowToTheBottom(event:{ target:HTMLElement }) { const row = event.target.closest('tr')!; - const last = this.customOptionRowTargets[this.customOptionRowTargets.length - 1]; + const last = this.customOptionRows[this.customOptionRows.length - 1]; if (last && last !== row) { last.after(row); @@ -112,47 +123,27 @@ export default class CustomFieldsController extends Controller { } removeOption(event:MouseEvent) { - const self = event.target as HTMLAnchorElement; - if (self.href === '#' || self.href.endsWith('/0')) { - const row = self.closest('tr'); + const self = event.target as HTMLButtonElement; + const row = self.closest('tr'); - if (row && this.customOptionRowTargets.length > 1) { - row.remove(); - } - - event.preventDefault(); - event.stopImmediatePropagation(); + if (row && this.customOptionRows.length > 1) { + row.remove(); } + + event.preventDefault(); + event.stopImmediatePropagation(); + return true; // send off deletion } addOption() { - const count = this.customOptionRowTargets.length; - const last = this.customOptionRowTargets[count - 1]; - const dup = last.cloneNode(true) as HTMLElement; + const newRow = this.templateTarget.cloneNode(true); + this.tableTarget.append(newRow); - const input = dup.querySelector('.custom-option-value input') as HTMLInputElement; + const addedRow = this.tableTarget.lastChild as HTMLElement; + addedRow.outerHTML = addedRow.outerHTML.replace(/INDEX/g, this.itemCountValue.toString()); - input.setAttribute('name', `custom_field[custom_options_attributes][${count}][value]`); - input.setAttribute('id', `custom_field_custom_options_attributes_${count}_value`); - input.value = ''; - - dup - .querySelector('.custom-option-id') - ?.remove(); - - const defaultValueCheckbox = dup.querySelector('input[type="checkbox"]') as HTMLInputElement; - const defaultValueHidden = dup.querySelector('input[type="hidden"]') as HTMLInputElement; - - defaultValueHidden.setAttribute('name', `custom_field[custom_options_attributes][${count}][default_value]`); - defaultValueHidden.removeAttribute('id'); - defaultValueCheckbox.setAttribute('name', `custom_field[custom_options_attributes][${count}][default_value]`); - defaultValueCheckbox.setAttribute('id', `custom_field_custom_options_attributes_${count}_default_value`); - defaultValueCheckbox.checked = false; - - last.insertAdjacentElement('afterend', dup); - - return false; + this.itemCountValue += 1; } uncheckOtherDefaults(event:{ target:HTMLElement }) { diff --git a/spec/components/custom_fields/custom_options/row_component_spec.rb b/spec/components/custom_fields/custom_options/row_component_spec.rb new file mode 100644 index 00000000000..f424bb977b3 --- /dev/null +++ b/spec/components/custom_fields/custom_options/row_component_spec.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require "rails_helper" + +RSpec.describe CustomFields::CustomOptions::RowComponent, type: :component do + let(:custom_field) { create(:custom_field) } + let(:table) do + instance_double( + CustomFields::CustomOptions::TableComponent, + columns: %i[value default_value], + custom_field: + ) + end + + subject(:rendered_component) do + render_in_view_context( + described_class, + custom_option, + custom_field, + table, + self + ) do |described_class, custom_option, custom_field, table, spec_context| + primer_form_with(url: "/foo", model: custom_field) do |f| + spec_context.allow(table).to spec_context.receive(:form).and_return(f) + + render(described_class.new(row: custom_option, row_counter: 0, table:)) + end + end + + rendered_content # we want a string rather Nokogiri::HTML5.fragment to workaround it stripping + end + + context "with new custom option" do + let(:custom_option) { build(:custom_option, custom_field:) } + + it "renders row" do + expect(rendered_component).to have_element :tr + end + + it "renders cells" do + expect(rendered_component).to have_element :td, count: 3 + end + + it "renders 'Value' input" do + expect(rendered_component).to have_field "Value" do |field| + expect(field["name"]).to eq "custom_field[custom_options_attributes][0][value]" + expect(field["id"]).to eq "custom_field_custom_options_attributes_0_value" + end + end + + it "renders 'Default' input" do + expect(rendered_component).to have_field "Default" do |field| + expect(field["name"]).to eq "custom_field[custom_options_attributes][0][default_value]" + expect(field["id"]).to eq "custom_field_custom_options_attributes_0_default_value" + end + end + end + + context "with existing custom option" do + let(:custom_option) { create(:custom_option, custom_field:) } + + it "renders row" do + expect(rendered_component).to have_element :tr + end + + it "renders cells" do + expect(rendered_component).to have_element :td, count: 3 + end + + it "renders 'Value' input" do + expect(rendered_component).to have_field "Value" do |field| + expect(field["name"]).to eq "custom_field[custom_options_attributes][#{custom_option.id}][value]" + expect(field["id"]).to eq "custom_field_custom_options_attributes_#{custom_option.id}_value" + end + end + + it "renders 'Default' input" do + expect(rendered_component).to have_field "Default" do |select| + expect(select["name"]).to eq "custom_field[custom_options_attributes][#{custom_option.id}][default_value]" + expect(select["id"]).to eq "custom_field_custom_options_attributes_#{custom_option.id}_default_value" + end + end + end +end diff --git a/spec/components/custom_fields/custom_options/table_component_spec.rb b/spec/components/custom_fields/custom_options/table_component_spec.rb new file mode 100644 index 00000000000..1b743db6667 --- /dev/null +++ b/spec/components/custom_fields/custom_options/table_component_spec.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. +#++ + +require "rails_helper" + +RSpec.describe CustomFields::CustomOptions::TableComponent, type: :component do + def render_component(...) + render_inline(described_class.new(...)) + end + + let(:form) { instance_double(Primer::Forms::Builder, fields_for: "") } + let(:custom_field) { create(:custom_field) } + + subject(:rendered_component) do + render_component(rows: custom_options, form:, custom_field:) + end + + context "with no custom options" do + let(:custom_options) { create_list(:custom_option, 0, custom_field:) } + + it "renders headers" do + expect(rendered_component).to have_css "th .generic-table--sort-header", text: "Value" + expect(rendered_component).to have_css "th .generic-table--sort-header", text: "Default" + end + + it "renders 1 row" do + expect(rendered_component).to have_css "tbody tr", count: 1 + end + end + + context "with custom options" do + let(:custom_options) { create_list(:custom_option, 2, custom_field:) } + + it "renders 2 rows" do + expect(rendered_component).to have_css "tbody tr", count: 2 + end + end +end diff --git a/spec/components/custom_fields/custom_options_component_spec.rb b/spec/components/custom_fields/custom_options_component_spec.rb new file mode 100644 index 00000000000..a91c3491baf --- /dev/null +++ b/spec/components/custom_fields/custom_options_component_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require "rails_helper" + +RSpec.describe CustomFields::CustomOptionsComponent, type: :component do + def render_component(...) + render_inline(described_class.new(...)) + end + + let(:custom_field) { create(:custom_field) } + let(:form) { instance_double(Primer::Forms::Builder, fields_for: "") } + + subject(:rendered_component) do + render_component(custom_field: custom_field, form:) + end + + before do + custom_field.reload + end + + context "with no custom options" do + let!(:custom_options) { create_list(:custom_option, 0, custom_field:) } + + it "renders table" do + expect(rendered_component).to have_element :table do |table| + expect(table["data-admin--custom-fields-target"]).to eq "table" + end + end + end + + context "with custom options" do + let!(:custom_options) { create_list(:custom_option, 2, custom_field:) } + + it "renders table" do + expect(rendered_component).to have_element :table do |table| + expect(table["data-admin--custom-fields-target"]).to eq "table" + end + end + end +end From 04aa644370b1884cd73b8cfbfbbb02316f22a6a4 Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Tue, 17 Feb 2026 08:42:47 +0100 Subject: [PATCH 10/71] fix failures in tests --- .../components/grids/grid/area.service.ts | 14 +------- .../grids/grid/drag-and-drop.service.ts | 3 +- .../components/grids/grid/grid.component.html | 2 +- .../components/grids/grid/grid.component.ts | 13 +++++++ .../my_page/spec/features/my/my_page_spec.rb | 33 +++++++++--------- .../features/my/work_package_table_spec.rb | 6 ++-- .../features/managing_overview_page_spec.rb | 34 +++++++++---------- spec/support/components/grids/grid_area.rb | 21 ++++++++++-- 8 files changed, 73 insertions(+), 53 deletions(-) diff --git a/frontend/src/app/shared/components/grids/grid/area.service.ts b/frontend/src/app/shared/components/grids/grid/area.service.ts index 3afb3ea6eaa..702b35e4fbb 100644 --- a/frontend/src/app/shared/components/grids/grid/area.service.ts +++ b/frontend/src/app/shared/components/grids/grid/area.service.ts @@ -193,7 +193,6 @@ export class GridAreaService { area.widget = newWidget!; }); - this.sortWidgetAreasRowMajor(); } private buildGridAreas() { @@ -262,18 +261,7 @@ export class GridAreaService { } private buildGridWidgetAreas() { - const index = (w:GridWidgetResource) => - (w.startRow - 1) * this.numColumns + w.startColumn; - - const key = (w:GridWidgetResource) => - w.id?.toString() ?? `${w.identifier}:${w.startRow}:${w.startColumn}:${w.endRow}:${w.endColumn}`; - - return [...this.widgetResources] - .sort((a, b) => { - const d = index(a) - index(b); - return d !== 0 ? d : key(a).localeCompare(key(b)); - }) - .map((w) => new GridWidgetArea(w)); + return this.widgetResources.map((widget) => new GridWidgetArea(widget)); } // persist all changes to the areas caused by dragging/resizing diff --git a/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts b/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts index ce12217ee3f..7c130915d36 100644 --- a/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts +++ b/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts @@ -107,8 +107,7 @@ export class GridDragAndDropService implements OnDestroy { if (!this.draggedArea.unchangedSize) { this.layout.writeAreaChangesToWidgets(); this.layout.cleanupUnusedAreas(); - this.layout.sortWidgetAreasRowMajor(); - this.layout.persist(); + this.layout.rebuildAndPersist(); } this.draggedArea = null; 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)) {
Date: Tue, 17 Feb 2026 11:19:34 +0100 Subject: [PATCH 11/71] fix failures in dashboard page --- .../features/managing_dashboard_page_spec.rb | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/modules/overviews/spec/features/managing_dashboard_page_spec.rb b/modules/overviews/spec/features/managing_dashboard_page_spec.rb index b596f50a1ab..aa0c0615268 100644 --- a/modules/overviews/spec/features/managing_dashboard_page_spec.rb +++ b/modules/overviews/spec/features/managing_dashboard_page_spec.rb @@ -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 From 51da23bc4ff9d0e2c51f6accb868cd1ac1f46051 Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Tue, 17 Feb 2026 13:22:17 +0100 Subject: [PATCH 12/71] Remove unused method --- .../components/grids/grid/area.service.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/frontend/src/app/shared/components/grids/grid/area.service.ts b/frontend/src/app/shared/components/grids/grid/area.service.ts index 702b35e4fbb..4875ebeb5d2 100644 --- a/frontend/src/app/shared/components/grids/grid/area.service.ts +++ b/frontend/src/app/shared/components/grids/grid/area.service.ts @@ -272,24 +272,6 @@ export class GridAreaService { }); } - public sortWidgetAreasRowMajor():void { - const index = (area:GridWidgetArea) => - (area.startRow - 1) * this.numColumns + area.startColumn; - - this.widgetAreas.sort((a, b) => { - const diff = index(a) - index(b); - - if (diff !== 0) return diff; - - // stable fallback - const aKey = (a.widget.id ?? a.guid).toString(); - const bKey = (b.widget.id ?? b.guid).toString(); - - return aKey.localeCompare(bKey); - }); - } - - public addColumn(column:number, excludeRow:number) { this.numColumns++; From dd581b800b94182e6dd6469eac13290ac4139246 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Wed, 18 Feb 2026 13:35:42 +0100 Subject: [PATCH 13/71] Add mising form elements to exisiting cf::detailsForm instead of creating two different ways of rendering cf forms --- .../edit_form_header_component.rb | 2 +- .../custom_options/base_row_component.rb | 128 ---------- .../custom_options/row_component.rb | 55 ----- .../custom_options/table_component.rb | 69 ------ .../custom_options_component.html.erb | 48 ---- .../custom_fields/custom_options_component.rb | 65 ----- .../custom_fields/details_component.rb | 6 - .../custom_fields/details/admin_only_form.rb | 43 ---- .../details/allow_non_open_versions_form.rb | 48 ---- app/forms/custom_fields/details/base_form.rb | 71 ------ .../details/default_bool_form.rb | 44 ---- .../details/default_long_text_form.rb | 45 ---- .../details/default_text_form.rb | 44 ---- .../custom_fields/details/length_form.rb | 57 ----- .../details/multi_select_form.rb | 50 ---- app/forms/custom_fields/details/name_form.rb | 44 ---- .../details/possible_values_form.rb | 40 ---- .../details/project_attribute_section_form.rb | 57 ----- .../custom_fields/details/regexp_form.rb | 46 ---- .../custom_fields/details/required_form.rb | 47 ---- .../details/right_to_left_form.rb | 44 ---- .../custom_fields/details/searchable_form.rb | 49 ---- .../details/user_custom_field_form.rb | 49 ---- .../details/work_package_custom_field_form.rb | 49 ---- app/forms/custom_fields/details_form.rb | 106 ++++++++- app/forms/custom_fields/save.rb | 46 ---- .../project_custom_fields/edit.html.erb | 25 +- .../project_custom_fields/new.html.erb | 25 +- app/views/custom_fields/_form.html.erb | 101 -------- app/views/custom_fields/edit.html.erb | 22 +- app/views/custom_fields/new.html.erb | 24 +- config/locales/en.yml | 8 +- .../dynamic/admin/custom-fields.controller.ts | 223 ------------------ .../custom_field_format_dependent.rb | 27 ++- .../custom_options/row_component_spec.rb | 112 --------- .../custom_options/table_component_spec.rb | 65 ----- .../custom_options_component_spec.rb | 68 ------ .../custom_field_format_dependent_spec.rb | 80 +++++++ 38 files changed, 215 insertions(+), 1917 deletions(-) delete mode 100644 app/components/custom_fields/custom_options/base_row_component.rb delete mode 100644 app/components/custom_fields/custom_options/row_component.rb delete mode 100644 app/components/custom_fields/custom_options/table_component.rb delete mode 100644 app/components/custom_fields/custom_options_component.html.erb delete mode 100644 app/components/custom_fields/custom_options_component.rb delete mode 100644 app/forms/custom_fields/details/admin_only_form.rb delete mode 100644 app/forms/custom_fields/details/allow_non_open_versions_form.rb delete mode 100644 app/forms/custom_fields/details/base_form.rb delete mode 100644 app/forms/custom_fields/details/default_bool_form.rb delete mode 100644 app/forms/custom_fields/details/default_long_text_form.rb delete mode 100644 app/forms/custom_fields/details/default_text_form.rb delete mode 100644 app/forms/custom_fields/details/length_form.rb delete mode 100644 app/forms/custom_fields/details/multi_select_form.rb delete mode 100644 app/forms/custom_fields/details/name_form.rb delete mode 100644 app/forms/custom_fields/details/possible_values_form.rb delete mode 100644 app/forms/custom_fields/details/project_attribute_section_form.rb delete mode 100644 app/forms/custom_fields/details/regexp_form.rb delete mode 100644 app/forms/custom_fields/details/required_form.rb delete mode 100644 app/forms/custom_fields/details/right_to_left_form.rb delete mode 100644 app/forms/custom_fields/details/searchable_form.rb delete mode 100644 app/forms/custom_fields/details/user_custom_field_form.rb delete mode 100644 app/forms/custom_fields/details/work_package_custom_field_form.rb delete mode 100644 app/forms/custom_fields/save.rb delete mode 100644 app/views/custom_fields/_form.html.erb delete mode 100644 frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts delete mode 100644 spec/components/custom_fields/custom_options/row_component_spec.rb delete mode 100644 spec/components/custom_fields/custom_options/table_component_spec.rb delete mode 100644 spec/components/custom_fields/custom_options_component_spec.rb create mode 100644 spec/lib/open_project/custom_field_format_dependent_spec.rb 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..85a0319df35 100644 --- a/app/components/admin/custom_fields/edit_form_header_component.rb +++ b/app/components/admin/custom_fields/edit_form_header_component.rb @@ -46,7 +46,7 @@ module Admin } ] - if @custom_field.hierarchical_list? + if @custom_field.hierarchical_list? || @custom_field.list? tabs << { name: "items", path: custom_field_items_path(@custom_field), diff --git a/app/components/custom_fields/custom_options/base_row_component.rb b/app/components/custom_fields/custom_options/base_row_component.rb deleted file mode 100644 index cd20942429d..00000000000 --- a/app/components/custom_fields/custom_options/base_row_component.rb +++ /dev/null @@ -1,128 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module CustomOptions - class BaseRowComponent < ::RowComponent - with_collection_parameter :row - - alias :custom_option :model - - delegate :form, :custom_field, to: :table - - attr_reader :index, :templated - - def initialize(row_counter:, templated: false, **) - @index = row_counter || "INDEX" - @templated = templated - - super - end - - def button_links - [ - primer_action_button( - icon: :"move-to-top", - label: t(:label_sort_highest), - data: { action: "admin--custom-fields#moveRowToTheTop" } - ), - - primer_action_button( - icon: :"arrow-up", - label: t(:label_sort_higher), - data: { action: "admin--custom-fields#moveRowUp" } - ), - - primer_action_button( - icon: :"arrow-down", - label: t(:label_sort_lower), - data: { action: "admin--custom-fields#moveRowDown" } - ), - - primer_action_button( - icon: :"move-to-bottom", - label: t(:label_sort_lowest), - data: { action: "admin--custom-fields#moveRowToTheBottom" } - ), - - primer_action_button( - icon: :trash, - label: t(:button_delete), - data: { - action: "admin--custom-fields#removeOption", - turbo_method: :delete, - turbo_confirm: t(:"custom_fields.confirm_destroy_option") - } - ) - ] - end - - def row_css_class - "dragula-element custom-option-row" - end - - def prefix - raise NotImplementedError - end - - private - - def primer_text_field(name:, **) - with_form do |f| - f.text_field(name:, visually_hide_label: true, **) - end - end - - def primer_check_box(name:, **, &) - with_form do |f| - f.check_box(name:, visually_hide_label: true, **, &) - end - end - - def primer_action_button(icon:, label:, **system_arguments) - render( - Primer::Beta::IconButton.new( - scheme: :invisible, - icon:, - tooltip_direction: :se, - aria: { label: }, - **system_arguments - ) - ) - end - - def with_form(&) - form.fields_for(prefix, custom_option) do |fields| - render_inline_form(fields, &) - end - end - end - end -end diff --git a/app/components/custom_fields/custom_options/row_component.rb b/app/components/custom_fields/custom_options/row_component.rb deleted file mode 100644 index a79c1957408..00000000000 --- a/app/components/custom_fields/custom_options/row_component.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module CustomOptions - class RowComponent < BaseRowComponent - def value - # safe_join([ - # hidden_field :id, disabled: true - # ]) - primer_text_field(name: :value, label: CustomOption.human_attribute_name(:value)) - end - - def default_value - primer_check_box( - name: :default_value, - label: t(:label_default), - data: { - "admin--custom-fields-target": "customOptionDefaults", - action: "admin--custom-fields#uncheckOtherDefaults" - } - ) - end - - def prefix = :custom_options - end - end -end diff --git a/app/components/custom_fields/custom_options/table_component.rb b/app/components/custom_fields/custom_options/table_component.rb deleted file mode 100644 index 358a2ce5f02..00000000000 --- a/app/components/custom_fields/custom_options/table_component.rb +++ /dev/null @@ -1,69 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module CustomOptions - class TableComponent < ::TableComponent - options :form - options :custom_field - - columns :value, :default_value - - def row_class - RowComponent - end - - def headers - [ - ["value", { caption: CustomOption.human_attribute_name(:value) }], - ["default_value", { caption: t(:label_default) }] - ] - end - - def sortable? - false - end - - def inline_create_link - render( - Primer::Beta::IconButton.new( - scheme: :invisible, - icon: :plus, - tooltip_direction: :e, - aria: { label: t(:button_add) }, - data: { action: "admin--custom-fields#addOption" }, - test_selector: "add-custom-option", - mt: 2 - ) - ) - end - end - end -end diff --git a/app/components/custom_fields/custom_options_component.html.erb b/app/components/custom_fields/custom_options_component.html.erb deleted file mode 100644 index 98689d88377..00000000000 --- a/app/components/custom_fields/custom_options_component.html.erb +++ /dev/null @@ -1,48 +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. - -++#%> - - -<% 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(table) %> - - - <%= render(template_row) %> -
diff --git a/app/components/custom_fields/custom_options_component.rb b/app/components/custom_fields/custom_options_component.rb deleted file mode 100644 index b8dc06f37ba..00000000000 --- a/app/components/custom_fields/custom_options_component.rb +++ /dev/null @@ -1,65 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - class CustomOptionsComponent < ApplicationComponent - extend Dry::Initializer - - option :form - option :custom_field - - attr_reader :custom_options, :template_object - - def initialize(...) - super - - @custom_options = @custom_field.custom_options - @template_object = @custom_options.build - end - - private - - def table - @table ||= CustomOptions::TableComponent.new( - rows: custom_options, - form:, - custom_field:, - table_arguments: { - id: "custom_options", - data: { admin__custom_fields_target: "table" } - } - ) - end - - def template_row - CustomOptions::RowComponent.new(row: template_object, row_counter: nil, table:, templated: true) - end - end -end diff --git a/app/components/custom_fields/details_component.rb b/app/components/custom_fields/details_component.rb index a81ad052f37..6d3e345a815 100644 --- a/app/components/custom_fields/details_component.rb +++ b/app/components/custom_fields/details_component.rb @@ -41,12 +41,6 @@ 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 diff --git a/app/forms/custom_fields/details/admin_only_form.rb b/app/forms/custom_fields/details/admin_only_form.rb deleted file mode 100644 index aa208f4bccb..00000000000 --- a/app/forms/custom_fields/details/admin_only_form.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class AdminOnlyForm < BaseForm - form do |f| - f.check_box( - name: :admin_only, - label: attribute_name(:admin_only), - caption: I18n.t("custom_fields.instructions.admin_only") - ) - end - end - end -end diff --git a/app/forms/custom_fields/details/allow_non_open_versions_form.rb b/app/forms/custom_fields/details/allow_non_open_versions_form.rb deleted file mode 100644 index 5295758f2ab..00000000000 --- a/app/forms/custom_fields/details/allow_non_open_versions_form.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class AllowNonOpenVersionsForm < BaseForm - supports_formats only: %i[version] - - form do |f| - f.check_box( - name: :allow_non_open_versions, - label: attribute_name(:allow_non_open_versions) - ) - end - - def render? - super && model.allow_non_open_versions_possible? - end - end - end -end diff --git a/app/forms/custom_fields/details/base_form.rb b/app/forms/custom_fields/details/base_form.rb deleted file mode 100644 index b6b7bf4bf8a..00000000000 --- a/app/forms/custom_fields/details/base_form.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class BaseForm < ApplicationForm - ALLOWED_SUPPORTED_FORMATS_OPERATORS = %i[only except].freeze - private_constant :ALLOWED_SUPPORTED_FORMATS_OPERATORS - - delegate :supported_formats_config, to: :class, private: true - - class << self - def supports_formats(config = {}) - @supported_formats_config = config.transform_values { |formats| Array(formats).map(&:to_sym) } - end - - def supported_formats_config - @supported_formats_config ||= {} - end - end - - def render? - supported_format? - end - - private - - def instructions_for(attribute) - I18n.t(attribute, scope: %i[custom_fields instructions]) - end - - def supported_format? - @supported_format ||= begin - field_format = model.field_format.to_sym - case supported_formats_config - in { only: } then only.include?(field_format) - in { except: } then except.exclude?(field_format) - else true - end - end - end - end - end -end diff --git a/app/forms/custom_fields/details/default_bool_form.rb b/app/forms/custom_fields/details/default_bool_form.rb deleted file mode 100644 index b039ca0bb22..00000000000 --- a/app/forms/custom_fields/details/default_bool_form.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class DefaultBoolForm < BaseForm - supports_formats only: %i[bool] - - form do |f| - f.check_box( - name: :default_value, - label: attribute_name(:default_value) - ) - end - end - end -end diff --git a/app/forms/custom_fields/details/default_long_text_form.rb b/app/forms/custom_fields/details/default_long_text_form.rb deleted file mode 100644 index e5604257e00..00000000000 --- a/app/forms/custom_fields/details/default_long_text_form.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class DefaultLongTextForm < BaseForm - supports_formats only: %i[text] - - form do |f| - f.rich_text_area( - name: :default_value, - label: attribute_name(:default_value), - rich_text_options: { resource: nil, macros: :none } - ) - end - end - end -end diff --git a/app/forms/custom_fields/details/default_text_form.rb b/app/forms/custom_fields/details/default_text_form.rb deleted file mode 100644 index 20fb1aecdc1..00000000000 --- a/app/forms/custom_fields/details/default_text_form.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class DefaultTextForm < BaseForm - supports_formats except: %i[list bool date text user version hierarchy calculated_value] - - form do |f| - f.text_field( - name: :default_value, - label: attribute_name(:default_value) - ) - end - end - end -end diff --git a/app/forms/custom_fields/details/length_form.rb b/app/forms/custom_fields/details/length_form.rb deleted file mode 100644 index 61bb40f4438..00000000000 --- a/app/forms/custom_fields/details/length_form.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class LengthForm < BaseForm - supports_formats except: %i[list bool date user version link hierarchy calculated_value] - - form do |f| - f.group(layout: :horizontal) do |g| - g.text_field( - name: :min_length, - type: :number, - label: attribute_name(:min_length), - caption: I18n.t(:text_min_max_length_info), - input_width: :xsmall - ) - - g.text_field( - name: :max_length, - type: :number, - label: attribute_name(:max_length), - caption: I18n.t(:text_min_max_length_info), - input_width: :xsmall - ) - end - end - end - end -end diff --git a/app/forms/custom_fields/details/multi_select_form.rb b/app/forms/custom_fields/details/multi_select_form.rb deleted file mode 100644 index f7a6bcfc339..00000000000 --- a/app/forms/custom_fields/details/multi_select_form.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class MultiSelectForm < BaseForm - supports_formats only: %i[list user version hierarchy] - - form do |f| - f.check_box( - name: :multi_value, - label: attribute_name(:multi_value), - caption: instructions_for(:multi_select), - data: { action: "admin--custom-fields#checkOnlyOne" } - ) - end - - def render? - super && model.multi_value_possible? - end - end - end -end diff --git a/app/forms/custom_fields/details/name_form.rb b/app/forms/custom_fields/details/name_form.rb deleted file mode 100644 index 259ee108389..00000000000 --- a/app/forms/custom_fields/details/name_form.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class NameForm < BaseForm - form do |f| - f.text_field( - name: :name, - label: attribute_name(:name), - required: true, - data: { test_selector: "op-custom-fields--new-custom-field-name" } - ) - end - end - end -end diff --git a/app/forms/custom_fields/details/possible_values_form.rb b/app/forms/custom_fields/details/possible_values_form.rb deleted file mode 100644 index 764502ed370..00000000000 --- a/app/forms/custom_fields/details/possible_values_form.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class PossibleValuesForm < BaseForm - # supports_formats only: %i[list user version hierarchy] - - form do |f| - end - end - end -end diff --git a/app/forms/custom_fields/details/project_attribute_section_form.rb b/app/forms/custom_fields/details/project_attribute_section_form.rb deleted file mode 100644 index b169a103aac..00000000000 --- a/app/forms/custom_fields/details/project_attribute_section_form.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class ProjectAttributeSectionForm < BaseForm - form do |f| - f.select_list( - name: :custom_field_section_id, - label: ProjectCustomField.human_attribute_name(:custom_field_section), - required: true - ) do |list| - available_attribute_sections.each do |label, value| - list.option(label:, value:) - end - end - end - - def render? - super && model.is_a?(ProjectCustomField) - end - - private - - def available_attribute_sections - ProjectCustomFieldSection.pluck(:name, :id) - end - end - end -end diff --git a/app/forms/custom_fields/details/regexp_form.rb b/app/forms/custom_fields/details/regexp_form.rb deleted file mode 100644 index e7843c17bfa..00000000000 --- a/app/forms/custom_fields/details/regexp_form.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class RegexpForm < BaseForm - supports_formats except: %i[list bool date user version hierarchy calculated_value] - - form do |f| - f.text_field( - name: :regexp, - label: attribute_name(:regexp), - size: 50, - caption: I18n.t(:text_regexp_info) - ) - end - end - end -end diff --git a/app/forms/custom_fields/details/required_form.rb b/app/forms/custom_fields/details/required_form.rb deleted file mode 100644 index f77541b3c66..00000000000 --- a/app/forms/custom_fields/details/required_form.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class RequiredForm < BaseForm - extend Dry::Initializer - - option :for_project, default: -> { false } - - form do |f| - f.check_box( - name: :is_required, - label: attribute_name(:is_required), - caption: for_project ? instructions_for(:is_required_for_project) : instructions_for(:is_required) - ) - end - end - end -end diff --git a/app/forms/custom_fields/details/right_to_left_form.rb b/app/forms/custom_fields/details/right_to_left_form.rb deleted file mode 100644 index 5c375ba2067..00000000000 --- a/app/forms/custom_fields/details/right_to_left_form.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class RightToLeftForm < BaseForm - supports_formats only: %i[text] - - form do |f| - f.check_box( - name: :content_right_to_left, - label: attribute_name(:content_right_to_left) - ) - end - end - end -end diff --git a/app/forms/custom_fields/details/searchable_form.rb b/app/forms/custom_fields/details/searchable_form.rb deleted file mode 100644 index 301f94d2af8..00000000000 --- a/app/forms/custom_fields/details/searchable_form.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class SearchableForm < BaseForm - extend Dry::Initializer - - supports_formats except: %i[bool date float int user version hierarchy calculated_value] - - option :for_project, default: -> { false } - - form do |f| - f.check_box( - name: :searchable, - label: attribute_name(:searchable), - caption: for_project ? instructions_for(:searchable_for_project) : instructions_for(:searchable) - ) - end - end - end -end diff --git a/app/forms/custom_fields/details/user_custom_field_form.rb b/app/forms/custom_fields/details/user_custom_field_form.rb deleted file mode 100644 index 13aff78d79d..00000000000 --- a/app/forms/custom_fields/details/user_custom_field_form.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class UserCustomFieldForm < BaseForm - form do |f| - f.check_box( - name: :admin_only, - label: attribute_name(:admin_only), - caption: instructions_for(:admin_only) - ) - - f.check_box( - name: :editable, - label: attribute_name(:editable), - caption: instructions_for(:admin_only) - ) - end - end - end -end diff --git a/app/forms/custom_fields/details/work_package_custom_field_form.rb b/app/forms/custom_fields/details/work_package_custom_field_form.rb deleted file mode 100644 index eee72314fac..00000000000 --- a/app/forms/custom_fields/details/work_package_custom_field_form.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - module Details - class WorkPackageCustomFieldForm < BaseForm - form do |f| - f.check_box( - name: :is_for_all, - label: attribute_name(:is_for_all), - caption: instructions_for(:is_for_all) - ) - - f.check_box( - name: :is_filter, - label: attribute_name(:is_filter), - caption: instructions_for(:is_filter) - ) - end - end - end -end diff --git a/app/forms/custom_fields/details_form.rb b/app/forms/custom_fields/details_form.rb index 44d17c05556..4eaf8771b8b 100644 --- a/app/forms/custom_fields/details_form.rb +++ b/app/forms/custom_fields/details_form.rb @@ -60,11 +60,33 @@ 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.group(layout: :horizontal) do |g| + g.text_field( + name: :min_length, + type: :number, + label: label(:min_length), + caption: instructions(:min_max), + input_width: :xsmall + ) + + g.text_field( + name: :max_length, + type: :number, + label: label(:max_length), + caption: instructions(:min_max), + input_width: :xsmall + ) + end + end + + if show_regex_field? + details_form.text_field( + name: :regexp, + label: label(:regexp), + size: 50, + caption: instructions(:regexp), + input_width: :medium ) end @@ -86,6 +108,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 +163,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 +219,30 @@ module CustomFields %w[bool].include?(model.field_format) end + def show_default_text_field? + %w[list bool date text user version hierarchy 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 calculated_value].exclude?(model.field_format) + end + + def show_regex_field? + %w[list bool date user version hierarchy calculated_value].exclude?(model.field_format) + end + + def show_right_to_left_field? + %w[text].include?(model.field_format) + end + def show_multi_value_field? model.multi_value_possible? end @@ -171,6 +259,14 @@ module CustomFields model.is_a?(WorkPackageCustomField) end + def show_is_searchable_field? + %w[bool date float int user version hierarchy 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/custom_fields/save.rb b/app/forms/custom_fields/save.rb deleted file mode 100644 index f31f5147381..00000000000 --- a/app/forms/custom_fields/save.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module CustomFields - class Save < ApplicationForm - attr_reader :label, :system_arguments - - form do |f| - f.submit(scheme: :primary, name: :submit, label:, **system_arguments) - end - - def initialize(label: I18n.t(:button_save), **system_arguments) - super() - - @label = label - @system_arguments = system_arguments - end - end -end 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 367c86f153d..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,27 +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-item-count-value": @custom_field.custom_options.size %> - - <%= - 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/form", locals: { f: } %> - <% if @custom_field.new_record? %> - <%= hidden_field_tag "type", @custom_field.type %> - <% end %> - <%= render CustomFields::Save.new(f) %> - <% end %> -<% end %> +<%= render CustomFields::DetailsComponent.new(@custom_field) %> 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 a4197cac852..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,27 +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-item-count-value": @custom_field.custom_options.size %> - - <%= - settings_primer_form_with( - model: @custom_field, - scope: :custom_field, - url: admin_settings_project_custom_fields_path, - html: { id: "custom_field_form" } - ) do |f| - %> - <%= render partial: "custom_fields/form", locals: { f: } %> - <% if @custom_field.new_record? %> - <%= hidden_field_tag "type", @custom_field.type %> - <% end %> - <%= render CustomFields::Save.new(f) %> - <% end %> -<% end %> +<%= render CustomFields::DetailsComponent.new(@custom_field) %> diff --git a/app/views/custom_fields/_form.html.erb b/app/views/custom_fields/_form.html.erb deleted file mode 100644 index 7d1da90ea0e..00000000000 --- a/app/views/custom_fields/_form.html.erb +++ /dev/null @@ -1,101 +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:) %> - -
- <%= f.hidden_field :field_format if @custom_field.new_record? %> - - <%= - render( - Primer::Forms::FormList.new( - CustomFields::Details::NameForm.new(f), - CustomFields::Details::ProjectAttributeSectionForm.new(f), - CustomFields::Details::LengthForm.new(f), - CustomFields::Details::RegexpForm.new(f), - CustomFields::Details::MultiSelectForm.new(f), - CustomFields::Details::PossibleValuesForm.new(f), - CustomFields::Details::AllowNonOpenVersionsForm.new(f), - CustomFields::Details::DefaultBoolForm.new(f), - CustomFields::Details::DefaultLongTextForm.new(f), - CustomFields::Details::DefaultTextForm.new(f) - ) - ) - %> - - <%= I18n.t("activerecord.attributes.custom_field.possible_values") %> - <%# format_dependent.attributes(:possibleValues) %> - <%= render(CustomFields::CustomOptionsComponent.new(form: f, custom_field: @custom_field)) %> - - <%= call_hook(:view_custom_fields_form_upper_box, custom_field: @custom_field, form: f) %> -
- -
- <% case @custom_field.type %> - <% when "WorkPackageCustomField" %> - <%= - render( - Primer::Forms::FormList.new( - CustomFields::Details::RequiredForm.new(f), - CustomFields::Details::WorkPackageCustomFieldForm.new(f), - CustomFields::Details::SearchableForm.new(f), - CustomFields::Details::RightToLeftForm.new(f) - ) - ) - %> - <% when "UserCustomField" %> - <%= - render( - Primer::Forms::FormList.new( - CustomFields::Details::RequiredForm.new(f), - CustomFields::Details::UserCustomFieldForm.new(f) - ) - ) - %> - <% when "ProjectCustomField" %> - <%= - render( - Primer::Forms::FormList.new( - CustomFields::Details::RequiredForm.new(f, for_project: true), - CustomFields::Details::AdminOnlyForm.new(f), - CustomFields::Details::SearchableForm.new(f, for_project: true) - ) - ) - %> - <% else %> - <%= - render( - Primer::Forms::FormList.new( - CustomFields::Details::RequiredForm.new(f) - ) - ) - %> - <% 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 4a2623545d0..70fbff4a008 100644 --- a/app/views/custom_fields/edit.html.erb +++ b/app/views/custom_fields/edit.html.erb @@ -31,24 +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-value": @custom_field.field_format, - "admin--custom-fields-item-count-value": @custom_field.custom_options.size %> - - <%= - 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: "form", locals: { f: } %> - <%= render CustomFields::Save.new(f) %> - <% end %> -<% end %> +<%= render CustomFields::DetailsComponent.new(@custom_field) %> diff --git a/app/views/custom_fields/new.html.erb b/app/views/custom_fields/new.html.erb index a39af02320c..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-value": @custom_field.field_format, - "admin--custom-fields-hierarchy-enabled-value": EnterpriseToken.allows_to?(:custom_field_hierarchies), - "admin--custom-fields-item-count-value": @custom_field.custom_options.size %> - - <%= - settings_primer_form_with( - model: @custom_field, - scope: :custom_field, - url: custom_fields_path, - html: { id: "custom_field_form" } - ) do |f| - %> - <%= render partial: "form", locals: { f: } %> - <%= hidden_field_tag "type", @custom_field.type %> - <%= render CustomFields::Save.new(f, data: { admin__custom_fields_target: "submitButton" }) %> - <% end %> -<% end %> +<%= render CustomFields::DetailsComponent.new(@custom_field) %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 24696a3adf2..a00652d50f1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -417,6 +417,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. @@ -5075,7 +5081,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." @@ -5094,7 +5099,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/frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts b/frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts deleted file mode 100644 index 4b6c364ac0e..00000000000 --- a/frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts +++ /dev/null @@ -1,223 +0,0 @@ -/* - * -- copyright - * OpenProject is an open source project management software. - * Copyright (C) the OpenProject GmbH - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 3. - * - * OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: - * Copyright (C) 2006-2013 Jean-Philippe Lang - * Copyright (C) 2010-2013 the ChiliProject Team - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * See COPYRIGHT and LICENSE files for more details. - * ++ - */ - -import { Controller } from '@hotwired/stimulus'; -import dragula from 'dragula'; - -export default class CustomFieldsController extends Controller { - static targets = [ - 'format', - 'dragContainer', - 'submitButton', - - 'template', - 'table', - 'customOptionDefaults', - 'customOptionRow', - - 'enterpriseBanner', - ]; - - static values = { - itemCount: Number, - hierarchyEnabled: Boolean, - format: String, - }; - - declare itemCountValue:number; - declare readonly formatValue:string; - declare readonly hierarchyEnabledValue:boolean; - - declare readonly formatTarget:HTMLInputElement; - declare readonly dragContainerTarget:HTMLElement; - declare readonly hasDragContainerTarget:boolean; - declare readonly submitButtonTarget:HTMLButtonElement; - declare readonly hasSubmitButtonTarget:boolean; - - declare readonly templateTarget:HTMLElement; - declare readonly tableTarget:HTMLTableElement; - - declare readonly customOptionDefaultsTargets:HTMLInputElement[]; - declare readonly enterpriseBannerTarget:HTMLElement; - - get customOptionRows() { - return [...this.tableTarget.tBodies[0].rows]; - } - - connect() { - if (this.hasDragContainerTarget) { - this.setupDragAndDrop(); - } - - this.adaptInputsToFormat(this.formatValue); - } - - moveRowUp(event:{ target:HTMLElement }) { - const row = event.target.closest('tr')!; - const idx = this.customOptionRows.indexOf(row); - if (idx > 0) { - this.customOptionRows[idx - 1].before(row); - } - - return false; - } - - moveRowDown(event:{ target:HTMLElement }) { - const row = event.target.closest('tr')!; - const idx = this.customOptionRows.indexOf(row); - - if (idx < this.customOptionRows.length - 1) { - this.customOptionRows[idx + 1].after(row); - } - - return false; - } - - moveRowToTheTop(event:{ target:HTMLElement }) { - const row = event.target.closest('tr')!; - const first = this.customOptionRows[0]; - - if (first && first !== row) { - first.before(row); - } - - return false; - } - - moveRowToTheBottom(event:{ target:HTMLElement }) { - const row = event.target.closest('tr')!; - const last = this.customOptionRows[this.customOptionRows.length - 1]; - - if (last && last !== row) { - last.after(row); - } - - return false; - } - - removeOption(event:MouseEvent) { - const self = event.target as HTMLButtonElement; - const row = self.closest('tr'); - - if (row && this.customOptionRows.length > 1) { - row.remove(); - } - - event.preventDefault(); - event.stopImmediatePropagation(); - - return true; // send off deletion - } - - addOption() { - const newRow = this.templateTarget.cloneNode(true); - this.tableTarget.append(newRow); - - const addedRow = this.tableTarget.lastChild as HTMLElement; - addedRow.outerHTML = addedRow.outerHTML.replace(/INDEX/g, this.itemCountValue.toString()); - - this.itemCountValue += 1; - } - - uncheckOtherDefaults(event:{ target:HTMLElement }) { - const cb = event.target as HTMLInputElement; - - if (cb.checked) { - const multi = undefined; // FIXME 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)); - } - } - - private setupDragAndDrop() { - // Make custom fields draggable - const drake = dragula([this.dragContainerTarget], { - isContainer: () => false, - moves: (el, source, handle:HTMLElement) => handle.classList.contains('dragula-handle'), - accepts: () => true, - invalid: () => false, - direction: 'vertical', - copy: false, - copySortSource: false, - revertOnSpill: true, - removeOnSpill: false, - mirrorContainer: this.dragContainerTarget, - ignoreInputTextSelection: true, - }); - - // Setup autoscroll - void window.OpenProject.getPluginContext().then((pluginContext) => { - new pluginContext.classes.DomAutoscrollService( - [ - document.getElementById('content-body')!, - ], - { - margin: 25, - maxSpeed: 10, - scrollWhenOutside: true, - autoScroll: () => drake.dragging, - }, - ); - }); - } - - 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; - } - - - } -} diff --git a/lib/open_project/custom_field_format_dependent.rb b/lib/open_project/custom_field_format_dependent.rb index 1ba4ff5a6d9..6b29c3da5bd 100644 --- a/lib/open_project/custom_field_format_dependent.rb +++ b/lib/open_project/custom_field_format_dependent.rb @@ -29,23 +29,42 @@ module OpenProject class CustomFieldFormatDependent CONFIG = { - possibleValues: [:only, %w[list]], + allowNonOpenVersions: [:only, %w[version]], + defaultBool: [:only, %w[bool]], + defaultLongText: [:only, %w[text]], + defaultText: [:except, %w[list bool date text user version hierarchy calculated_value]], + enterpriseBanner: [:only, %w[hierarchy]], formula: [:only, %w[calculated_value]], - enterpriseBanner: [:only, %w[hierarchy]] + length: [:except, %w[list bool date user version link hierarchy calculated_value]], + multiSelect: [:only, %w[list user version hierarchy]], + possibleValues: [:only, %w[list]], + regexp: [:except, %w[list bool date user version hierarchy calculated_value]], + searchable: [:except, %w[bool date float int user version hierarchy calculated_value]], + textOrientation: [:only, %w[text]] }.freeze + def self.stimulus_config + CONFIG + .map { |target_name, (operator, formats)| [target_name, operator, formats] }.to_json + end + attr_reader :format def initialize(format) @format = format end - def visible?(target_name) + def attributes(target_name) operator, formats = CONFIG[target_name.to_sym] fail ArgumentError, "Unknown target name #{target_name}" unless formats - operator == :only ? format.in?(formats) : !format.in?(formats) + visible = operator == :only ? format.in?(formats) : !format.in?(formats) + + ApplicationController.helpers.tag.attributes( + data: { "admin--custom-fields-target": target_name }, + hidden: !visible + ) end end end diff --git a/spec/components/custom_fields/custom_options/row_component_spec.rb b/spec/components/custom_fields/custom_options/row_component_spec.rb deleted file mode 100644 index f424bb977b3..00000000000 --- a/spec/components/custom_fields/custom_options/row_component_spec.rb +++ /dev/null @@ -1,112 +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 "rails_helper" - -RSpec.describe CustomFields::CustomOptions::RowComponent, type: :component do - let(:custom_field) { create(:custom_field) } - let(:table) do - instance_double( - CustomFields::CustomOptions::TableComponent, - columns: %i[value default_value], - custom_field: - ) - end - - subject(:rendered_component) do - render_in_view_context( - described_class, - custom_option, - custom_field, - table, - self - ) do |described_class, custom_option, custom_field, table, spec_context| - primer_form_with(url: "/foo", model: custom_field) do |f| - spec_context.allow(table).to spec_context.receive(:form).and_return(f) - - render(described_class.new(row: custom_option, row_counter: 0, table:)) - end - end - - rendered_content # we want a string rather Nokogiri::HTML5.fragment to workaround it stripping - end - - context "with new custom option" do - let(:custom_option) { build(:custom_option, custom_field:) } - - it "renders row" do - expect(rendered_component).to have_element :tr - end - - it "renders cells" do - expect(rendered_component).to have_element :td, count: 3 - end - - it "renders 'Value' input" do - expect(rendered_component).to have_field "Value" do |field| - expect(field["name"]).to eq "custom_field[custom_options_attributes][0][value]" - expect(field["id"]).to eq "custom_field_custom_options_attributes_0_value" - end - end - - it "renders 'Default' input" do - expect(rendered_component).to have_field "Default" do |field| - expect(field["name"]).to eq "custom_field[custom_options_attributes][0][default_value]" - expect(field["id"]).to eq "custom_field_custom_options_attributes_0_default_value" - end - end - end - - context "with existing custom option" do - let(:custom_option) { create(:custom_option, custom_field:) } - - it "renders row" do - expect(rendered_component).to have_element :tr - end - - it "renders cells" do - expect(rendered_component).to have_element :td, count: 3 - end - - it "renders 'Value' input" do - expect(rendered_component).to have_field "Value" do |field| - expect(field["name"]).to eq "custom_field[custom_options_attributes][#{custom_option.id}][value]" - expect(field["id"]).to eq "custom_field_custom_options_attributes_#{custom_option.id}_value" - end - end - - it "renders 'Default' input" do - expect(rendered_component).to have_field "Default" do |select| - expect(select["name"]).to eq "custom_field[custom_options_attributes][#{custom_option.id}][default_value]" - expect(select["id"]).to eq "custom_field_custom_options_attributes_#{custom_option.id}_default_value" - end - end - end -end diff --git a/spec/components/custom_fields/custom_options/table_component_spec.rb b/spec/components/custom_fields/custom_options/table_component_spec.rb deleted file mode 100644 index 1b743db6667..00000000000 --- a/spec/components/custom_fields/custom_options/table_component_spec.rb +++ /dev/null @@ -1,65 +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 "rails_helper" - -RSpec.describe CustomFields::CustomOptions::TableComponent, type: :component do - def render_component(...) - render_inline(described_class.new(...)) - end - - let(:form) { instance_double(Primer::Forms::Builder, fields_for: "") } - let(:custom_field) { create(:custom_field) } - - subject(:rendered_component) do - render_component(rows: custom_options, form:, custom_field:) - end - - context "with no custom options" do - let(:custom_options) { create_list(:custom_option, 0, custom_field:) } - - it "renders headers" do - expect(rendered_component).to have_css "th .generic-table--sort-header", text: "Value" - expect(rendered_component).to have_css "th .generic-table--sort-header", text: "Default" - end - - it "renders 1 row" do - expect(rendered_component).to have_css "tbody tr", count: 1 - end - end - - context "with custom options" do - let(:custom_options) { create_list(:custom_option, 2, custom_field:) } - - it "renders 2 rows" do - expect(rendered_component).to have_css "tbody tr", count: 2 - end - end -end diff --git a/spec/components/custom_fields/custom_options_component_spec.rb b/spec/components/custom_fields/custom_options_component_spec.rb deleted file mode 100644 index a91c3491baf..00000000000 --- a/spec/components/custom_fields/custom_options_component_spec.rb +++ /dev/null @@ -1,68 +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 "rails_helper" - -RSpec.describe CustomFields::CustomOptionsComponent, type: :component do - def render_component(...) - render_inline(described_class.new(...)) - end - - let(:custom_field) { create(:custom_field) } - let(:form) { instance_double(Primer::Forms::Builder, fields_for: "") } - - subject(:rendered_component) do - render_component(custom_field: custom_field, form:) - end - - before do - custom_field.reload - end - - context "with no custom options" do - let!(:custom_options) { create_list(:custom_option, 0, custom_field:) } - - it "renders table" do - expect(rendered_component).to have_element :table do |table| - expect(table["data-admin--custom-fields-target"]).to eq "table" - end - end - end - - context "with custom options" do - let!(:custom_options) { create_list(:custom_option, 2, custom_field:) } - - it "renders table" do - expect(rendered_component).to have_element :table do |table| - expect(table["data-admin--custom-fields-target"]).to eq "table" - end - end - end -end diff --git a/spec/lib/open_project/custom_field_format_dependent_spec.rb b/spec/lib/open_project/custom_field_format_dependent_spec.rb new file mode 100644 index 00000000000..79ff275d8a9 --- /dev/null +++ b/spec/lib/open_project/custom_field_format_dependent_spec.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require "spec_helper" + +RSpec.describe OpenProject::CustomFieldFormatDependent do + describe ".stimulus_config" do + it "returns a json with expected structure" do + expect(JSON.parse(described_class.stimulus_config)).to all match([be_a(String), be_a(String), all(be_a(String))]) + end + end + + describe "#attributes" do + let(:instance) { described_class.new(format) } + let(:format) { "string" } + + subject(:call) { instance.attributes(target_name) } + + context "for targets using operator only" do + let(:target_name) { :defaultLongText } + + context "for matching format" do + let(:format) { "text" } + + it { is_expected.to be_html_safe & eq('data-admin--custom-fields-target="defaultLongText"') } + end + + context "for non matching format" do + it { is_expected.to be_html_safe & eq('data-admin--custom-fields-target="defaultLongText" hidden="hidden"') } + end + end + + context "for targets using operator except" do + let(:target_name) { :defaultText } + + context "for matching format" do + let(:format) { "text" } + + it { is_expected.to be_html_safe & eq('data-admin--custom-fields-target="defaultText" hidden="hidden"') } + end + + context "for non matching format" do + it { is_expected.to be_html_safe & eq('data-admin--custom-fields-target="defaultText"') } + end + end + + context "for unknown target" do + let(:target_name) { :foo } + + it { expect { call }.to raise_error(ArgumentError) } + end + end +end From 72a274dbd5df8a0ef0df0bdcf83caff743050c46 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Thu, 19 Feb 2026 14:12:18 +0100 Subject: [PATCH 14/71] Restore the old customOptions table but within a separate "items" tab --- .../edit_form_header_component.rb | 8 +- .../custom_fields/details_component.rb | 14 +- .../edit_form_header_component.rb | 6 + .../project_custom_fields_controller.rb | 4 +- .../concerns/custom_fields/shared_actions.rb | 38 +++- app/controllers/custom_fields_controller.rb | 7 +- app/forms/custom_fields/details_form.rb | 38 ++-- app/models/custom_field.rb | 3 - .../project_custom_fields/list_items.html.erb | 76 +++++++ .../custom_fields/_custom_options.html.erb | 170 ++++++++++++++++ app/views/custom_fields/list_items.html.erb | 69 +++++++ config/locales/en.yml | 2 +- config/routes.rb | 4 + .../dynamic/admin/custom-fields.controller.ts | 186 ++++++++++++++++++ .../custom_fields/details_component_spec.rb | 138 ------------- 15 files changed, 587 insertions(+), 176 deletions(-) create mode 100644 app/views/admin/settings/project_custom_fields/list_items.html.erb create mode 100644 app/views/custom_fields/_custom_options.html.erb create mode 100644 app/views/custom_fields/list_items.html.erb create mode 100644 frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts delete mode 100644 spec/components/custom_fields/details_component_spec.rb 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 85a0319df35..46c9ac4fd79 100644 --- a/app/components/admin/custom_fields/edit_form_header_component.rb +++ b/app/components/admin/custom_fields/edit_form_header_component.rb @@ -46,12 +46,18 @@ module Admin } ] - if @custom_field.hierarchical_list? || @custom_field.list? + if @custom_field.hierarchical_list? tabs << { name: "items", 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 6d3e345a815..31b51f12b82 100644 --- a/app/components/custom_fields/details_component.rb +++ b/app/components/custom_fields/details_component.rb @@ -44,7 +44,11 @@ module CustomFields 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 @@ -65,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 @@ -74,12 +78,16 @@ 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? && custom_field.projects.empty? + return true + end + custom_field.persisted? && custom_field.hierarchical_list? && custom_field.hierarchy_root.children.empty? && 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/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..8cd591d82a4 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,18 @@ 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 + 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/forms/custom_fields/details_form.rb b/app/forms/custom_fields/details_form.rb index 4eaf8771b8b..57d4f747f26 100644 --- a/app/forms/custom_fields/details_form.rb +++ b/app/forms/custom_fields/details_form.rb @@ -61,23 +61,21 @@ module CustomFields end if show_min_max_field? - details_form.group(layout: :horizontal) do |g| - g.text_field( - name: :min_length, - type: :number, - label: label(:min_length), - caption: instructions(:min_max), - input_width: :xsmall - ) + details_form.text_field( + name: :min_length, + type: :number, + label: label(:min_length), + caption: instructions(:min_max), + input_width: :small + ) - g.text_field( - name: :max_length, - type: :number, - label: label(:max_length), - caption: instructions(:min_max), - input_width: :xsmall - ) - end + 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? @@ -220,7 +218,7 @@ module CustomFields end def show_default_text_field? - %w[list bool date text user version hierarchy calculated_value].exclude?(model.field_format) + %w[list bool date text user version hierarchy weighted_item_list calculated_value].exclude?(model.field_format) end def show_default_rich_text_field? @@ -232,11 +230,11 @@ module CustomFields end def show_min_max_field? - %w[list bool date user version link hierarchy calculated_value].exclude?(model.field_format) + %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 calculated_value].exclude?(model.field_format) + %w[list bool date user version hierarchy weighted_item_list calculated_value].exclude?(model.field_format) end def show_right_to_left_field? @@ -260,7 +258,7 @@ module CustomFields end def show_is_searchable_field? - %w[bool date float int user version hierarchy calculated_value].exclude?(model.field_format) + %w[bool date float int user version hierarchy weighted_item_list calculated_value].exclude?(model.field_format) end def show_non_open_versions_field? 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/list_items.html.erb b/app/views/admin/settings/project_custom_fields/list_items.html.erb new file mode 100644 index 00000000000..f0adfc60203 --- /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_submit) + end + end + end + %> +<% end %> diff --git a/app/views/custom_fields/_custom_options.html.erb b/app/views/custom_fields/_custom_options.html.erb new file mode 100644 index 00000000000..b122d871e5a --- /dev/null +++ b/app/views/custom_fields/_custom_options.html.erb @@ -0,0 +1,170 @@ +<%#-- 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. + +++#%> +<% content_controller "admin--custom-fields", + "admin--custom-fields-multi-select-value": @custom_field.multi_value? %> + +<% custom_field.custom_options.build if custom_field.custom_options.empty? %> + +
+
+ + + + + + + + + + + + + + + + + + <% custom_field.custom_options.each_with_index do |custom_option, i| %> + <%= f.fields_for :custom_options, custom_option do |co_f| %> + + + + + + + <% end %> + <% end %> + +
+
+
+ + <%= t("activerecord.attributes.custom_value.value") %> + +
+
+
+
+
+ + <%= t(:label_default) %> + +
+
+
+
+
+ + <%= t(:button_sort) %> + +
+
+
+
+
+
+ + <%= co_f.hidden_field :id, + class: "custom-option-id" %> + + <%= co_f.text_field :value, + no_label: true %> + + + <%= co_f.check_box :default_value, + container_class: "custom-option-default-value", + data: { + "admin--custom-fields-target": "customOptionDefaults", + action: "click->admin--custom-fields#uncheckOtherDefaults" + }, + no_label: true %> + + + + <%= op_icon("icon-context icon-sort-up icon-small") %> + + + <%= op_icon("icon-context icon-arrow-up2 icon-small") %> + + + <%= op_icon("icon-context icon-arrow-down2 icon-small") %> + + + <%= op_icon("icon-context icon-sort-down icon-small") %> + + + + <%= link_to "", + delete_option_of_custom_field_path(id: custom_field.id || 0, option_id: custom_option.id || 0), + data: { + turbo_method: :delete, + action: "admin--custom-fields#removeOption", + turbo_confirm: t(:"custom_fields.confirm_destroy_option") + }, + class: "icon icon-delete delete-custom-option", + title: t(:button_delete) %> +
+ +
+
+ +<%= + 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/list_items.html.erb b/app/views/custom_fields/list_items.html.erb new file mode 100644 index 00000000000..b284ab04ed0 --- /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_submit) + end + end + end + %> +<% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index a00652d50f1..bcaa3256caa 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -389,7 +389,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" diff --git a/config/routes.rb b/config/routes.rb index 5373a93f1f3..ef6e89695a5 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/frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts b/frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts new file mode 100644 index 00000000000..e9039f7336f --- /dev/null +++ b/frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts @@ -0,0 +1,186 @@ +/* + * -- copyright + * OpenProject is an open source project management software. + * Copyright (C) the OpenProject GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 3. + * + * OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: + * Copyright (C) 2006-2013 Jean-Philippe Lang + * Copyright (C) 2010-2013 the ChiliProject Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * See COPYRIGHT and LICENSE files for more details. + * ++ + */ + +import { Controller } from '@hotwired/stimulus'; +import dragula from 'dragula'; + +export default class CustomFieldsController extends Controller { + static targets = [ + 'dragContainer', + + 'customOptionDefaults', + 'customOptionRow', + ]; + + static values = { + multiSelect: Boolean, + }; + + declare readonly multiSelectValue:boolean; + + declare readonly dragContainerTarget:HTMLElement; + declare readonly hasDragContainerTarget:boolean; + + declare readonly customOptionDefaultsTargets:HTMLInputElement[]; + declare readonly customOptionRowTargets:HTMLTableRowElement[]; + + connect() { + if (this.hasDragContainerTarget) { + this.setupDragAndDrop(); + } + } + + moveRowUp(event:{ target:HTMLElement }) { + const row = event.target.closest('tr')!; + const idx = this.customOptionRowTargets.indexOf(row); + if (idx > 0) { + this.customOptionRowTargets[idx - 1].before(row); + } + + return false; + } + + moveRowDown(event:{ target:HTMLElement }) { + const row = event.target.closest('tr')!; + const idx = this.customOptionRowTargets.indexOf(row); + if (idx < this.customOptionRowTargets.length - 1) { + this.customOptionRowTargets[idx + 1].after(row); + } + + return false; + } + + moveRowToTheTop(event:{ target:HTMLElement }) { + const row = event.target.closest('tr')!; + const first = this.customOptionRowTargets[0]; + + if (first && first !== row) { + first.before(row); + } + + return false; + } + + moveRowToTheBottom(event:{ target:HTMLElement }) { + const row = event.target.closest('tr')!; + const last = this.customOptionRowTargets[this.customOptionRowTargets.length - 1]; + + if (last && last !== row) { + last.after(row); + } + + return false; + } + + removeOption(event:MouseEvent) { + const self = event.target as HTMLAnchorElement; + if (self.href === '#' || self.href.endsWith('/0')) { + const row = self.closest('tr'); + + if (row && this.customOptionRowTargets.length > 1) { + row.remove(); + } + + event.preventDefault(); + event.stopImmediatePropagation(); + } + return true; // send off deletion + } + + addOption() { + const count = this.customOptionRowTargets.length; + const last = this.customOptionRowTargets[count - 1]; + const dup = last.cloneNode(true) as HTMLElement; + + const input = dup.querySelector('.custom-option-value input') as HTMLInputElement; + + input.setAttribute('name', `custom_field[custom_options_attributes][${count}][value]`); + input.setAttribute('id', `custom_field_custom_options_attributes_${count}_value`); + input.value = ''; + + dup + .querySelector('.custom-option-id') + ?.remove(); + + const defaultValueCheckbox = dup.querySelector('input[type="checkbox"]') as HTMLInputElement; + const defaultValueHidden = dup.querySelector('input[type="hidden"]') as HTMLInputElement; + + defaultValueHidden.setAttribute('name', `custom_field[custom_options_attributes][${count}][default_value]`); + defaultValueHidden.removeAttribute('id'); + defaultValueCheckbox.setAttribute('name', `custom_field[custom_options_attributes][${count}][default_value]`); + defaultValueCheckbox.setAttribute('id', `custom_field_custom_options_attributes_${count}_default_value`); + defaultValueCheckbox.checked = false; + + last.insertAdjacentElement('afterend', dup); + + return false; + } + + uncheckOtherDefaults(event:{ target:HTMLElement }) { + const cb = event.target as HTMLInputElement; + + if (cb.checked && !this.multiSelectValue) { + this.customOptionDefaultsTargets.forEach((el) => (el.checked = false)); + cb.checked = true; + } + } + + private setupDragAndDrop() { + // Make custom fields draggable + const drake = dragula([this.dragContainerTarget], { + isContainer: () => false, + moves: (el, source, handle:HTMLElement) => handle.classList.contains('dragula-handle'), + accepts: () => true, + invalid: () => false, + direction: 'vertical', + copy: false, + copySortSource: false, + revertOnSpill: true, + removeOnSpill: false, + mirrorContainer: this.dragContainerTarget, + ignoreInputTextSelection: true, + }); + + // Setup autoscroll + void window.OpenProject.getPluginContext().then((pluginContext) => { + new pluginContext.classes.DomAutoscrollService( + [ + document.getElementById('content-body')!, + ], + { + margin: 25, + maxSpeed: 10, + scrollWhenOutside: true, + autoScroll: () => drake.dragging, + }, + ); + }); + } +} 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 From 4edda42f5a3da9598fadcdf5963188e2e87cc7a2 Mon Sep 17 00:00:00 2001 From: ulferts Date: Thu, 19 Feb 2026 17:40:18 +0100 Subject: [PATCH 15/71] turn sharing into an enum --- modules/backlogs/app/models/agile/sprint.rb | 24 +++++++++++++------ .../backlogs/spec/models/agile/sprint_spec.rb | 11 ++++----- 2 files changed, 22 insertions(+), 13 deletions(-) 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/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 From 9214e06f01652b567a88014b5e3be892fd1fbc42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 20:36:41 +0000 Subject: [PATCH 16/71] Bump hono from 4.11.9 to 4.12.0 in /frontend Bumps [hono](https://github.com/honojs/hono) from 4.11.9 to 4.12.0. - [Release notes](https://github.com/honojs/hono/releases) - [Commits](https://github.com/honojs/hono/compare/v4.11.9...v4.12.0) --- updated-dependencies: - dependency-name: hono dependency-version: 4.12.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ab2ef48df52..9c04dd5c5ff 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15614,9 +15614,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" } @@ -35693,9 +35693,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", From 0ecfcb46302864d0628611ea0cf6b6b059b63db0 Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Wed, 11 Feb 2026 18:44:15 +0100 Subject: [PATCH 17/71] Add initial meeting template scaffolding and services --- .../blank_slate_component.html.erb | 36 +++++ .../blank_slate_component.rb | 34 +++++ .../index/dialog_component.html.erb | 70 ++++++++++ .../index/dialog_component.rb | 55 ++++++++ .../index/form_component.html.erb | 57 ++++++++ .../meeting_templates/index/form_component.rb | 57 ++++++++ .../index_page_header_component.html.erb | 35 +++++ .../index_page_header_component.rb | 49 +++++++ .../index_sub_header_component.html.erb | 47 +++++++ .../index_sub_header_component.rb | 65 +++++++++ .../meeting_templates/row_component.rb | 97 ++++++++++++++ .../meeting_templates/table_component.rb | 72 ++++++++++ .../meeting_templates/base_contract.rb | 65 +++++++++ .../meeting_templates/create_contract.rb | 47 +++++++ .../meeting_templates_controller.rb | 123 ++++++++++++++++++ modules/meeting/app/menus/meetings/menu.rb | 18 ++- modules/meeting/app/models/meeting.rb | 32 ++++- .../meeting_templates/create_service.rb | 37 ++++++ .../set_attributes_service.rb | 44 +++++++ .../views/meeting_templates/index.html.erb | 38 ++++++ modules/meeting/config/locales/en.yml | 6 + modules/meeting/config/routes.rb | 7 + .../lib/open_project/meeting/engine.rb | 6 +- 23 files changed, 1092 insertions(+), 5 deletions(-) create mode 100644 modules/meeting/app/components/meeting_templates/blank_slate_component.html.erb create mode 100644 modules/meeting/app/components/meeting_templates/blank_slate_component.rb create mode 100644 modules/meeting/app/components/meeting_templates/index/dialog_component.html.erb create mode 100644 modules/meeting/app/components/meeting_templates/index/dialog_component.rb create mode 100644 modules/meeting/app/components/meeting_templates/index/form_component.html.erb create mode 100644 modules/meeting/app/components/meeting_templates/index/form_component.rb create mode 100644 modules/meeting/app/components/meeting_templates/index_page_header_component.html.erb create mode 100644 modules/meeting/app/components/meeting_templates/index_page_header_component.rb create mode 100644 modules/meeting/app/components/meeting_templates/index_sub_header_component.html.erb create mode 100644 modules/meeting/app/components/meeting_templates/index_sub_header_component.rb create mode 100644 modules/meeting/app/components/meeting_templates/row_component.rb create mode 100644 modules/meeting/app/components/meeting_templates/table_component.rb create mode 100644 modules/meeting/app/contracts/meeting_templates/base_contract.rb create mode 100644 modules/meeting/app/contracts/meeting_templates/create_contract.rb create mode 100644 modules/meeting/app/controllers/meeting_templates_controller.rb create mode 100644 modules/meeting/app/services/meeting_templates/create_service.rb create mode 100644 modules/meeting/app/services/meeting_templates/set_attributes_service.rb create mode 100644 modules/meeting/app/views/meeting_templates/index.html.erb diff --git a/modules/meeting/app/components/meeting_templates/blank_slate_component.html.erb b/modules/meeting/app/components/meeting_templates/blank_slate_component.html.erb new file mode 100644 index 00000000000..45bf386ddc7 --- /dev/null +++ b/modules/meeting/app/components/meeting_templates/blank_slate_component.html.erb @@ -0,0 +1,36 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + +<%= + render(Primer::Beta::Blankslate.new(border: true)) do |component| + component.with_visual_icon(icon: :book) + component.with_heading(tag: :h2).with_content(I18n.t("text_meeting_template_blank_slate_heading")) + component.with_description { I18n.t("text_meeting_template_blank_slate") } + end +%> diff --git a/modules/meeting/app/components/meeting_templates/blank_slate_component.rb b/modules/meeting/app/components/meeting_templates/blank_slate_component.rb new file mode 100644 index 00000000000..73d8fa48ed8 --- /dev/null +++ b/modules/meeting/app/components/meeting_templates/blank_slate_component.rb @@ -0,0 +1,34 @@ +# 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 BlankSlateComponent < ApplicationComponent + end +end diff --git a/modules/meeting/app/components/meeting_templates/index/dialog_component.html.erb b/modules/meeting/app/components/meeting_templates/index/dialog_component.html.erb new file mode 100644 index 00000000000..e2c7905f78b --- /dev/null +++ b/modules/meeting/app/components/meeting_templates/index/dialog_component.html.erb @@ -0,0 +1,70 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + +<%= + render( + Primer::Alpha::Dialog.new( + id: "new-meeting-template-dialog", + title:, + size: :medium_portrait + ) + ) do |dialog| + dialog.with_header(variant: :large) + dialog.with_body do + render( + MeetingTemplates::Index::FormComponent.new( + template: @template, + project: @project + ) + ) + end + + dialog.with_footer do + component_collection do |modal_footer| + modal_footer.with_component( + Primer::Beta::Button.new( + data: { "close-dialog-id": "new-meeting-template-dialog" } + ) + ) do + I18n.t(:button_cancel) + end + + modal_footer.with_component( + Primer::Beta::Button.new( + scheme: :primary, + form: "meeting-template-form", + type: :submit + ) + ) do + I18n.t(:label_meeting_template_create) + end + end + end + end +%> diff --git a/modules/meeting/app/components/meeting_templates/index/dialog_component.rb b/modules/meeting/app/components/meeting_templates/index/dialog_component.rb new file mode 100644 index 00000000000..68470c6756e --- /dev/null +++ b/modules/meeting/app/components/meeting_templates/index/dialog_component.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module MeetingTemplates + class Index::DialogComponent < ApplicationComponent + include ApplicationHelper + include OpenProject::FormTagHelper + include OpTurbo::Streamable + include OpPrimer::ComponentHelpers + + def initialize(template:, project:) + super + + @template = template + @project = project + end + + private + + def render? + @project.present? && User.current.allowed_in_project?(:create_meetings, @project) + end + + def title + I18n.t(:label_meeting_template_new) + end + end +end diff --git a/modules/meeting/app/components/meeting_templates/index/form_component.html.erb b/modules/meeting/app/components/meeting_templates/index/form_component.html.erb new file mode 100644 index 00000000000..3115741a3c9 --- /dev/null +++ b/modules/meeting/app/components/meeting_templates/index/form_component.html.erb @@ -0,0 +1,57 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + +<%= + component_wrapper do + primer_form_with( + scope: :meeting, + model: @template, + method: form_method, + data: { turbo: true }, + html: { id: "meeting-template-form" }, + url: { + controller: "meeting_templates", + action: form_action, + project_id: @project + } + ) do |f| + flex_layout(mb: 3) do |modal_body| + if @template.errors[:base].present? + modal_body.with_row do + render(Primer::Alpha::Banner.new(mb: 3, icon: :stop, scheme: :danger)) { @template.errors[:base].join("\n") } + end + end + + modal_body.with_row do + render(Meeting::Title.new(f)) + end + end + end + end +%> diff --git a/modules/meeting/app/components/meeting_templates/index/form_component.rb b/modules/meeting/app/components/meeting_templates/index/form_component.rb new file mode 100644 index 00000000000..0fb57e51b1e --- /dev/null +++ b/modules/meeting/app/components/meeting_templates/index/form_component.rb @@ -0,0 +1,57 @@ +# 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 + module Index + class FormComponent < ApplicationComponent + include OpTurbo::Streamable + include OpPrimer::ComponentHelpers + + def initialize(template:, project: nil) + super + + @template = template + @project = project + end + + def form_url + create_template_project_meetings_path(@project) + end + + def form_method + :post + end + + def form_action + :create + end + end + end +end diff --git a/modules/meeting/app/components/meeting_templates/index_page_header_component.html.erb b/modules/meeting/app/components/meeting_templates/index_page_header_component.html.erb new file mode 100644 index 00000000000..04f44b82a0b --- /dev/null +++ b/modules/meeting/app/components/meeting_templates/index_page_header_component.html.erb @@ -0,0 +1,35 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + +<%= + render(Primer::OpenProject::PageHeader.new) do |header| + header.with_title { I18n.t(:label_meeting_templates) } + header.with_breadcrumbs(breadcrumb_items) + end +%> 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..b7a84d7af46 --- /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: { controller: "async-dialog" } + ) 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..e58952f051c --- /dev/null +++ b/modules/meeting/app/components/meeting_templates/index_sub_header_component.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module MeetingTemplates + class IndexSubHeaderComponent < ApplicationComponent + include ApplicationHelper + + def initialize(project: nil) + super + @project = project + end + + def render_create_button? + # TODO + return false unless @project + + User.current.allowed_in_project?(:create_meetings, @project) + end + + def create_path + return nil unless @project + + new_dialog_template_project_meetings_path(@project) + 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..4f851ae535f --- /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(:button_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(:button_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?(:edit_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/contracts/meeting_templates/base_contract.rb b/modules/meeting/app/contracts/meeting_templates/base_contract.rb new file mode 100644 index 00000000000..7d5082f01a4 --- /dev/null +++ b/modules/meeting/app/contracts/meeting_templates/base_contract.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 MeetingTemplates + class BaseContract < ::ModelContract + def self.model + Meeting + end + + attribute :title + attribute :author_id + attribute :project_id + attribute :duration + attribute :state + attribute :notify + attribute :template + attribute :recurring_meeting_id + + validate :must_be_standalone_template + validate :title_present + + private + + def must_be_standalone_template + unless model.template == true + errors.add(:base, :error_unauthorized) + end + + unless model.recurring_meeting_id.nil? + errors.add(:base, :error_unauthorized) + end + end + + def title_present + errors.add(:title, :blank) if model.title.blank? + end + end +end diff --git a/modules/meeting/app/contracts/meeting_templates/create_contract.rb b/modules/meeting/app/contracts/meeting_templates/create_contract.rb new file mode 100644 index 00000000000..804695525ee --- /dev/null +++ b/modules/meeting/app/contracts/meeting_templates/create_contract.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module MeetingTemplates + class CreateContract < BaseContract + attribute :uid + + validate :user_allowed_to_create + + private + + def user_allowed_to_create + return if model.project.nil? + + unless user.allowed_in_project?(:create_meetings, model.project) + errors.add :base, :error_unauthorized + end + end + end +end 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..47d31ff0b67 --- /dev/null +++ b/modules/meeting/app/controllers/meeting_templates_controller.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +class MeetingTemplatesController < ApplicationController + before_action :load_and_authorize_in_optional_project + before_action :require_project, except: %i[index] + + include Layout + include OpTurbo::ComponentStream + include OpTurbo::FlashStreamHelper + + menu_item :meetings + + def index + @templates = Meeting.standalone_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 MeetingTemplates::Index::DialogComponent.new( + template: @template, + project: @project + ) + end + + def create + call = ::MeetingTemplates::CreateService + .new(user: current_user) + .call(template_params) + + @template = call.result + + if call.success? + flash[:notice] = I18n.t(:notice_meeting_template_created) + redirect_to project_meeting_path(@project, @template), status: :see_other + else + update_via_turbo_stream( + component: MeetingTemplates::Index::FormComponent.new( + template: @template, + project: @project + ), + status: :bad_request + ) + + respond_with_turbo_streams + end + end + + # TODO + # def show + # end + # + # def edit + # end + # + # def update + # end + # + # def update_title + # end + # + # def delete_dialog + # end + # + # def destroy + # end + + private + + def require_project + render_404 unless @project + end + + def template_params + params + .expect(meeting: [:title]) + .merge( + template: true, + recurring_meeting_id: nil, + project_id: @project.id + ) + 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..175808318b4 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 :standalone_templates, -> { where(template: true, recurring_meeting_id: nil) } + scope :recurring_templates, -> { where(template: true).where.not(recurring_meeting_id: nil) } scope :not_cancelled, -> { where.not.cancelled } @@ -116,6 +118,7 @@ class Meeting < ApplicationRecord accepts_nested_attributes_for :participants, allow_destroy: true validates :title, :project_id, presence: true + validates :start_time, presence: { unless: :standalone_template? } validates :duration, numericality: { greater_than: 0 } @@ -157,14 +160,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 +181,14 @@ class Meeting < ApplicationRecord !!template end + def standalone_template? + template? && recurring_meeting_id.nil? + end + + def recurring_template? + template? && recurring_meeting_id.present? + end + # One-time meeting time zone # is always in the user's time zone def time_zone @@ -192,6 +205,8 @@ class Meeting < ApplicationRecord end def notify? + return false if standalone_template? + if recurring? recurring_meeting.template.notify else @@ -258,11 +273,24 @@ class Meeting < ApplicationRecord end def send_emails? + return false if standalone_template? return false if template? && recurring_meeting.scheduled_meetings.none? persisted? && notify? end + def set_initial_values + return if template == true && recurring_meeting_id.nil? + + super + end + + def validate_date_and_time + return if standalone_template? + + super + end + private def add_new_participants_as_watcher diff --git a/modules/meeting/app/services/meeting_templates/create_service.rb b/modules/meeting/app/services/meeting_templates/create_service.rb new file mode 100644 index 00000000000..a7b77555e3c --- /dev/null +++ b/modules/meeting/app/services/meeting_templates/create_service.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module MeetingTemplates + class CreateService < ::BaseServices::Create + def instance_class + Meeting + end + end +end diff --git a/modules/meeting/app/services/meeting_templates/set_attributes_service.rb b/modules/meeting/app/services/meeting_templates/set_attributes_service.rb new file mode 100644 index 00000000000..c78c9676ec8 --- /dev/null +++ b/modules/meeting/app/services/meeting_templates/set_attributes_service.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module MeetingTemplates + class SetAttributesService < ::BaseServices::SetAttributes + # TODO Necessary? + def set_default_attributes(_params) + model.change_by_system do + model.author = user + model.duration ||= 1 + model.state = "draft" + model.notify = false + model.start_time = nil + end + end + end +end 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..3e7f1b72993 --- /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(MeetingTemplates::BlankSlateComponent.new) %> +<% end %> diff --git a/modules/meeting/config/locales/en.yml b/modules/meeting/config/locales/en.yml index 28c115ec33a..eb6f13a7b20 100644 --- a/modules/meeting/config/locales/en.yml +++ b/modules/meeting/config/locales/en.yml @@ -130,6 +130,9 @@ en: label_meeting: "Meeting" label_meeting_plural: "Meetings" + label_meeting_templates: "Templates" + label_meeting_template_new: "New template" + label_meeting_template_create: "Create template" label_meeting_new: "New Meeting" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" @@ -522,6 +525,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." @@ -539,6 +543,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?" diff --git a/modules/meeting/config/routes.rb b/modules/meeting/config/routes.rb index 5876e00cc76..b2adb5163a8 100644 --- a/modules/meeting/config/routes.rb +++ b/modules/meeting/config/routes.rb @@ -44,6 +44,8 @@ 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" end end @@ -54,6 +56,11 @@ 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", action: :new, controller: "meeting_templates", as: "new_template" + 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, From bbaa7b1dc3f4848c82f2f0c659be74e1ffebc22e Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Thu, 12 Feb 2026 11:45:28 +0100 Subject: [PATCH 18/71] Remove separate contracts and services --- .../meeting_templates/base_contract.rb | 65 ------------------- .../meeting_templates/create_contract.rb | 47 -------------- .../app/contracts/meetings/base_contract.rb | 10 --- .../meeting_templates_controller.rb | 2 +- .../meeting_templates/create_service.rb | 37 ----------- .../set_attributes_service.rb | 44 ------------- .../app/services/meetings/create_service.rb | 3 + 7 files changed, 4 insertions(+), 204 deletions(-) delete mode 100644 modules/meeting/app/contracts/meeting_templates/base_contract.rb delete mode 100644 modules/meeting/app/contracts/meeting_templates/create_contract.rb delete mode 100644 modules/meeting/app/services/meeting_templates/create_service.rb delete mode 100644 modules/meeting/app/services/meeting_templates/set_attributes_service.rb diff --git a/modules/meeting/app/contracts/meeting_templates/base_contract.rb b/modules/meeting/app/contracts/meeting_templates/base_contract.rb deleted file mode 100644 index 7d5082f01a4..00000000000 --- a/modules/meeting/app/contracts/meeting_templates/base_contract.rb +++ /dev/null @@ -1,65 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module MeetingTemplates - class BaseContract < ::ModelContract - def self.model - Meeting - end - - attribute :title - attribute :author_id - attribute :project_id - attribute :duration - attribute :state - attribute :notify - attribute :template - attribute :recurring_meeting_id - - validate :must_be_standalone_template - validate :title_present - - private - - def must_be_standalone_template - unless model.template == true - errors.add(:base, :error_unauthorized) - end - - unless model.recurring_meeting_id.nil? - errors.add(:base, :error_unauthorized) - end - end - - def title_present - errors.add(:title, :blank) if model.title.blank? - end - end -end diff --git a/modules/meeting/app/contracts/meeting_templates/create_contract.rb b/modules/meeting/app/contracts/meeting_templates/create_contract.rb deleted file mode 100644 index 804695525ee..00000000000 --- a/modules/meeting/app/contracts/meeting_templates/create_contract.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module MeetingTemplates - class CreateContract < BaseContract - attribute :uid - - validate :user_allowed_to_create - - private - - def user_allowed_to_create - return if model.project.nil? - - unless user.allowed_in_project?(:create_meetings, model.project) - errors.add :base, :error_unauthorized - end - end - end -end 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/meeting_templates_controller.rb b/modules/meeting/app/controllers/meeting_templates_controller.rb index 47d31ff0b67..57b87bec225 100644 --- a/modules/meeting/app/controllers/meeting_templates_controller.rb +++ b/modules/meeting/app/controllers/meeting_templates_controller.rb @@ -64,7 +64,7 @@ class MeetingTemplatesController < ApplicationController end def create - call = ::MeetingTemplates::CreateService + call = ::Meetings::CreateService .new(user: current_user) .call(template_params) diff --git a/modules/meeting/app/services/meeting_templates/create_service.rb b/modules/meeting/app/services/meeting_templates/create_service.rb deleted file mode 100644 index a7b77555e3c..00000000000 --- a/modules/meeting/app/services/meeting_templates/create_service.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module MeetingTemplates - class CreateService < ::BaseServices::Create - def instance_class - Meeting - end - end -end diff --git a/modules/meeting/app/services/meeting_templates/set_attributes_service.rb b/modules/meeting/app/services/meeting_templates/set_attributes_service.rb deleted file mode 100644 index c78c9676ec8..00000000000 --- a/modules/meeting/app/services/meeting_templates/set_attributes_service.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module MeetingTemplates - class SetAttributesService < ::BaseServices::SetAttributes - # TODO Necessary? - def set_default_attributes(_params) - model.change_by_system do - model.author = user - model.duration ||= 1 - model.state = "draft" - model.notify = false - model.start_time = nil - end - end - end -end diff --git a/modules/meeting/app/services/meetings/create_service.rb b/modules/meeting/app/services/meetings/create_service.rb index 72044a0f2fc..b7887b7263e 100644 --- a/modules/meeting/app/services/meetings/create_service.rb +++ b/modules/meeting/app/services/meetings/create_service.rb @@ -35,6 +35,9 @@ module Meetings def after_perform(call) meeting = call.result + # Skip post creation steps for one-time templates + return call if meeting.template? && meeting.recurring_meeting_id.nil? + if call.success? && Journal::NotificationConfiguration.active? && meeting.send_emails? meeting.participants.where(invited: true).find_each do |participant| MeetingMailer From 59c739ba5c707dbbde05d9602692522ba428b718 Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Thu, 12 Feb 2026 12:04:47 +0100 Subject: [PATCH 19/71] Consolidate some view components --- .../blank_slate_component.html.erb | 36 ---------- .../blank_slate_component.rb | 34 --------- .../index/dialog_component.html.erb | 70 ------------------- .../index/dialog_component.rb | 55 --------------- .../index/form_component.html.erb | 57 --------------- .../meeting_templates/index/form_component.rb | 57 --------------- .../meetings/blank_slate_component.html.erb | 4 +- .../meetings/blank_slate_component.rb | 11 ++- .../meetings/index/dialog_component.html.erb | 11 +-- .../meetings/index/dialog_component.rb | 12 +++- .../meetings/index/form_component.html.erb | 18 ++--- .../meetings/index/form_component.rb | 5 +- .../meeting_templates_controller.rb | 14 ++-- .../views/meeting_templates/index.html.erb | 2 +- 14 files changed, 49 insertions(+), 337 deletions(-) delete mode 100644 modules/meeting/app/components/meeting_templates/blank_slate_component.html.erb delete mode 100644 modules/meeting/app/components/meeting_templates/blank_slate_component.rb delete mode 100644 modules/meeting/app/components/meeting_templates/index/dialog_component.html.erb delete mode 100644 modules/meeting/app/components/meeting_templates/index/dialog_component.rb delete mode 100644 modules/meeting/app/components/meeting_templates/index/form_component.html.erb delete mode 100644 modules/meeting/app/components/meeting_templates/index/form_component.rb diff --git a/modules/meeting/app/components/meeting_templates/blank_slate_component.html.erb b/modules/meeting/app/components/meeting_templates/blank_slate_component.html.erb deleted file mode 100644 index 45bf386ddc7..00000000000 --- a/modules/meeting/app/components/meeting_templates/blank_slate_component.html.erb +++ /dev/null @@ -1,36 +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. - -++#%> - -<%= - render(Primer::Beta::Blankslate.new(border: true)) do |component| - component.with_visual_icon(icon: :book) - component.with_heading(tag: :h2).with_content(I18n.t("text_meeting_template_blank_slate_heading")) - component.with_description { I18n.t("text_meeting_template_blank_slate") } - end -%> diff --git a/modules/meeting/app/components/meeting_templates/blank_slate_component.rb b/modules/meeting/app/components/meeting_templates/blank_slate_component.rb deleted file mode 100644 index 73d8fa48ed8..00000000000 --- a/modules/meeting/app/components/meeting_templates/blank_slate_component.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module MeetingTemplates - class BlankSlateComponent < ApplicationComponent - end -end diff --git a/modules/meeting/app/components/meeting_templates/index/dialog_component.html.erb b/modules/meeting/app/components/meeting_templates/index/dialog_component.html.erb deleted file mode 100644 index e2c7905f78b..00000000000 --- a/modules/meeting/app/components/meeting_templates/index/dialog_component.html.erb +++ /dev/null @@ -1,70 +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. - -++#%> - -<%= - render( - Primer::Alpha::Dialog.new( - id: "new-meeting-template-dialog", - title:, - size: :medium_portrait - ) - ) do |dialog| - dialog.with_header(variant: :large) - dialog.with_body do - render( - MeetingTemplates::Index::FormComponent.new( - template: @template, - project: @project - ) - ) - end - - dialog.with_footer do - component_collection do |modal_footer| - modal_footer.with_component( - Primer::Beta::Button.new( - data: { "close-dialog-id": "new-meeting-template-dialog" } - ) - ) do - I18n.t(:button_cancel) - end - - modal_footer.with_component( - Primer::Beta::Button.new( - scheme: :primary, - form: "meeting-template-form", - type: :submit - ) - ) do - I18n.t(:label_meeting_template_create) - end - end - end - end -%> diff --git a/modules/meeting/app/components/meeting_templates/index/dialog_component.rb b/modules/meeting/app/components/meeting_templates/index/dialog_component.rb deleted file mode 100644 index 68470c6756e..00000000000 --- a/modules/meeting/app/components/meeting_templates/index/dialog_component.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module MeetingTemplates - class Index::DialogComponent < ApplicationComponent - include ApplicationHelper - include OpenProject::FormTagHelper - include OpTurbo::Streamable - include OpPrimer::ComponentHelpers - - def initialize(template:, project:) - super - - @template = template - @project = project - end - - private - - def render? - @project.present? && User.current.allowed_in_project?(:create_meetings, @project) - end - - def title - I18n.t(:label_meeting_template_new) - end - end -end diff --git a/modules/meeting/app/components/meeting_templates/index/form_component.html.erb b/modules/meeting/app/components/meeting_templates/index/form_component.html.erb deleted file mode 100644 index 3115741a3c9..00000000000 --- a/modules/meeting/app/components/meeting_templates/index/form_component.html.erb +++ /dev/null @@ -1,57 +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. - -++#%> - -<%= - component_wrapper do - primer_form_with( - scope: :meeting, - model: @template, - method: form_method, - data: { turbo: true }, - html: { id: "meeting-template-form" }, - url: { - controller: "meeting_templates", - action: form_action, - project_id: @project - } - ) do |f| - flex_layout(mb: 3) do |modal_body| - if @template.errors[:base].present? - modal_body.with_row do - render(Primer::Alpha::Banner.new(mb: 3, icon: :stop, scheme: :danger)) { @template.errors[:base].join("\n") } - end - end - - modal_body.with_row do - render(Meeting::Title.new(f)) - end - end - end - end -%> diff --git a/modules/meeting/app/components/meeting_templates/index/form_component.rb b/modules/meeting/app/components/meeting_templates/index/form_component.rb deleted file mode 100644 index 0fb57e51b1e..00000000000 --- a/modules/meeting/app/components/meeting_templates/index/form_component.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module MeetingTemplates - module Index - class FormComponent < ApplicationComponent - include OpTurbo::Streamable - include OpPrimer::ComponentHelpers - - def initialize(template:, project: nil) - super - - @template = template - @project = project - end - - def form_url - create_template_project_meetings_path(@project) - end - - def form_method - :post - end - - def form_action - :create - end - 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/index/dialog_component.html.erb b/modules/meeting/app/components/meetings/index/dialog_component.html.erb index 703820c53c2..47354e9fc8a 100644 --- a/modules/meeting/app/components/meetings/index/dialog_component.html.erb +++ b/modules/meeting/app/components/meetings/index/dialog_component.html.erb @@ -13,7 +13,8 @@ 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..9e05354ecb4 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,6 +51,7 @@ module Meetings end def title + return I18n.t(:label_meeting_template_new) if @template return I18n.t(:label_meeting_duplicate) if @copy_from return I18n.t(:label_meeting_edit) if @meeting.persisted? @@ -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..ec6de3326f3 100644 --- a/modules/meeting/app/components/meetings/index/form_component.html.erb +++ b/modules/meeting/app/components/meetings/index/form_component.html.erb @@ -39,15 +39,17 @@ render(Meeting::Title.new(f)) end - modal_body.with_row(mt: 3) do - render(Meeting::Location.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 - modal_body.with_row(mt: 3) do - render(Meeting::TimeGroup.new(f, meeting: @meeting)) - 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 @@ -134,7 +136,7 @@ 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..e086ea53060 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 diff --git a/modules/meeting/app/controllers/meeting_templates_controller.rb b/modules/meeting/app/controllers/meeting_templates_controller.rb index 57b87bec225..983fa00f89a 100644 --- a/modules/meeting/app/controllers/meeting_templates_controller.rb +++ b/modules/meeting/app/controllers/meeting_templates_controller.rb @@ -57,9 +57,10 @@ class MeetingTemplatesController < ApplicationController recurring_meeting_id: nil ) - respond_with_dialog MeetingTemplates::Index::DialogComponent.new( - template: @template, - project: @project + respond_with_dialog Meetings::Index::DialogComponent.new( + meeting: @template, + project: @project, + template: true ) end @@ -75,9 +76,10 @@ class MeetingTemplatesController < ApplicationController redirect_to project_meeting_path(@project, @template), status: :see_other else update_via_turbo_stream( - component: MeetingTemplates::Index::FormComponent.new( - template: @template, - project: @project + component: Meetings::Index::FormComponent.new( + meeting: @template, + project: @project, + template: true ), status: :bad_request ) diff --git a/modules/meeting/app/views/meeting_templates/index.html.erb b/modules/meeting/app/views/meeting_templates/index.html.erb index 3e7f1b72993..4ccd8bf058d 100644 --- a/modules/meeting/app/views/meeting_templates/index.html.erb +++ b/modules/meeting/app/views/meeting_templates/index.html.erb @@ -34,5 +34,5 @@ See COPYRIGHT and LICENSE files for more details. <% if @templates.any? %> <%= render(MeetingTemplates::TableComponent.new(rows: @templates, current_project: @project)) %> <% else %> - <%= render(MeetingTemplates::BlankSlateComponent.new) %> + <%= render(Meetings::BlankSlateComponent.new(project: @project, template: true)) %> <% end %> From 4d8f7e3fc9666a460c45a830171e23fe52e64165 Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Thu, 12 Feb 2026 12:38:22 +0100 Subject: [PATCH 20/71] Add logic to handle global template creation --- .../index_sub_header_component.rb | 17 +++++++++------- .../meeting_templates_controller.rb | 20 +++++++++---------- modules/meeting/config/routes.rb | 3 ++- 3 files changed, 22 insertions(+), 18 deletions(-) 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 index e58952f051c..8f18a959d57 100644 --- a/modules/meeting/app/components/meeting_templates/index_sub_header_component.rb +++ b/modules/meeting/app/components/meeting_templates/index_sub_header_component.rb @@ -38,16 +38,19 @@ module MeetingTemplates end def render_create_button? - # TODO - return false unless @project - - User.current.allowed_in_project?(:create_meetings, @project) + if @project + User.current.allowed_in_project?(:create_meetings, @project) + else + User.current.allowed_in_any_project?(:create_meetings) + end end def create_path - return nil unless @project - - new_dialog_template_project_meetings_path(@project) + if @project + new_dialog_template_project_meetings_path(@project) + else + new_dialog_template_meetings_path + end end def id diff --git a/modules/meeting/app/controllers/meeting_templates_controller.rb b/modules/meeting/app/controllers/meeting_templates_controller.rb index 983fa00f89a..008046658de 100644 --- a/modules/meeting/app/controllers/meeting_templates_controller.rb +++ b/modules/meeting/app/controllers/meeting_templates_controller.rb @@ -30,7 +30,6 @@ class MeetingTemplatesController < ApplicationController before_action :load_and_authorize_in_optional_project - before_action :require_project, except: %i[index] include Layout include OpTurbo::ComponentStream @@ -73,12 +72,12 @@ class MeetingTemplatesController < ApplicationController if call.success? flash[:notice] = I18n.t(:notice_meeting_template_created) - redirect_to project_meeting_path(@project, @template), status: :see_other + redirect_to controller: "/meetings", action: "show", id: @template, status: :see_other else update_via_turbo_stream( component: Meetings::Index::FormComponent.new( meeting: @template, - project: @project, + project: @template.project, template: true ), status: :bad_request @@ -114,12 +113,13 @@ class MeetingTemplatesController < ApplicationController end def template_params - params - .expect(meeting: [:title]) - .merge( - template: true, - recurring_meeting_id: nil, - project_id: @project.id - ) + permitted = params.expect(meeting: %i[title project_id]) + + permitted.merge( + template: true, + recurring_meeting_id: nil + ).tap do |p| + p[:project_id] = @project.id if @project.present? + end end end diff --git a/modules/meeting/config/routes.rb b/modules/meeting/config/routes.rb index b2adb5163a8..6679305e5f2 100644 --- a/modules/meeting/config/routes.rb +++ b/modules/meeting/config/routes.rb @@ -46,6 +46,8 @@ Rails.application.routes.draw do 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 @@ -58,7 +60,6 @@ Rails.application.routes.draw do get :fetch_timezone get "templates", action: :index, controller: "meeting_templates", as: "templates" - get "templates/new", action: :new, controller: "meeting_templates", as: "new_template" 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 From ffd7a563222769e2c7572d262c9ac130ece947e2 Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Thu, 12 Feb 2026 17:22:25 +0100 Subject: [PATCH 21/71] Update show page components to work with onetime templates Also rename some methods and use them wherever possible: - standalone_template? -> onetime_template? - recurring_template? -> series_template? --- .../blank_slate_component.rb | 10 +++++-- .../meeting_agenda_items/list_component.rb | 4 ++- .../components/meetings/header_component.rb | 3 +- .../header_infoline_component.html.erb | 9 +++++- .../meetings/side_panel_component.html.erb | 30 ++++++++++--------- .../meetings/agenda_component_streams.rb | 9 +++++- .../meeting_templates_controller.rb | 2 +- .../app/controllers/meetings_controller.rb | 4 +-- modules/meeting/app/models/meeting.rb | 22 +++++++------- .../app/services/meetings/create_service.rb | 4 +-- modules/meeting/config/locales/en.yml | 3 ++ 11 files changed, 63 insertions(+), 37 deletions(-) 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/modules/meeting/app/components/meetings/header_component.rb b/modules/meeting/app/components/meetings/header_component.rb index 1849321326c..65cf13af06d 100644 --- a/modules/meeting/app/components/meetings/header_component.rb +++ b/modules/meeting/app/components/meetings/header_component.rb @@ -76,11 +76,12 @@ module Meetings 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 action_button_params 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/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/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 index 008046658de..f3387a60e93 100644 --- a/modules/meeting/app/controllers/meeting_templates_controller.rb +++ b/modules/meeting/app/controllers/meeting_templates_controller.rb @@ -38,7 +38,7 @@ class MeetingTemplatesController < ApplicationController menu_item :meetings def index - @templates = Meeting.standalone_templates + @templates = Meeting.onetime_templates .visible .order(:title) diff --git a/modules/meeting/app/controllers/meetings_controller.rb b/modules/meeting/app/controllers/meetings_controller.rb index 19a6a074bab..651fef64b54 100644 --- a/modules/meeting/app/controllers/meetings_controller.rb +++ b/modules/meeting/app/controllers/meetings_controller.rb @@ -336,10 +336,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) diff --git a/modules/meeting/app/models/meeting.rb b/modules/meeting/app/models/meeting.rb index 175808318b4..d5af7813fbf 100644 --- a/modules/meeting/app/models/meeting.rb +++ b/modules/meeting/app/models/meeting.rb @@ -56,8 +56,8 @@ class Meeting < ApplicationRecord scope :templated, -> { where(template: true) } scope :not_templated, -> { where(template: false) } - scope :standalone_templates, -> { where(template: true, recurring_meeting_id: nil) } - scope :recurring_templates, -> { where(template: true).where.not(recurring_meeting_id: nil) } + 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 } @@ -118,7 +118,6 @@ class Meeting < ApplicationRecord accepts_nested_attributes_for :participants, allow_destroy: true validates :title, :project_id, presence: true - validates :start_time, presence: { unless: :standalone_template? } validates :duration, numericality: { greater_than: 0 } @@ -181,12 +180,12 @@ class Meeting < ApplicationRecord !!template end - def standalone_template? - template? && recurring_meeting_id.nil? + def series_template? + template? && recurring_meeting_id.present? end - def recurring_template? - template? && recurring_meeting_id.present? + def onetime_template? + template? && recurring_meeting_id.nil? end # One-time meeting time zone @@ -205,7 +204,7 @@ class Meeting < ApplicationRecord end def notify? - return false if standalone_template? + return false if onetime_template? if recurring? recurring_meeting.template.notify @@ -273,20 +272,21 @@ class Meeting < ApplicationRecord end def send_emails? - return false if standalone_template? + 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 template == true && recurring_meeting_id.nil? + return if onetime_template? super end def validate_date_and_time - return if standalone_template? + return if onetime_template? super end diff --git a/modules/meeting/app/services/meetings/create_service.rb b/modules/meeting/app/services/meetings/create_service.rb index b7887b7263e..e061e5d80c4 100644 --- a/modules/meeting/app/services/meetings/create_service.rb +++ b/modules/meeting/app/services/meetings/create_service.rb @@ -32,11 +32,11 @@ 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.template? && meeting.recurring_meeting_id.nil? + return call if meeting.onetime_template? if call.success? && Journal::NotificationConfiguration.active? && meeting.send_emails? meeting.participants.where(invited: true).find_each do |participant| diff --git a/modules/meeting/config/locales/en.yml b/modules/meeting/config/locales/en.yml index eb6f13a7b20..2ba1ca01248 100644 --- a/modules/meeting/config/locales/en.yml +++ b/modules/meeting/config/locales/en.yml @@ -667,6 +667,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?" From 5ec9174e06b8f256e752d0b00d2706732e35f41d Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Fri, 13 Feb 2026 17:46:08 +0100 Subject: [PATCH 22/71] Update creation flow to skip dialog when project is known --- .../index_sub_header_component.html.erb | 2 +- .../index_sub_header_component.rb | 10 +++++- .../meetings/header_component.html.erb | 5 +-- .../components/meetings/header_component.rb | 9 +++++ .../meetings/index/dialog_component.html.erb | 2 +- .../meetings/index/form_component.html.erb | 6 ++-- .../meetings/show_component.html.erb | 2 +- .../app/components/meetings/show_component.rb | 3 +- .../meeting_templates_controller.rb | 33 +++++++------------ .../app/controllers/meetings_controller.rb | 6 +++- 10 files changed, 45 insertions(+), 33 deletions(-) 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 index b7a84d7af46..38282732404 100644 --- 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 @@ -38,7 +38,7 @@ See COPYRIGHT and LICENSE files for more details. href: create_path, id: id, test_selector: "add-template-button", - data: { controller: "async-dialog" } + data: button_data ) do label_text 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 index 8f18a959d57..d42b57837b6 100644 --- a/modules/meeting/app/components/meeting_templates/index_sub_header_component.rb +++ b/modules/meeting/app/components/meeting_templates/index_sub_header_component.rb @@ -47,12 +47,20 @@ module MeetingTemplates def create_path if @project - new_dialog_template_project_meetings_path(@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 diff --git a/modules/meeting/app/components/meetings/header_component.html.erb b/modules/meeting/app/components/meetings/header_component.html.erb index c7d2b9f3f01..6033a665fd6 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| diff --git a/modules/meeting/app/components/meetings/header_component.rb b/modules/meeting/app/components/meetings/header_component.rb index 65cf13af06d..61e4698b886 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 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 47354e9fc8a..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,7 +8,7 @@ ) ) 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, 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 ec6de3326f3..69c87ce4599 100644 --- a/modules/meeting/app/components/meetings/index/form_component.html.erb +++ b/modules/meeting/app/components/meetings/index/form_component.html.erb @@ -35,8 +35,10 @@ end end - modal_body.with_row do - render(Meeting::Title.new(f)) + unless @template + modal_body.with_row do + render(Meeting::Title.new(f)) + end end unless @template 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/controllers/meeting_templates_controller.rb b/modules/meeting/app/controllers/meeting_templates_controller.rb index f3387a60e93..06c29571803 100644 --- a/modules/meeting/app/controllers/meeting_templates_controller.rb +++ b/modules/meeting/app/controllers/meeting_templates_controller.rb @@ -66,13 +66,15 @@ class MeetingTemplatesController < ApplicationController def create call = ::Meetings::CreateService .new(user: current_user) - .call(template_params) + .call(create_template_params) @template = call.result if call.success? - flash[:notice] = I18n.t(:notice_meeting_template_created) - redirect_to controller: "/meetings", action: "show", id: @template, status: :see_other + 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( @@ -88,18 +90,6 @@ class MeetingTemplatesController < ApplicationController end # TODO - # def show - # end - # - # def edit - # end - # - # def update - # end - # - # def update_title - # end - # # def delete_dialog # end # @@ -112,14 +102,15 @@ class MeetingTemplatesController < ApplicationController render_404 unless @project end - def template_params - permitted = params.expect(meeting: %i[title project_id]) + def create_template_params + project_id = @project&.id || params.dig(:meeting, :project_id) + project = project_id ? Project.find_by(id: project_id) : nil - permitted.merge( + { + title: I18n.t(:label_meeting_template_new), + project:, template: true, recurring_meeting_id: nil - ).tap do |p| - p[:project_id] = @project.id if @project.present? - end + } end end diff --git a/modules/meeting/app/controllers/meetings_controller.rb b/modules/meeting/app/controllers/meetings_controller.rb index 651fef64b54..a6a93199099 100644 --- a/modules/meeting/app/controllers/meetings_controller.rb +++ b/modules/meeting/app/controllers/meetings_controller.rb @@ -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 @@ -613,4 +613,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 From 72237767d77b28c476829aeac2e41d6467d72fbc Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Mon, 16 Feb 2026 11:45:54 +0100 Subject: [PATCH 23/71] Fix breadcrumbs --- .../app/components/meetings/header_component.rb | 11 ++++++++--- .../app/controllers/meeting_templates_controller.rb | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/meeting/app/components/meetings/header_component.rb b/modules/meeting/app/components/meetings/header_component.rb index 61e4698b886..9f2353d4618 100644 --- a/modules/meeting/app/components/meetings/header_component.rb +++ b/modules/meeting/app/components/meetings/header_component.rb @@ -128,13 +128,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) @@ -143,9 +145,12 @@ 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 diff --git a/modules/meeting/app/controllers/meeting_templates_controller.rb b/modules/meeting/app/controllers/meeting_templates_controller.rb index 06c29571803..1444f9db003 100644 --- a/modules/meeting/app/controllers/meeting_templates_controller.rb +++ b/modules/meeting/app/controllers/meeting_templates_controller.rb @@ -63,7 +63,7 @@ class MeetingTemplatesController < ApplicationController ) end - def create + def create # rubocop:disable Metrics/AbcSize call = ::Meetings::CreateService .new(user: current_user) .call(create_template_params) From e25b97a711640c82b848bf879dc887f5870ee82e Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Mon, 16 Feb 2026 13:33:37 +0100 Subject: [PATCH 24/71] Fix deletion --- .../components/meetings/delete_dialog_component.rb | 6 ++++-- .../components/meetings/header_component.html.erb | 2 +- .../app/components/meetings/header_component.rb | 12 +++++++++++- .../meeting/app/controllers/meetings_controller.rb | 6 +++--- modules/meeting/config/locales/en.yml | 5 +++++ 5 files changed, 24 insertions(+), 7 deletions(-) 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 6033a665fd6..5fb9578bec1 100644 --- a/modules/meeting/app/components/meetings/header_component.html.erb +++ b/modules/meeting/app/components/meetings/header_component.html.erb @@ -62,7 +62,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 9f2353d4618..e52a2324044 100644 --- a/modules/meeting/app/components/meetings/header_component.rb +++ b/modules/meeting/app/components/meetings/header_component.rb @@ -80,7 +80,7 @@ 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? @@ -157,6 +157,8 @@ module Meetings 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 @@ -169,5 +171,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/controllers/meetings_controller.rb b/modules/meeting/app/controllers/meetings_controller.rb index a6a93199099..c7232ca6f64 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 @@ -548,8 +548,8 @@ class MeetingsController < ApplicationController } 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 diff --git a/modules/meeting/config/locales/en.yml b/modules/meeting/config/locales/en.yml index 2ba1ca01248..450d2d176a9 100644 --- a/modules/meeting/config/locales/en.yml +++ b/modules/meeting/config/locales/en.yml @@ -133,6 +133,8 @@ en: label_meeting_templates: "Templates" 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_new: "New Meeting" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" @@ -295,6 +297,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?" From 17baf262488dee53a16ff69c884eec76985bdbd9 Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Mon, 16 Feb 2026 14:56:58 +0100 Subject: [PATCH 25/71] Add index row actions --- .../meeting_templates/row_component.rb | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/modules/meeting/app/components/meeting_templates/row_component.rb b/modules/meeting/app/components/meeting_templates/row_component.rb index 4f851ae535f..45c46e869f6 100644 --- a/modules/meeting/app/components/meeting_templates/row_component.rb +++ b/modules/meeting/app/components/meeting_templates/row_component.rb @@ -54,44 +54,44 @@ module MeetingTemplates data: { "test-selector": "more-button" } ) - # edit_action(menu) - # delete_action(menu) + edit_action(menu) + delete_action(menu) end end - # def edit_action(menu) - # return unless edit_allowed? - # - # menu.with_item( - # label: I18n.t(:button_edit), - # href: project_meeting_path(project, model) - # ) do |item| - # item.with_leading_visual_icon(icon: :pencil) - # end - # end + def edit_action(menu) + return unless edit_allowed? - # def delete_action(menu) - # return unless delete_allowed? - # - # menu.with_item( - # label: I18n.t(:button_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 + 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_allowed? - # User.current.allowed_in_project?(:edit_meetings, project) - # end - # - # def edit_allowed? - # User.current.allowed_in_project?(:edit_meetings, project) - # 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 From 7a75667ce6717b72f6207c2dac233cba89c22e86 Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Mon, 16 Feb 2026 17:56:57 +0100 Subject: [PATCH 26/71] Add request specs --- .../meeting/spec/factories/meeting_factory.rb | 6 + .../spec/requests/meeting_templates_spec.rb | 153 ++++++++++++++++++ .../meeting/spec/requests/meetings_spec.rb | 65 ++++++++ 3 files changed, 224 insertions(+) create mode 100644 modules/meeting/spec/requests/meeting_templates_spec.rb 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/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 From a13d8bc51ab38f49db3f1d296474043a6c151ef2 Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Tue, 17 Feb 2026 09:26:02 +0100 Subject: [PATCH 27/71] Add feature specs --- .../onetime_template_crud_spec.rb | 299 ++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 modules/meeting/spec/features/meeting_templates/onetime_template_crud_spec.rb 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..ebe3d57d570 --- /dev/null +++ b/modules/meeting/spec/features/meeting_templates/onetime_template_crud_spec.rb @@ -0,0 +1,299 @@ +# 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, + :with_cuprite 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 From 083d4a9dfa988b3931909ff1802804e9d6a5e1b9 Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Thu, 19 Feb 2026 15:59:46 +0100 Subject: [PATCH 28/71] Remove commented out code --- .../app/controllers/meeting_templates_controller.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/modules/meeting/app/controllers/meeting_templates_controller.rb b/modules/meeting/app/controllers/meeting_templates_controller.rb index 1444f9db003..fc7ed86f27b 100644 --- a/modules/meeting/app/controllers/meeting_templates_controller.rb +++ b/modules/meeting/app/controllers/meeting_templates_controller.rb @@ -89,13 +89,6 @@ class MeetingTemplatesController < ApplicationController end end - # TODO - # def delete_dialog - # end - # - # def destroy - # end - private def require_project From 508a04e95e75d4d629a6d79ab620ee6d090f3bdb Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Wed, 18 Feb 2026 13:33:21 +0100 Subject: [PATCH 29/71] Add button to create meeting from onetime template show page --- .../meetings/header_component.html.erb | 7 ++++ .../components/meetings/header_component.rb | 17 +++++++++ .../meetings/index/dialog_component.rb | 2 +- .../meetings/index/form_component.html.erb | 20 ++++++----- .../app/controllers/meetings_controller.rb | 35 +++++++++++++++---- modules/meeting/config/locales/en.yml | 1 + 6 files changed, 67 insertions(+), 15 deletions(-) diff --git a/modules/meeting/app/components/meetings/header_component.html.erb b/modules/meeting/app/components/meetings/header_component.html.erb index 5fb9578bec1..97c39222044 100644 --- a/modules/meeting/app/components/meetings/header_component.html.erb +++ b/modules/meeting/app/components/meetings/header_component.html.erb @@ -38,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, diff --git a/modules/meeting/app/components/meetings/header_component.rb b/modules/meeting/app/components/meetings/header_component.rb index e52a2324044..0a80d42ba69 100644 --- a/modules/meeting/app/components/meetings/header_component.rb +++ b/modules/meeting/app/components/meetings/header_component.rb @@ -93,6 +93,23 @@ module Meetings @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 { tag: :button, diff --git a/modules/meeting/app/components/meetings/index/dialog_component.rb b/modules/meeting/app/components/meetings/index/dialog_component.rb index 9e05354ecb4..eaed9e0a4c1 100644 --- a/modules/meeting/app/components/meetings/index/dialog_component.rb +++ b/modules/meeting/app/components/meetings/index/dialog_component.rb @@ -52,7 +52,7 @@ module Meetings def title return I18n.t(:label_meeting_template_new) if @template - return I18n.t(:label_meeting_duplicate) if @copy_from + return I18n.t(:label_meeting_duplicate) if @copy_from && !@copy_from.onetime_template? return I18n.t(:label_meeting_edit) if @meeting.persisted? case @meeting 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 69c87ce4599..04609953d6e 100644 --- a/modules/meeting/app/components/meetings/index/form_component.html.erb +++ b/modules/meeting/app/components/meetings/index/form_component.html.erb @@ -35,6 +35,8 @@ end end + # TODO: Add template autocompleter + unless @template modal_body.with_row do render(Meeting::Title.new(f)) @@ -125,16 +127,18 @@ 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 diff --git a/modules/meeting/app/controllers/meetings_controller.rb b/modules/meeting/app/controllers/meetings_controller.rb index c7232ca6f64..4253b11d831 100644 --- a/modules/meeting/app/controllers/meetings_controller.rb +++ b/modules/meeting/app/controllers/meetings_controller.rb @@ -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 @@ -447,6 +448,11 @@ class MeetingsController < ApplicationController .call(project: @project) @meeting = call.result + + # Load template if template_id is provided + @copy_from = if params[:template_id].present? + Meeting.onetime_templates.visible.find_by(id: params[:template_id]) + end end def global_upcoming_meetings @@ -484,6 +490,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 @@ -541,11 +550,25 @@ class MeetingsController < ApplicationController 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_series_template_destruction diff --git a/modules/meeting/config/locales/en.yml b/modules/meeting/config/locales/en.yml index 450d2d176a9..657fedf0f5f 100644 --- a/modules/meeting/config/locales/en.yml +++ b/modules/meeting/config/locales/en.yml @@ -135,6 +135,7 @@ en: 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" label_meeting_new_dynamic: "New one-time meeting" label_meeting_new_recurring: "New recurring meeting" From 39e847792f15b76c239a2040e34311e95493140c Mon Sep 17 00:00:00 2001 From: ulferts Date: Mon, 9 Feb 2026 09:40:36 +0100 Subject: [PATCH 30/71] move groups form to primer --- app/forms/groups/form.rb | 65 +++++++++++++++++++++++++++++ app/views/groups/_form.html.erb | 48 --------------------- app/views/groups/_general.html.erb | 9 ++-- app/views/groups/new.html.erb | 11 +++-- spec/features/groups/groups_spec.rb | 6 +-- 5 files changed, 80 insertions(+), 59 deletions(-) create mode 100644 app/forms/groups/form.rb delete mode 100644 app/views/groups/_form.html.erb 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/views/groups/_form.html.erb b/app/views/groups/_form.html.erb deleted file mode 100644 index 416c8efb177..00000000000 --- a/app/views/groups/_form.html.erb +++ /dev/null @@ -1,48 +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. - -++#%> - -<%# - needs locals: - f: labelled form builder -%> - -<%= 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/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/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" From 32f1940e6c682685bc12f0fc2260b6706059f8b2 Mon Sep 17 00:00:00 2001 From: ulferts Date: Wed, 18 Feb 2026 17:30:24 +0100 Subject: [PATCH 31/71] render version form in primer --- app/forms/versions/form.rb | 205 ++++++++++++++++++ app/helpers/versions_helper.rb | 12 +- app/helpers/wiki_helper.rb | 2 +- app/views/versions/_form.html.erb | 81 ------- app/views/versions/edit.html.erb | 9 +- app/views/versions/new.html.erb | 9 +- .../app/helpers/version_settings_helper.rb | 84 ------- .../patches/versions_controller_patch.rb | 4 - .../helpers/version_settings_helper_spec.rb | 41 ---- spec/features/versions/create_spec.rb | 2 +- spec/features/versions/edit_spec.rb | 4 +- 11 files changed, 217 insertions(+), 236 deletions(-) create mode 100644 app/forms/versions/form.rb delete mode 100644 app/views/versions/_form.html.erb delete mode 100644 modules/backlogs/app/helpers/version_settings_helper.rb delete mode 100644 modules/backlogs/spec/helpers/version_settings_helper_spec.rb 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/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/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/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/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/helpers/version_settings_helper_spec.rb b/modules/backlogs/spec/helpers/version_settings_helper_spec.rb deleted file mode 100644 index 33724b427af..00000000000 --- a/modules/backlogs/spec/helpers/version_settings_helper_spec.rb +++ /dev/null @@ -1,41 +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. -#++ - -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 - - it { expect(helper.send(:position_display_options)).to eql @expected_options } - end -end 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" From 70512d15d3082a6b4936057274754e3193dc1331 Mon Sep 17 00:00:00 2001 From: Klaus Zanders Date: Fri, 20 Feb 2026 16:27:47 +0100 Subject: [PATCH 32/71] Do not allow to set a section outside of the meeting when creating an agenda item --- .../meeting_agenda_items/create_contract.rb | 12 +++++++++++- modules/meeting/config/locales/en.yml | 2 ++ .../meeting_agenda_items/create_contract_spec.rb | 15 +++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) 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/config/locales/en.yml b/modules/meeting/config/locales/en.yml index 49639681fef..f5a5d210b28 100644 --- a/modules/meeting/config/locales/en.yml +++ b/modules/meeting/config/locales/en.yml @@ -76,6 +76,8 @@ en: participation_status: "Participation status" errors: models: + meeting_agenda_item: + section_not_belong_to_meeting: "Section does not belong to the same meeting." meeting_participant: invalid_user: "%{name} is not a valid participant." recurring_meeting: 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 cb5006e0feb..e70d1f5ef44 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 @@ -80,4 +80,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 From 702518e4f5bf2b9415488ce2cd1106dc89f43438 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 05:49:13 +0000 Subject: [PATCH 33/71] Bump benc-uk/workflow-dispatch from 1.2.4 to 1.3.1 Bumps [benc-uk/workflow-dispatch](https://github.com/benc-uk/workflow-dispatch) from 1.2.4 to 1.3.1. - [Release notes](https://github.com/benc-uk/workflow-dispatch/releases) - [Commits](https://github.com/benc-uk/workflow-dispatch/compare/e2e5e9a103e331dad343f381a29e654aea3cf8fc...7a027648b88c2413826b6ddd6c76114894dc5ec4) --- updated-dependencies: - dependency-name: benc-uk/workflow-dispatch dependency-version: 1.3.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/hocuspocus-docker.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/hocuspocus-docker.yml b/.github/workflows/hocuspocus-docker.yml index a4b21af5de6..030b6bf2bdc 100644 --- a/.github/workflows/hocuspocus-docker.yml +++ b/.github/workflows/hocuspocus-docker.yml @@ -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 From 9f13c338b0b5e5b8267e30f61eca57c387a192c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 23 Feb 2026 07:08:04 +0100 Subject: [PATCH 34/71] Use visible scope in project breadcrumb https://community.openproject.org/work_packages/71972 --- .../app/components/overviews/page_header_component.rb | 8 ++++---- .../components/overviews/page_header_component_spec.rb | 10 ++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/modules/overviews/app/components/overviews/page_header_component.rb b/modules/overviews/app/components/overviews/page_header_component.rb index 698412d7dd6..a2ff7ea4f1b 100644 --- a/modules/overviews/app/components/overviews/page_header_component.rb +++ b/modules/overviews/app/components/overviews/page_header_component.rb @@ -42,18 +42,18 @@ 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 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..35bca716afa 100644 --- a/modules/overviews/spec/components/overviews/page_header_component_spec.rb +++ b/modules/overviews/spec/components/overviews/page_header_component_spec.rb @@ -74,7 +74,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 +82,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 @@ -231,7 +229,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 +244,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 From 8d29a1ef161a3d44d35fa566e2739a33bcfda50c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 23 Feb 2026 08:55:35 +0100 Subject: [PATCH 35/71] Add .visible on backlogs story --- modules/backlogs/app/controllers/rb_stories_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/backlogs/app/controllers/rb_stories_controller.rb b/modules/backlogs/app/controllers/rb_stories_controller.rb index 3b6dbc3402a..c696dfa8d9b 100644 --- a/modules/backlogs/app/controllers/rb_stories_controller.rb +++ b/modules/backlogs/app/controllers/rb_stories_controller.rb @@ -44,7 +44,7 @@ class RbStoriesController < RbApplicationController end def update - story = Story.find(params[:id]) + story = Story.visible.find(params[:id]) call = Stories::UpdateService .new(user: current_user, story:) From 6e228dc06a041e646730ca1bfce1634707d4fe4e Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Thu, 19 Feb 2026 15:54:35 +0100 Subject: [PATCH 36/71] Fine-tune the shown forms of custom fields & fix tests --- .../custom_fields/details_component.rb | 8 +++++-- app/forms/custom_fields/details_form.rb | 6 +++--- .../project_custom_fields/list_items.html.erb | 6 +++--- .../custom_fields/_custom_options.html.erb | 10 ++++----- app/views/custom_fields/list_items.html.erb | 6 +++--- .../custom_fields/projects/boolean_spec.rb | 4 +--- .../projects/create_in_section_spec.rb | 2 +- .../custom_fields/projects/shared_context.rb | 12 +---------- .../shared_custom_field_expectations.rb | 21 ++++++++----------- .../custom_fields/time_entries/list_spec.rb | 12 +++++++---- .../custom_fields/work_packages/list_spec.rb | 9 ++++---- .../work_packages/multi_value_list_spec.rb | 9 +++++--- .../custom_fields/reorder_options_spec.rb | 4 ++++ spec/models/custom_field_spec.rb | 11 ---------- spec/support/pages/custom_fields/index.rb | 2 +- 15 files changed, 56 insertions(+), 66 deletions(-) diff --git a/app/components/custom_fields/details_component.rb b/app/components/custom_fields/details_component.rb index 31b51f12b82..e2d4f0272ee 100644 --- a/app/components/custom_fields/details_component.rb +++ b/app/components/custom_fields/details_component.rb @@ -84,8 +84,12 @@ module CustomFields end def persisted_cf_has_no_items_or_projects? - if custom_field.list? && custom_field.custom_options.empty? && custom_field.projects.empty? - return true + 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? && diff --git a/app/forms/custom_fields/details_form.rb b/app/forms/custom_fields/details_form.rb index 57d4f747f26..eb37d53f2ee 100644 --- a/app/forms/custom_fields/details_form.rb +++ b/app/forms/custom_fields/details_form.rb @@ -82,7 +82,6 @@ module CustomFields details_form.text_field( name: :regexp, label: label(:regexp), - size: 50, caption: instructions(:regexp), input_width: :medium ) @@ -238,7 +237,7 @@ module CustomFields end def show_right_to_left_field? - %w[text].include?(model.field_format) + model.is_a?(WorkPackageCustomField) && %w[text].include?(model.field_format) end def show_multi_value_field? @@ -258,7 +257,8 @@ module CustomFields end def show_is_searchable_field? - %w[bool date float int user version hierarchy weighted_item_list calculated_value].exclude?(model.field_format) + (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? 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 index f0adfc60203..6247d928387 100644 --- a/app/views/admin/settings/project_custom_fields/list_items.html.erb +++ b/app/views/admin/settings/project_custom_fields/list_items.html.erb @@ -65,10 +65,10 @@ See COPYRIGHT and LICENSE files for more details. flex.with_column do render Primer::Beta::Button.new( scheme: :primary, - type: :submit, - ) do |button| + type: :submit + ) do |button| button.with_leading_visual_icon(icon: :check) - t(:button_submit) + t(:button_save) end end end diff --git a/app/views/custom_fields/_custom_options.html.erb b/app/views/custom_fields/_custom_options.html.erb index b122d871e5a..fa89235fbe6 100644 --- a/app/views/custom_fields/_custom_options.html.erb +++ b/app/views/custom_fields/_custom_options.html.erb @@ -93,7 +93,7 @@ See COPYRIGHT and LICENSE files for more details. no_label: true %> - + <%= co_f.check_box :default_value, container_class: "custom-option-default-value", data: { @@ -105,7 +105,7 @@ See COPYRIGHT and LICENSE files for more details. <%= op_icon("icon-context icon-arrow-down2 icon-small") %> Date: Mon, 23 Feb 2026 09:15:16 +0100 Subject: [PATCH 37/71] Hide perPage options on mobile --- frontend/src/global_styles/content/_pagination.sass | 3 +++ 1 file changed, 3 insertions(+) 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 From 8e0b3513671aafaf29bfed6123e5a80c2ebdebdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 23 Feb 2026 07:13:50 +0100 Subject: [PATCH 38/71] Pass enabled value to external links controller so it can be properly disabled https://community.openproject.org/projects/openproject/work_packages/71946/activity --- app/helpers/application_helper.rb | 1 + .../controllers/external-links.controller.ts | 57 +++++++++++-------- spec/views/layouts/base.html.erb_spec.rb | 6 +- 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 10d6b126d3a..9a10f1ea181 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -305,6 +305,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/frontend/src/stimulus/controllers/external-links.controller.ts b/frontend/src/stimulus/controllers/external-links.controller.ts index e65f3737731..a9f3853934c 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 { document.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/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 From 6e8df4ce22d3c2d28079cd4b786f255a265f80ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 23 Feb 2026 08:38:28 +0100 Subject: [PATCH 39/71] Revert specs changed for external redirect behavior again when not active --- spec/features/a11y/external_links_spec.rb | 3 +-- spec/features/external_link_capture_spec.rb | 3 +-- .../overview_page/dialog/inputs_spec.rb | 3 +-- spec/features/wiki/wiki_page_external_link_spec.rb | 3 +-- .../details/custom_fields/custom_field_spec.rb | 7 +++---- .../work_packages/details/markdown/todolist_spec.rb | 6 ++---- spec/features/wysiwyg/linking_spec.rb | 4 ++-- 7 files changed, 11 insertions(+), 18 deletions(-) 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/external_link_capture_spec.rb b/spec/features/external_link_capture_spec.rb index 2ef6c02cdde..185a88f43d4 100644 --- a/spec/features/external_link_capture_spec.rb +++ b/spec/features/external_link_capture_spec.rb @@ -51,8 +51,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/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/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/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/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 From 8b3488bcd5d29679bd21c6e52ac47a54d14d4626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 23 Feb 2026 10:25:09 +0100 Subject: [PATCH 40/71] Fix double agenda item section --- modules/meeting/config/locales/en.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/meeting/config/locales/en.yml b/modules/meeting/config/locales/en.yml index 771bcb5c4aa..8fb6b01c88b 100644 --- a/modules/meeting/config/locales/en.yml +++ b/modules/meeting/config/locales/en.yml @@ -78,11 +78,10 @@ en: participation_status: "Participation status" errors: models: - meeting_agenda_item: - section_not_belong_to_meeting: "Section does not belong to the same meeting." 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" From 17d6cb06f1059889b0c8dceefbc1f1963af2f48a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 23 Feb 2026 09:27:22 +0100 Subject: [PATCH 41/71] Use visible scope on sprint and projects in backlogs --- .../controllers/rb_application_controller.rb | 6 +- .../app/controllers/rb_sprints_controller.rb | 6 +- .../controllers/rb_sprints_controller_spec.rb | 97 +++++++++++++++++++ 3 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 modules/backlogs/spec/controllers/rb_sprints_controller_spec.rb diff --git a/modules/backlogs/app/controllers/rb_application_controller.rb b/modules/backlogs/app/controllers/rb_application_controller.rb index 37e0502db72..6091a5a63cc 100644 --- a/modules/backlogs/app/controllers/rb_application_controller.rb +++ b/modules/backlogs/app/controllers/rb_application_controller.rb @@ -44,11 +44,11 @@ class RbApplicationController < ApplicationController # because of strong params, we want to pluck this variable out right now, # otherwise it causes issues where we are doing `attributes=`. if (@sprint_id = params.delete(:sprint_id)) - @sprint = Sprint.find(@sprint_id) + @sprint = Sprint.visible.find(@sprint_id) @project = @sprint.project + elsif params[:project_id] + @project = Project.visible.find(params[:project_id]) end - # This overrides sprint's project if we set another project, say a subproject - @project = Project.find(params[:project_id]) if params[:project_id] end def check_if_plugin_is_configured diff --git a/modules/backlogs/app/controllers/rb_sprints_controller.rb b/modules/backlogs/app/controllers/rb_sprints_controller.rb index 3603a756b44..04894ac3b90 100644 --- a/modules/backlogs/app/controllers/rb_sprints_controller.rb +++ b/modules/backlogs/app/controllers/rb_sprints_controller.rb @@ -46,10 +46,10 @@ class RbSprintsController < RbApplicationController # :sprint_id def load_sprint_and_project if params[:id] - @sprint = Sprint.find(params[:id]) + @sprint = Sprint.visible.find(params[:id]) @project = @sprint.project + elsif params[:project_id] + @project = Project.visible.find(params[:project_id]) end - # This overrides sprint's project if we set another project, say a subproject - @project = Project.find(params[:project_id]) if params[:project_id] end end diff --git a/modules/backlogs/spec/controllers/rb_sprints_controller_spec.rb b/modules/backlogs/spec/controllers/rb_sprints_controller_spec.rb new file mode 100644 index 00000000000..851e6acff73 --- /dev/null +++ b/modules/backlogs/spec/controllers/rb_sprints_controller_spec.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. +#++ +require "spec_helper" + +RSpec.describe RbSprintsController 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 + + put :update, + params: { + project_id: other_project.id, + id: sprint.id, + name: new_name + } + 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 + put :update, + params: { + project_id: sprint_project.id, + id: sprint.id, + name: new_name + } + sprint.reload + + expect(sprint.name).to eq(new_name) + end + end + end +end From c406e7c5c35a2584848fc6c62ea5680f87d944da Mon Sep 17 00:00:00 2001 From: Kabiru Mwenja Date: Mon, 23 Feb 2026 14:57:31 +0300 Subject: [PATCH 42/71] Scope journal lookup to work package in activities tab https://community.openproject.org/wp/72390 https://community.openproject.org/wp/72393 --- .../activities_tab_controller.rb | 5 +- .../activities_tab_controller_spec.rb | 81 +++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/app/controllers/work_packages/activities_tab_controller.rb b/app/controllers/work_packages/activities_tab_controller.rb index 4f5fa502eb6..e0f6db3ba80 100644 --- a/app/controllers/work_packages/activities_tab_controller.rb +++ b/app/controllers/work_packages/activities_tab_controller.rb @@ -232,6 +232,7 @@ class WorkPackages::ActivitiesTabController < ApplicationController 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 @@ -249,7 +250,9 @@ class WorkPackages::ActivitiesTabController < ApplicationController end def find_journal - @journal = Journal + @journal = @work_package + .journals + .internal_visible .with_sequence_version .find(params[:id]) rescue ActiveRecord::RecordNotFound diff --git a/spec/controllers/work_packages/activities_tab_controller_spec.rb b/spec/controllers/work_packages/activities_tab_controller_spec.rb index e43413fa486..f6d318a882e 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 From 058e0e1dd9d6bfa8ee250e2dbfe484e1e63ee19a Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Mon, 23 Feb 2026 12:57:04 +0100 Subject: [PATCH 43/71] Guard params check and allow further controller on table component --- app/components/table_component.rb | 7 ++++++- app/controllers/concerns/custom_fields/shared_actions.rb | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/components/table_component.rb b/app/components/table_component.rb index a5ab35781ee..efe7f01ebd8 100644 --- a/app/components/table_component.rb +++ b/app/components/table_component.rb @@ -31,6 +31,8 @@ ## # Abstract view component. Subclass this for a concrete table. class TableComponent < ApplicationComponent + include Primer::AttributesHelper + def initialize(rows: [], table_arguments: {}, **) super(rows, **) @@ -41,7 +43,10 @@ class TableComponent < ApplicationComponent "generic-table" ) @system_arguments[:data] ||= {} - @system_arguments[:data][:controller] = "table-highlighting" + @system_arguments[:data] = merge_data( + @system_arguments, + data: { controller: "table-highlighting" } + ) end class << self diff --git a/app/controllers/concerns/custom_fields/shared_actions.rb b/app/controllers/concerns/custom_fields/shared_actions.rb index 8cd591d82a4..30d4a7c5e0c 100644 --- a/app/controllers/concerns/custom_fields/shared_actions.rb +++ b/app/controllers/concerns/custom_fields/shared_actions.rb @@ -167,6 +167,8 @@ module CustomFields end def custom_options_attributes + return unless params[:custom_field] + params[:custom_field][:custom_options_attributes] end end From cf7e6947aa463d563cbd149b616da46f6e1d3513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 23 Feb 2026 15:14:42 +0100 Subject: [PATCH 44/71] Remove name and prefix id for all dom elements in pipeline --- .../filters/sanitization_filter.rb | 55 ++++++++- .../filters/table_of_contents_filter.rb | 8 +- .../filters/sanitization_filter_spec.rb | 104 ++++++++++++++++++ 3 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 spec/lib/open_project/text_formatting/filters/sanitization_filter_spec.rb 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..1912a7c6e2a 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 @@ -44,7 +44,7 @@ module OpenProject::TextFormatting def add_header_link_class_and_id(node, id) node.css("a").first["class"] = "op-uc-link_permalink icon-link" - node["id"] = id + node["id"] = "#{fragment_id_prefix}#{id}" end ## @@ -134,7 +134,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/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 From 6aa69fc7e66b94e9e5a64c8dc32a2f923c82b61f Mon Sep 17 00:00:00 2001 From: Kabiru Mwenja Date: Fri, 13 Feb 2026 13:37:44 +0300 Subject: [PATCH 45/71] Add hocuspocus logging extension for more detail in logs https://tiptap.dev/docs/hocuspocus/server/extensions/logger --- .../op-blocknote-hocuspocus/package-lock.json | 65 +++++++++++-------- .../op-blocknote-hocuspocus/package.json | 1 + .../op-blocknote-hocuspocus/src/index.ts | 5 +- 3 files changed, 43 insertions(+), 28 deletions(-) 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/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, + }), ], }); From 7ff33210f6ae33ab36a6efca3f26f74f5428d927 Mon Sep 17 00:00:00 2001 From: Jan Sandbrink Date: Mon, 23 Feb 2026 15:40:42 +0100 Subject: [PATCH 46/71] Upgrade mcp gem to 0.7.1 This fixed a few regressions around error handling, but some errors are still handled differently than before... --- Gemfile | 2 +- Gemfile.lock | 9 ++---- spec/requests/api/v3/support/mcp_examples.rb | 32 ++++++++++++++++++- .../mcp/mcp_tools/search_portfolios_spec.rb | 2 +- .../mcp/mcp_tools/search_programs_spec.rb | 2 +- .../mcp/mcp_tools/search_projects_spec.rb | 2 +- 6 files changed, 38 insertions(+), 11 deletions(-) diff --git a/Gemfile b/Gemfile index 14aec14508e..e8ccb4a4166 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" diff --git a/Gemfile.lock b/Gemfile.lock index 54235568b40..24f5744efb4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -741,7 +741,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) @@ -806,9 +805,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) @@ -1632,7 +1630,7 @@ 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) @@ -1964,7 +1962,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 @@ -1985,7 +1982,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 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 From 0f06d11c4fa95d6b914e617f320941ec2192f432 Mon Sep 17 00:00:00 2001 From: Dombi Attila <83396+dombesz@users.noreply.github.com> Date: Mon, 23 Feb 2026 16:11:58 +0200 Subject: [PATCH 47/71] [#72374] User can get edit budget permissions without view budget permissions https://community.openproject.org/work_packages/72374 --- ...view_budgets_to_roles_with_edit_budgets.rb | 41 +++++++++ modules/budgets/lib/budgets/engine.rb | 3 +- .../spec/lib/budgets/permissions_spec.rb | 41 +++++++++ ...budgets_to_roles_with_edit_budgets_spec.rb | 85 +++++++++++++++++++ 4 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20260223142025_add_view_budgets_to_roles_with_edit_budgets.rb create mode 100644 modules/budgets/spec/lib/budgets/permissions_spec.rb create mode 100644 spec/migrations/add_view_budgets_to_roles_with_edit_budgets_spec.rb 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/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/budgets/spec/lib/budgets/permissions_spec.rb b/modules/budgets/spec/lib/budgets/permissions_spec.rb new file mode 100644 index 00000000000..b8983f5ccb3 --- /dev/null +++ b/modules/budgets/spec/lib/budgets/permissions_spec.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 "spec_helper" + +RSpec.describe OpenProject::AccessControl, "Budgets module permissions" do # rubocop:disable RSpec/SpecFilePathFormat + describe "edit_budgets" do + subject { described_class.permission(:edit_budgets) } + + it "depends on view_budgets" do + expect(subject.dependencies).to contain_exactly(:view_budgets) + end + end +end 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 From 91e4a357abac4821f47e463dc033db776945ffae Mon Sep 17 00:00:00 2001 From: Kabiru Mwenja Date: Mon, 23 Feb 2026 17:43:00 +0300 Subject: [PATCH 48/71] Reduce custom logging, delegate to logging extension --- .../op-blocknote-hocuspocus/src/extensions/openProjectApi.ts | 5 ----- 1 file changed, 5 deletions(-) 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 From cd8c6eb516a62786dd6be6f0e0fd91639448766e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 23 Feb 2026 15:46:14 +0100 Subject: [PATCH 49/71] Avoid duplicate anchor links --- .../filters/table_of_contents_filter.rb | 7 +- .../wiki/wiki_page_navigation_spec.rb | 4 +- .../markdown/attribute_macros_spec.rb | 8 +-- .../text_formatting/markdown/headings_spec.rb | 8 +-- .../markdown/toc_macro_spec.rb | 72 +++++++++---------- 5 files changed, 52 insertions(+), 47 deletions(-) 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 1912a7c6e2a..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,7 +43,12 @@ module OpenProject::TextFormatting end def add_header_link_class_and_id(node, id) - node.css("a").first["class"] = "op-uc-link_permalink icon-link" + 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 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/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

From 074f4a293aa9682a59ae7d4f65c3f469a72b8a02 Mon Sep 17 00:00:00 2001 From: Jan Sandbrink Date: Mon, 23 Feb 2026 17:50:55 +0100 Subject: [PATCH 50/71] Replace filled sparkle with sparkle outline --- config/initializers/menus.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/menus.rb b/config/initializers/menus.rb index f4f03e9a517..82f3f51a104 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 }, From b36e5113320c78ba1b85e3fa2384bc1969ad6b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 23 Feb 2026 19:15:52 +0100 Subject: [PATCH 51/71] Take over dataset value from body controller (#22073) Follow-up for https://github.com/opf/openproject/pull/22067 --- frontend/src/elements/block-note-element.ts | 3 +++ 1 file changed, 3 insertions(+) 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); From 71ea360df8f7e5fd127ef2e595b6668e110da941 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 17:50:47 -0300 Subject: [PATCH 52/71] Bump actions/create-github-app-token from 1 to 2 (#22040) Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1 to 2. - [Release notes](https://github.com/actions/create-github-app-token/releases) - [Commits](https://github.com/actions/create-github-app-token/compare/v1...v2) --- updated-dependencies: - dependency-name: actions/create-github-app-token dependency-version: '2' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/hocuspocus-docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/hocuspocus-docker.yml b/.github/workflows/hocuspocus-docker.yml index a4b21af5de6..aec8168d38d 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 }} From f003ed2d9c597d21e8eb3c826b3f233afe788c6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 17:51:15 -0300 Subject: [PATCH 53/71] Bump qs from 6.14.1 to 6.14.2 in /frontend (#21979) Bumps [qs](https://github.com/ljharb/qs) from 6.14.1 to 6.14.2. - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.14.1...v6.14.2) --- updated-dependencies: - dependency-name: qs dependency-version: 6.14.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/package-lock.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9c04dd5c5ff..9d5fe5d0543 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -21162,10 +21162,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" }, @@ -39468,9 +39467,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" } From dc577a0f91c363fa4bea7b5329bbe60c49392f8f Mon Sep 17 00:00:00 2001 From: OpenProject Actions CI Date: Tue, 24 Feb 2026 03:53:10 +0000 Subject: [PATCH 54/71] update locales from crowdin [ci skip] --- config/locales/crowdin/fr.yml | 52 ++++++++--------- config/locales/crowdin/tr.yml | 2 - .../backlogs/config/locales/crowdin/fr.yml | 56 +++++++++---------- .../backlogs/config/locales/crowdin/js-fr.yml | 2 +- modules/budgets/config/locales/crowdin/fr.yml | 46 +++++++-------- .../budgets/config/locales/crowdin/js-fr.yml | 4 +- modules/costs/config/locales/crowdin/fr.yml | 10 ++-- .../costs/config/locales/crowdin/js-fr.yml | 4 +- .../documents/config/locales/crowdin/af.yml | 5 +- .../documents/config/locales/crowdin/ar.yml | 5 +- .../documents/config/locales/crowdin/az.yml | 5 +- .../documents/config/locales/crowdin/be.yml | 5 +- .../documents/config/locales/crowdin/bg.yml | 5 +- .../documents/config/locales/crowdin/ca.yml | 5 +- .../config/locales/crowdin/ckb-IR.yml | 5 +- .../documents/config/locales/crowdin/cs.yml | 5 +- .../documents/config/locales/crowdin/da.yml | 5 +- .../documents/config/locales/crowdin/de.yml | 5 +- .../documents/config/locales/crowdin/el.yml | 5 +- .../documents/config/locales/crowdin/eo.yml | 5 +- .../documents/config/locales/crowdin/es.yml | 5 +- .../documents/config/locales/crowdin/et.yml | 5 +- .../documents/config/locales/crowdin/eu.yml | 5 +- .../documents/config/locales/crowdin/fa.yml | 5 +- .../documents/config/locales/crowdin/fi.yml | 5 +- .../documents/config/locales/crowdin/fil.yml | 5 +- .../documents/config/locales/crowdin/fr.yml | 5 +- .../documents/config/locales/crowdin/he.yml | 5 +- .../documents/config/locales/crowdin/hi.yml | 5 +- .../documents/config/locales/crowdin/hr.yml | 5 +- .../documents/config/locales/crowdin/hu.yml | 5 +- .../documents/config/locales/crowdin/id.yml | 5 +- .../documents/config/locales/crowdin/it.yml | 5 +- .../documents/config/locales/crowdin/ja.yml | 5 +- .../documents/config/locales/crowdin/ka.yml | 5 +- .../documents/config/locales/crowdin/kk.yml | 5 +- .../documents/config/locales/crowdin/ko.yml | 5 +- .../documents/config/locales/crowdin/lt.yml | 5 +- .../documents/config/locales/crowdin/lv.yml | 5 +- .../documents/config/locales/crowdin/mn.yml | 5 +- .../documents/config/locales/crowdin/ms.yml | 5 +- .../documents/config/locales/crowdin/ne.yml | 5 +- .../documents/config/locales/crowdin/nl.yml | 5 +- .../documents/config/locales/crowdin/no.yml | 5 +- .../documents/config/locales/crowdin/pl.yml | 5 +- .../config/locales/crowdin/pt-BR.yml | 5 +- .../config/locales/crowdin/pt-PT.yml | 5 +- .../documents/config/locales/crowdin/ro.yml | 5 +- .../documents/config/locales/crowdin/ru.yml | 5 +- .../documents/config/locales/crowdin/rw.yml | 5 +- .../documents/config/locales/crowdin/si.yml | 5 +- .../documents/config/locales/crowdin/sk.yml | 5 +- .../documents/config/locales/crowdin/sl.yml | 5 +- .../documents/config/locales/crowdin/sr.yml | 5 +- .../documents/config/locales/crowdin/sv.yml | 5 +- .../documents/config/locales/crowdin/th.yml | 5 +- .../documents/config/locales/crowdin/tr.yml | 5 +- .../documents/config/locales/crowdin/uk.yml | 5 +- .../documents/config/locales/crowdin/uz.yml | 5 +- .../documents/config/locales/crowdin/vi.yml | 5 +- .../config/locales/crowdin/zh-CN.yml | 5 +- .../config/locales/crowdin/zh-TW.yml | 5 +- .../grids/config/locales/crowdin/js-fr.yml | 2 +- modules/meeting/config/locales/crowdin/af.yml | 1 + modules/meeting/config/locales/crowdin/ar.yml | 1 + modules/meeting/config/locales/crowdin/az.yml | 1 + modules/meeting/config/locales/crowdin/be.yml | 1 + modules/meeting/config/locales/crowdin/bg.yml | 1 + modules/meeting/config/locales/crowdin/ca.yml | 1 + .../meeting/config/locales/crowdin/ckb-IR.yml | 1 + modules/meeting/config/locales/crowdin/cs.yml | 1 + modules/meeting/config/locales/crowdin/da.yml | 1 + modules/meeting/config/locales/crowdin/de.yml | 1 + modules/meeting/config/locales/crowdin/el.yml | 1 + modules/meeting/config/locales/crowdin/eo.yml | 1 + modules/meeting/config/locales/crowdin/es.yml | 1 + modules/meeting/config/locales/crowdin/et.yml | 1 + modules/meeting/config/locales/crowdin/eu.yml | 1 + modules/meeting/config/locales/crowdin/fa.yml | 1 + modules/meeting/config/locales/crowdin/fi.yml | 1 + .../meeting/config/locales/crowdin/fil.yml | 1 + modules/meeting/config/locales/crowdin/fr.yml | 21 +++---- modules/meeting/config/locales/crowdin/he.yml | 1 + modules/meeting/config/locales/crowdin/hi.yml | 1 + modules/meeting/config/locales/crowdin/hr.yml | 1 + modules/meeting/config/locales/crowdin/hu.yml | 1 + modules/meeting/config/locales/crowdin/id.yml | 1 + modules/meeting/config/locales/crowdin/it.yml | 1 + modules/meeting/config/locales/crowdin/ja.yml | 1 + modules/meeting/config/locales/crowdin/ka.yml | 1 + modules/meeting/config/locales/crowdin/kk.yml | 1 + modules/meeting/config/locales/crowdin/ko.yml | 1 + modules/meeting/config/locales/crowdin/lt.yml | 1 + modules/meeting/config/locales/crowdin/lv.yml | 1 + modules/meeting/config/locales/crowdin/mn.yml | 1 + modules/meeting/config/locales/crowdin/ms.yml | 1 + modules/meeting/config/locales/crowdin/ne.yml | 1 + modules/meeting/config/locales/crowdin/nl.yml | 1 + modules/meeting/config/locales/crowdin/no.yml | 1 + modules/meeting/config/locales/crowdin/pl.yml | 1 + .../meeting/config/locales/crowdin/pt-BR.yml | 1 + .../meeting/config/locales/crowdin/pt-PT.yml | 1 + modules/meeting/config/locales/crowdin/ro.yml | 1 + modules/meeting/config/locales/crowdin/ru.yml | 1 + modules/meeting/config/locales/crowdin/rw.yml | 1 + modules/meeting/config/locales/crowdin/si.yml | 1 + modules/meeting/config/locales/crowdin/sk.yml | 1 + modules/meeting/config/locales/crowdin/sl.yml | 1 + modules/meeting/config/locales/crowdin/sr.yml | 1 + modules/meeting/config/locales/crowdin/sv.yml | 1 + modules/meeting/config/locales/crowdin/th.yml | 1 + modules/meeting/config/locales/crowdin/tr.yml | 1 + modules/meeting/config/locales/crowdin/uk.yml | 1 + modules/meeting/config/locales/crowdin/uz.yml | 1 + modules/meeting/config/locales/crowdin/vi.yml | 1 + .../meeting/config/locales/crowdin/zh-CN.yml | 1 + .../meeting/config/locales/crowdin/zh-TW.yml | 1 + 117 files changed, 368 insertions(+), 154 deletions(-) diff --git a/config/locales/crowdin/fr.yml b/config/locales/crowdin/fr.yml index 44ee530fcfe..9f76b58e9f9 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." @@ -618,7 +618,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 +634,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 +649,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 +1172,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 +1548,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 +1656,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 +2878,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 +3988,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 +4150,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 +4335,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 +4382,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" @@ -4925,10 +4925,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/tr.yml b/config/locales/crowdin/tr.yml index caec81b968d..b7f06d69f15 100644 --- a/config/locales/crowdin/tr.yml +++ b/config/locales/crowdin/tr.yml @@ -1049,7 +1049,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 +3236,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" 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/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/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/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.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..e35a01ad7ff 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: "Hocuspocus server URL" activerecord: errors: models: @@ -125,7 +127,8 @@ ru: 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/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/config/locales/crowdin/js-fr.yml b/modules/grids/config/locales/crowdin/js-fr.yml index ec98e22264f..674c862520b 100644 --- a/modules/grids/config/locales/crowdin/js-fr.yml +++ b/modules/grids/config/locales/crowdin/js-fr.yml @@ -6,7 +6,7 @@ fr: 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/meeting/config/locales/crowdin/af.yml b/modules/meeting/config/locales/crowdin/af.yml index 934ad58b7fb..a40cde2366d 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" diff --git a/modules/meeting/config/locales/crowdin/ar.yml b/modules/meeting/config/locales/crowdin/ar.yml index b633fbfd2ee..adf6d511f2b 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" diff --git a/modules/meeting/config/locales/crowdin/az.yml b/modules/meeting/config/locales/crowdin/az.yml index 897a70456c5..d337236b72c 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" diff --git a/modules/meeting/config/locales/crowdin/be.yml b/modules/meeting/config/locales/crowdin/be.yml index 2ec94afa7ca..43bfe94048e 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" diff --git a/modules/meeting/config/locales/crowdin/bg.yml b/modules/meeting/config/locales/crowdin/bg.yml index 26d85a3fffc..dda898327b8 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" diff --git a/modules/meeting/config/locales/crowdin/ca.yml b/modules/meeting/config/locales/crowdin/ca.yml index 36ca638daeb..d0e43e1f758 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" diff --git a/modules/meeting/config/locales/crowdin/ckb-IR.yml b/modules/meeting/config/locales/crowdin/ckb-IR.yml index 730f7c4f592..b2d43e94609 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" diff --git a/modules/meeting/config/locales/crowdin/cs.yml b/modules/meeting/config/locales/crowdin/cs.yml index 8d35d7a204c..758989888ab 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" diff --git a/modules/meeting/config/locales/crowdin/da.yml b/modules/meeting/config/locales/crowdin/da.yml index eecfc2d7237..dec53c9c5b8 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" diff --git a/modules/meeting/config/locales/crowdin/de.yml b/modules/meeting/config/locales/crowdin/de.yml index 6be51b44ccc..936f9c6f2f0 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" diff --git a/modules/meeting/config/locales/crowdin/el.yml b/modules/meeting/config/locales/crowdin/el.yml index befadfa9541..7526acd66d3 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" diff --git a/modules/meeting/config/locales/crowdin/eo.yml b/modules/meeting/config/locales/crowdin/eo.yml index 1057ebeda36..df1c07ad2dc 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" diff --git a/modules/meeting/config/locales/crowdin/es.yml b/modules/meeting/config/locales/crowdin/es.yml index a8485411308..829560f4eb6 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" diff --git a/modules/meeting/config/locales/crowdin/et.yml b/modules/meeting/config/locales/crowdin/et.yml index 7ee0cc1c775..8b05c51fafe 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" diff --git a/modules/meeting/config/locales/crowdin/eu.yml b/modules/meeting/config/locales/crowdin/eu.yml index d0c2d90bb90..90d66561484 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" diff --git a/modules/meeting/config/locales/crowdin/fa.yml b/modules/meeting/config/locales/crowdin/fa.yml index 0097823cf82..25e7e22066a 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" diff --git a/modules/meeting/config/locales/crowdin/fi.yml b/modules/meeting/config/locales/crowdin/fi.yml index 4632c04a073..5540dcddc17 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" diff --git a/modules/meeting/config/locales/crowdin/fil.yml b/modules/meeting/config/locales/crowdin/fil.yml index 303d561ad87..05a1d97adc9 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" diff --git a/modules/meeting/config/locales/crowdin/fr.yml b/modules/meeting/config/locales/crowdin/fr.yml index af09ce7e2af..88f565a698e 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." @@ -237,9 +238,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 +254,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}" @@ -536,8 +537,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" diff --git a/modules/meeting/config/locales/crowdin/he.yml b/modules/meeting/config/locales/crowdin/he.yml index b080e1de5ef..ae832af120d 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" diff --git a/modules/meeting/config/locales/crowdin/hi.yml b/modules/meeting/config/locales/crowdin/hi.yml index 0292c7240dc..d362be60179 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" diff --git a/modules/meeting/config/locales/crowdin/hr.yml b/modules/meeting/config/locales/crowdin/hr.yml index fd01e47fecd..6e197064ac8 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" diff --git a/modules/meeting/config/locales/crowdin/hu.yml b/modules/meeting/config/locales/crowdin/hu.yml index 25b4e6d239b..f238931fb9b 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" diff --git a/modules/meeting/config/locales/crowdin/id.yml b/modules/meeting/config/locales/crowdin/id.yml index fcf33ecb8d3..87e839ed3ef 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" diff --git a/modules/meeting/config/locales/crowdin/it.yml b/modules/meeting/config/locales/crowdin/it.yml index 64288513a68..4b6966b5ddd 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" diff --git a/modules/meeting/config/locales/crowdin/ja.yml b/modules/meeting/config/locales/crowdin/ja.yml index 56ab02acd4e..7b19e8340f4 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" diff --git a/modules/meeting/config/locales/crowdin/ka.yml b/modules/meeting/config/locales/crowdin/ka.yml index 908765f192c..dff9498ec66 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" diff --git a/modules/meeting/config/locales/crowdin/kk.yml b/modules/meeting/config/locales/crowdin/kk.yml index 138cb931532..6a6c5d17f68 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" diff --git a/modules/meeting/config/locales/crowdin/ko.yml b/modules/meeting/config/locales/crowdin/ko.yml index 2007da31668..0f9ef52f1dc 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" diff --git a/modules/meeting/config/locales/crowdin/lt.yml b/modules/meeting/config/locales/crowdin/lt.yml index cc11f3d0c9b..9880ae00b62 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" diff --git a/modules/meeting/config/locales/crowdin/lv.yml b/modules/meeting/config/locales/crowdin/lv.yml index 5d460399aa6..2ae8d08589c 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" diff --git a/modules/meeting/config/locales/crowdin/mn.yml b/modules/meeting/config/locales/crowdin/mn.yml index 6968ca507f5..b29cc67fad5 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" diff --git a/modules/meeting/config/locales/crowdin/ms.yml b/modules/meeting/config/locales/crowdin/ms.yml index 2469d8ec515..f5f45b61984 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" diff --git a/modules/meeting/config/locales/crowdin/ne.yml b/modules/meeting/config/locales/crowdin/ne.yml index 33936a539d3..800a3caf119 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" diff --git a/modules/meeting/config/locales/crowdin/nl.yml b/modules/meeting/config/locales/crowdin/nl.yml index 22249dc22d2..98bff768dc6 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" diff --git a/modules/meeting/config/locales/crowdin/no.yml b/modules/meeting/config/locales/crowdin/no.yml index 878a82c7f5e..16164b38af8 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" diff --git a/modules/meeting/config/locales/crowdin/pl.yml b/modules/meeting/config/locales/crowdin/pl.yml index 77d6eb59272..508c28c61dd 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" diff --git a/modules/meeting/config/locales/crowdin/pt-BR.yml b/modules/meeting/config/locales/crowdin/pt-BR.yml index dee51c6750b..7b585539dc9 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" diff --git a/modules/meeting/config/locales/crowdin/pt-PT.yml b/modules/meeting/config/locales/crowdin/pt-PT.yml index c06dec3fe7e..0e78e1c96cc 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" diff --git a/modules/meeting/config/locales/crowdin/ro.yml b/modules/meeting/config/locales/crowdin/ro.yml index 24676ce2e6e..da44cce76ab 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" diff --git a/modules/meeting/config/locales/crowdin/ru.yml b/modules/meeting/config/locales/crowdin/ru.yml index abc93647701..055ff3f9f21 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" diff --git a/modules/meeting/config/locales/crowdin/rw.yml b/modules/meeting/config/locales/crowdin/rw.yml index acef788a27d..626d2afb1f0 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" diff --git a/modules/meeting/config/locales/crowdin/si.yml b/modules/meeting/config/locales/crowdin/si.yml index 1d4544b1d16..bed1300411d 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" diff --git a/modules/meeting/config/locales/crowdin/sk.yml b/modules/meeting/config/locales/crowdin/sk.yml index dfb92a6d95b..99d4610909c 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" diff --git a/modules/meeting/config/locales/crowdin/sl.yml b/modules/meeting/config/locales/crowdin/sl.yml index 41bab3bd6a3..491cd259615 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" diff --git a/modules/meeting/config/locales/crowdin/sr.yml b/modules/meeting/config/locales/crowdin/sr.yml index ad52fc19467..e8ccdd6c26f 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" diff --git a/modules/meeting/config/locales/crowdin/sv.yml b/modules/meeting/config/locales/crowdin/sv.yml index 8acd1a72bfc..c4aff08b2f5 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" diff --git a/modules/meeting/config/locales/crowdin/th.yml b/modules/meeting/config/locales/crowdin/th.yml index 3d6e206420c..4fc4705bd5e 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" diff --git a/modules/meeting/config/locales/crowdin/tr.yml b/modules/meeting/config/locales/crowdin/tr.yml index 62c096202e4..d65bf9b46f6 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" diff --git a/modules/meeting/config/locales/crowdin/uk.yml b/modules/meeting/config/locales/crowdin/uk.yml index 85e6dd4c78f..0ba19692399 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" diff --git a/modules/meeting/config/locales/crowdin/uz.yml b/modules/meeting/config/locales/crowdin/uz.yml index f30483ec914..f131a50b775 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" diff --git a/modules/meeting/config/locales/crowdin/vi.yml b/modules/meeting/config/locales/crowdin/vi.yml index 4d87a0e5599..cba07c32882 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" diff --git a/modules/meeting/config/locales/crowdin/zh-CN.yml b/modules/meeting/config/locales/crowdin/zh-CN.yml index 94575700f97..503e521a38b 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" diff --git a/modules/meeting/config/locales/crowdin/zh-TW.yml b/modules/meeting/config/locales/crowdin/zh-TW.yml index a1996180d20..1466eec8375 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" From 61ee2340651e6e154d7e1af82fe199ec1e0d1e29 Mon Sep 17 00:00:00 2001 From: OpenProject Actions CI Date: Tue, 24 Feb 2026 03:57:07 +0000 Subject: [PATCH 55/71] update locales from crowdin [ci skip] --- config/locales/crowdin/fr.yml | 18 ++++++++-------- modules/meeting/config/locales/crowdin/af.yml | 1 + modules/meeting/config/locales/crowdin/ar.yml | 1 + modules/meeting/config/locales/crowdin/az.yml | 1 + modules/meeting/config/locales/crowdin/be.yml | 1 + modules/meeting/config/locales/crowdin/bg.yml | 1 + modules/meeting/config/locales/crowdin/ca.yml | 1 + .../meeting/config/locales/crowdin/ckb-IR.yml | 1 + modules/meeting/config/locales/crowdin/cs.yml | 1 + modules/meeting/config/locales/crowdin/da.yml | 1 + modules/meeting/config/locales/crowdin/de.yml | 1 + modules/meeting/config/locales/crowdin/el.yml | 1 + modules/meeting/config/locales/crowdin/eo.yml | 1 + modules/meeting/config/locales/crowdin/es.yml | 1 + modules/meeting/config/locales/crowdin/et.yml | 1 + modules/meeting/config/locales/crowdin/eu.yml | 1 + modules/meeting/config/locales/crowdin/fa.yml | 1 + modules/meeting/config/locales/crowdin/fi.yml | 1 + .../meeting/config/locales/crowdin/fil.yml | 1 + modules/meeting/config/locales/crowdin/fr.yml | 21 ++++++++++--------- modules/meeting/config/locales/crowdin/he.yml | 1 + modules/meeting/config/locales/crowdin/hi.yml | 1 + modules/meeting/config/locales/crowdin/hr.yml | 1 + modules/meeting/config/locales/crowdin/hu.yml | 1 + modules/meeting/config/locales/crowdin/id.yml | 1 + modules/meeting/config/locales/crowdin/it.yml | 1 + modules/meeting/config/locales/crowdin/ja.yml | 1 + modules/meeting/config/locales/crowdin/ka.yml | 1 + modules/meeting/config/locales/crowdin/kk.yml | 1 + modules/meeting/config/locales/crowdin/ko.yml | 1 + modules/meeting/config/locales/crowdin/lt.yml | 1 + modules/meeting/config/locales/crowdin/lv.yml | 1 + modules/meeting/config/locales/crowdin/mn.yml | 1 + modules/meeting/config/locales/crowdin/ms.yml | 1 + modules/meeting/config/locales/crowdin/ne.yml | 1 + modules/meeting/config/locales/crowdin/nl.yml | 1 + modules/meeting/config/locales/crowdin/no.yml | 1 + modules/meeting/config/locales/crowdin/pl.yml | 1 + .../meeting/config/locales/crowdin/pt-BR.yml | 1 + .../meeting/config/locales/crowdin/pt-PT.yml | 1 + modules/meeting/config/locales/crowdin/ro.yml | 1 + modules/meeting/config/locales/crowdin/ru.yml | 1 + modules/meeting/config/locales/crowdin/rw.yml | 1 + modules/meeting/config/locales/crowdin/si.yml | 1 + modules/meeting/config/locales/crowdin/sk.yml | 1 + modules/meeting/config/locales/crowdin/sl.yml | 1 + modules/meeting/config/locales/crowdin/sr.yml | 1 + modules/meeting/config/locales/crowdin/sv.yml | 1 + modules/meeting/config/locales/crowdin/th.yml | 1 + modules/meeting/config/locales/crowdin/tr.yml | 1 + modules/meeting/config/locales/crowdin/uk.yml | 1 + modules/meeting/config/locales/crowdin/uz.yml | 1 + modules/meeting/config/locales/crowdin/vi.yml | 1 + .../meeting/config/locales/crowdin/zh-CN.yml | 1 + .../meeting/config/locales/crowdin/zh-TW.yml | 1 + 55 files changed, 73 insertions(+), 19 deletions(-) diff --git a/config/locales/crowdin/fr.yml b/config/locales/crowdin/fr.yml index 5a68b4f3a5c..d8d2f7c3e7f 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,7 +122,7 @@ 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." + title_caption: "Titre court affiché aux applications qui se connectent au serveur MCP." update: failure: "La configuration MCP n'a pas pu être mise à jour." success: "La configuration MCP a été mise à jour avec succès." @@ -608,7 +608,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: @@ -624,8 +624,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." @@ -639,7 +639,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: @@ -1643,7 +1643,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: @@ -2864,7 +2864,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). @@ -4909,7 +4909,7 @@ fr: text_change_disabled_for_ldap_login: "Le nom et l'e-mail sont définis par LDAP 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/modules/meeting/config/locales/crowdin/af.yml b/modules/meeting/config/locales/crowdin/af.yml index 934ad58b7fb..a40cde2366d 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" diff --git a/modules/meeting/config/locales/crowdin/ar.yml b/modules/meeting/config/locales/crowdin/ar.yml index b633fbfd2ee..adf6d511f2b 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" diff --git a/modules/meeting/config/locales/crowdin/az.yml b/modules/meeting/config/locales/crowdin/az.yml index 897a70456c5..d337236b72c 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" diff --git a/modules/meeting/config/locales/crowdin/be.yml b/modules/meeting/config/locales/crowdin/be.yml index 2ec94afa7ca..43bfe94048e 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" diff --git a/modules/meeting/config/locales/crowdin/bg.yml b/modules/meeting/config/locales/crowdin/bg.yml index 26d85a3fffc..dda898327b8 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" diff --git a/modules/meeting/config/locales/crowdin/ca.yml b/modules/meeting/config/locales/crowdin/ca.yml index 36ca638daeb..d0e43e1f758 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" diff --git a/modules/meeting/config/locales/crowdin/ckb-IR.yml b/modules/meeting/config/locales/crowdin/ckb-IR.yml index 730f7c4f592..b2d43e94609 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" diff --git a/modules/meeting/config/locales/crowdin/cs.yml b/modules/meeting/config/locales/crowdin/cs.yml index 9491087af84..ab8c9766cdd 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" diff --git a/modules/meeting/config/locales/crowdin/da.yml b/modules/meeting/config/locales/crowdin/da.yml index eecfc2d7237..dec53c9c5b8 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" diff --git a/modules/meeting/config/locales/crowdin/de.yml b/modules/meeting/config/locales/crowdin/de.yml index 6be51b44ccc..936f9c6f2f0 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" diff --git a/modules/meeting/config/locales/crowdin/el.yml b/modules/meeting/config/locales/crowdin/el.yml index befadfa9541..7526acd66d3 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" diff --git a/modules/meeting/config/locales/crowdin/eo.yml b/modules/meeting/config/locales/crowdin/eo.yml index 1057ebeda36..df1c07ad2dc 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" diff --git a/modules/meeting/config/locales/crowdin/es.yml b/modules/meeting/config/locales/crowdin/es.yml index a8485411308..829560f4eb6 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" diff --git a/modules/meeting/config/locales/crowdin/et.yml b/modules/meeting/config/locales/crowdin/et.yml index 7ee0cc1c775..8b05c51fafe 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" diff --git a/modules/meeting/config/locales/crowdin/eu.yml b/modules/meeting/config/locales/crowdin/eu.yml index d0c2d90bb90..90d66561484 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" diff --git a/modules/meeting/config/locales/crowdin/fa.yml b/modules/meeting/config/locales/crowdin/fa.yml index 0097823cf82..25e7e22066a 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" diff --git a/modules/meeting/config/locales/crowdin/fi.yml b/modules/meeting/config/locales/crowdin/fi.yml index 4632c04a073..5540dcddc17 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" diff --git a/modules/meeting/config/locales/crowdin/fil.yml b/modules/meeting/config/locales/crowdin/fil.yml index 303d561ad87..05a1d97adc9 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" diff --git a/modules/meeting/config/locales/crowdin/fr.yml b/modules/meeting/config/locales/crowdin/fr.yml index af09ce7e2af..88f565a698e 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." @@ -237,9 +238,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 +254,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}" @@ -536,8 +537,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" diff --git a/modules/meeting/config/locales/crowdin/he.yml b/modules/meeting/config/locales/crowdin/he.yml index b080e1de5ef..ae832af120d 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" diff --git a/modules/meeting/config/locales/crowdin/hi.yml b/modules/meeting/config/locales/crowdin/hi.yml index 0292c7240dc..d362be60179 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" diff --git a/modules/meeting/config/locales/crowdin/hr.yml b/modules/meeting/config/locales/crowdin/hr.yml index fd01e47fecd..6e197064ac8 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" diff --git a/modules/meeting/config/locales/crowdin/hu.yml b/modules/meeting/config/locales/crowdin/hu.yml index 25b4e6d239b..f238931fb9b 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" diff --git a/modules/meeting/config/locales/crowdin/id.yml b/modules/meeting/config/locales/crowdin/id.yml index fcf33ecb8d3..87e839ed3ef 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" diff --git a/modules/meeting/config/locales/crowdin/it.yml b/modules/meeting/config/locales/crowdin/it.yml index 64288513a68..4b6966b5ddd 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" diff --git a/modules/meeting/config/locales/crowdin/ja.yml b/modules/meeting/config/locales/crowdin/ja.yml index 7b6ca09e7f8..8900676b652 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" diff --git a/modules/meeting/config/locales/crowdin/ka.yml b/modules/meeting/config/locales/crowdin/ka.yml index 908765f192c..dff9498ec66 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" diff --git a/modules/meeting/config/locales/crowdin/kk.yml b/modules/meeting/config/locales/crowdin/kk.yml index 138cb931532..6a6c5d17f68 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" diff --git a/modules/meeting/config/locales/crowdin/ko.yml b/modules/meeting/config/locales/crowdin/ko.yml index 2007da31668..0f9ef52f1dc 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" diff --git a/modules/meeting/config/locales/crowdin/lt.yml b/modules/meeting/config/locales/crowdin/lt.yml index cc11f3d0c9b..9880ae00b62 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" diff --git a/modules/meeting/config/locales/crowdin/lv.yml b/modules/meeting/config/locales/crowdin/lv.yml index 5d460399aa6..2ae8d08589c 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" diff --git a/modules/meeting/config/locales/crowdin/mn.yml b/modules/meeting/config/locales/crowdin/mn.yml index 6968ca507f5..b29cc67fad5 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" diff --git a/modules/meeting/config/locales/crowdin/ms.yml b/modules/meeting/config/locales/crowdin/ms.yml index 2469d8ec515..f5f45b61984 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" diff --git a/modules/meeting/config/locales/crowdin/ne.yml b/modules/meeting/config/locales/crowdin/ne.yml index 33936a539d3..800a3caf119 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" diff --git a/modules/meeting/config/locales/crowdin/nl.yml b/modules/meeting/config/locales/crowdin/nl.yml index 22249dc22d2..98bff768dc6 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" diff --git a/modules/meeting/config/locales/crowdin/no.yml b/modules/meeting/config/locales/crowdin/no.yml index 878a82c7f5e..16164b38af8 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" diff --git a/modules/meeting/config/locales/crowdin/pl.yml b/modules/meeting/config/locales/crowdin/pl.yml index 77d6eb59272..508c28c61dd 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" diff --git a/modules/meeting/config/locales/crowdin/pt-BR.yml b/modules/meeting/config/locales/crowdin/pt-BR.yml index dee51c6750b..7b585539dc9 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" diff --git a/modules/meeting/config/locales/crowdin/pt-PT.yml b/modules/meeting/config/locales/crowdin/pt-PT.yml index c06dec3fe7e..0e78e1c96cc 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" diff --git a/modules/meeting/config/locales/crowdin/ro.yml b/modules/meeting/config/locales/crowdin/ro.yml index 24676ce2e6e..da44cce76ab 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" diff --git a/modules/meeting/config/locales/crowdin/ru.yml b/modules/meeting/config/locales/crowdin/ru.yml index abc93647701..055ff3f9f21 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" diff --git a/modules/meeting/config/locales/crowdin/rw.yml b/modules/meeting/config/locales/crowdin/rw.yml index acef788a27d..626d2afb1f0 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" diff --git a/modules/meeting/config/locales/crowdin/si.yml b/modules/meeting/config/locales/crowdin/si.yml index 1d4544b1d16..bed1300411d 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" diff --git a/modules/meeting/config/locales/crowdin/sk.yml b/modules/meeting/config/locales/crowdin/sk.yml index dfb92a6d95b..99d4610909c 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" diff --git a/modules/meeting/config/locales/crowdin/sl.yml b/modules/meeting/config/locales/crowdin/sl.yml index 41bab3bd6a3..491cd259615 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" diff --git a/modules/meeting/config/locales/crowdin/sr.yml b/modules/meeting/config/locales/crowdin/sr.yml index ad52fc19467..e8ccdd6c26f 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" diff --git a/modules/meeting/config/locales/crowdin/sv.yml b/modules/meeting/config/locales/crowdin/sv.yml index 8acd1a72bfc..c4aff08b2f5 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" diff --git a/modules/meeting/config/locales/crowdin/th.yml b/modules/meeting/config/locales/crowdin/th.yml index 3d6e206420c..4fc4705bd5e 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" diff --git a/modules/meeting/config/locales/crowdin/tr.yml b/modules/meeting/config/locales/crowdin/tr.yml index 62c096202e4..d65bf9b46f6 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" diff --git a/modules/meeting/config/locales/crowdin/uk.yml b/modules/meeting/config/locales/crowdin/uk.yml index 85e6dd4c78f..0ba19692399 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" diff --git a/modules/meeting/config/locales/crowdin/uz.yml b/modules/meeting/config/locales/crowdin/uz.yml index f30483ec914..f131a50b775 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" diff --git a/modules/meeting/config/locales/crowdin/vi.yml b/modules/meeting/config/locales/crowdin/vi.yml index 4d87a0e5599..cba07c32882 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" diff --git a/modules/meeting/config/locales/crowdin/zh-CN.yml b/modules/meeting/config/locales/crowdin/zh-CN.yml index 94575700f97..503e521a38b 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" diff --git a/modules/meeting/config/locales/crowdin/zh-TW.yml b/modules/meeting/config/locales/crowdin/zh-TW.yml index a1996180d20..1466eec8375 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" From cbeaa519705839560ae3d72da8d29e9754ab8842 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Tue, 24 Feb 2026 10:20:08 +0100 Subject: [PATCH 56/71] Add comments and replace sleep timeouts --- .../components/grids/grid/grid.component.ts | 5 +++++ .../spec/features/my/work_package_table_spec.rb | 4 ++-- spec/support/components/grids/grid_area.rb | 17 ++++++++++++----- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/shared/components/grids/grid/grid.component.ts b/frontend/src/app/shared/components/grids/grid/grid.component.ts index 3ee731bec0b..400f1d0e071 100644 --- a/frontend/src/app/shared/components/grids/grid/grid.component.ts +++ b/frontend/src/app/shared/components/grids/grid/grid.component.ts @@ -120,6 +120,11 @@ export class GridComponent implements OnDestroy, OnInit { } public get widgetAreasForDisplay():GridWidgetArea[] { + // Convert a 2D grid position to a flat linear index so widgets can be + // sorted by visual order (left-to-right, top-to-bottom) instead of their + // insertion order. Multiplying by numColumns ensures each row starts at a + // higher index than all cells of the previous row (e.g. for 3 columns: + // row 1 → 1–3, row 2 → 4–6, …). startRow is 1-based, hence the "- 1". const index = (a:GridWidgetArea) => (a.startRow - 1) * this.layout.numColumns + a.startColumn; 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 5a94101d2b2..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 @@ -79,7 +80,6 @@ RSpec.describe "Arbitrary WorkPackage query table widget on my page", # This one always exists by default. # Using it here as a safeguard to govern speed. created_by_me_area = Components::Grids::GridArea.new(".grid--area.-widgeted:nth-of-type(2)") - sleep(2) expect(created_by_me_area.area) .to have_css(".subject", text: type_work_package.subject) @@ -142,9 +142,9 @@ 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)") - sleep(2) expect(filter_area.area) .to have_css(".id", text: type_work_package.id) diff --git a/spec/support/components/grids/grid_area.rb b/spec/support/components/grids/grid_area.rb index ef0e6f9504e..ef59dcc5dec 100644 --- a/spec/support/components/grids/grid_area.rb +++ b/spec/support/components/grids/grid_area.rb @@ -7,6 +7,12 @@ 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) @@ -18,22 +24,23 @@ module Components end def logical_start_row - grid_value("grid-row-start") / 2 + grid_value("grid-row-start") / CSS_LINES_PER_LOGICAL_UNIT end def logical_start_col - grid_value("grid-column-start") / 2 + grid_value("grid-column-start") / CSS_LINES_PER_LOGICAL_UNIT end def resize_to(rows, cols) area.hover - # rows/cols are the desired SIZE of the widget - # e.g. (1,2) means same height, +1 column + # 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 * 2, target_col * 2).area + 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 From 211c9737d14e92fd03ce8f1a1a2767ef0a78bf88 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Tue, 24 Feb 2026 10:32:55 +0100 Subject: [PATCH 57/71] Apply widget menu label to all widget menus --- .../widgets/menu/widget-abstract-menu.component.ts | 4 ++++ .../grids/widgets/menu/widget-menu.component.html | 2 +- .../grids/widgets/wp-graph/wp-graph.component.html | 1 - .../grids/widgets/wp-graph/wp-graph.component.ts | 4 ---- .../icon-triggered-context-menu.component.html | 2 +- .../icon-triggered-context-menu.component.ts | 1 + .../components/principal/principal-renderer.service.ts | 10 +++++++++- 7 files changed, 16 insertions(+), 8 deletions(-) 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 679debac91b..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 @@ -4,7 +4,6 @@ diff --git a/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.ts b/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.ts index a42fc743f6c..2ff6bebd2e7 100644 --- a/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.ts @@ -85,8 +85,4 @@ export class WidgetWpGraphComponent extends AbstractWidgetComponent implements O public get chartType() { return this.graphConfiguration.chartType; } - - public get widgetMenuLabel():string { - return this.i18n.t('js.grid.widget_menu_label', { widgetName: this.widgetName }); - } } 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/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; From 650afc6bac6c8123c4f8303a026da9f54557962f Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Thu, 19 Feb 2026 15:51:27 +0100 Subject: [PATCH 58/71] Add autocompleter and allow template selection from new meeting form --- .../meetings/index/form_component.html.erb | 6 +- .../meetings/index/form_component.rb | 14 +++++ .../app/controllers/meetings_controller.rb | 18 ++++-- .../forms/meeting/template_autocompleter.rb | 63 +++++++++++++++++++ modules/meeting/config/locales/en.yml | 2 + 5 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 modules/meeting/app/forms/meeting/template_autocompleter.rb 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 04609953d6e..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,7 +35,11 @@ end end - # TODO: Add template autocompleter + 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 unless @template modal_body.with_row do diff --git a/modules/meeting/app/components/meetings/index/form_component.rb b/modules/meeting/app/components/meetings/index/form_component.rb index e086ea53060..a5acd44b608 100644 --- a/modules/meeting/app/components/meetings/index/form_component.rb +++ b/modules/meeting/app/components/meetings/index/form_component.rb @@ -69,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/controllers/meetings_controller.rb b/modules/meeting/app/controllers/meetings_controller.rb index 4253b11d831..7130a006f45 100644 --- a/modules/meeting/app/controllers/meetings_controller.rb +++ b/modules/meeting/app/controllers/meetings_controller.rb @@ -434,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 @@ -449,10 +449,8 @@ class MeetingsController < ApplicationController @meeting = call.result - # Load template if template_id is provided - @copy_from = if params[:template_id].present? - Meeting.onetime_templates.visible.find_by(id: params[:template_id]) - end + # 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 @@ -543,7 +541,15 @@ 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) 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..1d6970808f6 --- /dev/null +++ b/modules/meeting/app/forms/meeting/template_autocompleter.rb @@ -0,0 +1,63 @@ +# 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" + } + ) 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/config/locales/en.yml b/modules/meeting/config/locales/en.yml index 657fedf0f5f..7b46bbe51ea 100644 --- a/modules/meeting/config/locales/en.yml +++ b/modules/meeting/config/locales/en.yml @@ -131,12 +131,14 @@ 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" From c1cc3a42d2788466a78a688895e19ca5d468ecc5 Mon Sep 17 00:00:00 2001 From: Mir Bhatia Date: Thu, 19 Feb 2026 17:29:57 +0100 Subject: [PATCH 59/71] Add specs --- .../forms/meeting/template_autocompleter.rb | 5 +- .../create_meeting_from_template_spec.rb | 265 ++++++++++++++++++ .../onetime_template_crud_spec.rb | 4 +- 3 files changed, 270 insertions(+), 4 deletions(-) create mode 100644 modules/meeting/spec/features/meeting_templates/create_meeting_from_template_spec.rb diff --git a/modules/meeting/app/forms/meeting/template_autocompleter.rb b/modules/meeting/app/forms/meeting/template_autocompleter.rb index 1d6970808f6..e236a3d1ec5 100644 --- a/modules/meeting/app/forms/meeting/template_autocompleter.rb +++ b/modules/meeting/app/forms/meeting/template_autocompleter.rb @@ -38,7 +38,10 @@ class Meeting::TemplateAutocompleter < ApplicationForm decorated: true, defaultData: false, multiple: false, - appendTo: "#new-meeting-dialog" + appendTo: "#new-meeting-dialog", + data: { + "test-selector": "template_id" + } } ) do |select| templates.each do |template| 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 index ebe3d57d570..41b8e74f5b9 100644 --- a/modules/meeting/spec/features/meeting_templates/onetime_template_crud_spec.rb +++ b/modules/meeting/spec/features/meeting_templates/onetime_template_crud_spec.rb @@ -30,9 +30,7 @@ require "spec_helper" -RSpec.describe "Onetime templates CRUD", - :js, - :with_cuprite do +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]) } From 9409fa973000d97b2595966a57192ac62230ad3c Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Tue, 24 Feb 2026 11:46:50 +0100 Subject: [PATCH 60/71] Adapt tests to new project overview title --- modules/overviews/spec/features/navigation_spec.rb | 6 +++--- spec/features/work_packages/work_package_index_spec.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/overviews/spec/features/navigation_spec.rb b/modules/overviews/spec/features/navigation_spec.rb index 8d63417c886..4a5680a2e38 100644 --- a/modules/overviews/spec/features/navigation_spec.rb +++ b/modules/overviews/spec/features/navigation_spec.rb @@ -50,7 +50,7 @@ RSpec.describe "Navigate to overview", :js do end within "#content" do - expect(page).to have_heading "Project home" + expect(page).to have_heading project.name end end @@ -74,7 +74,7 @@ RSpec.describe "Navigate to overview", :js do # Expect page to be loaded within "#content" do - expect(page).to have_heading "Project home" + expect(page).to have_heading project.name end # Navigate to the WP module @@ -90,7 +90,7 @@ RSpec.describe "Navigate to overview", :js do 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 home" + expect(page).to have_no_heading project.name end # Navigate back to the Overview page diff --git a/spec/features/work_packages/work_package_index_spec.rb b/spec/features/work_packages/work_package_index_spec.rb index 529ba76db61..da3f57f3c1f 100644 --- a/spec/features/work_packages/work_package_index_spec.rb +++ b/spec/features/work_packages/work_package_index_spec.rb @@ -62,7 +62,7 @@ RSpec.describe "Work Packages", "index view", :js do visit project_path(project) within("#content") do - expect(page).to have_heading "Project home" + expect(page).to have_heading project.name end within("#main-menu") do From 238439fc23f9b62a6e842c5fed7e4c1db884af9f Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Tue, 24 Feb 2026 12:17:50 +0100 Subject: [PATCH 61/71] Move context menu out of h3 tag for better accessibility and focus the context menu trigger after close --- .../widgets/header/header.component.html | 34 ++++++++++--------- .../widgets/header/header.component.sass | 5 +++ .../op-context-menu-handler.ts | 10 ++++-- frontend/src/global_styles/content/_grid.sass | 2 ++ 4 files changed, 33 insertions(+), 18 deletions(-) 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/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/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, From ca4e3ff286c0038c6bd7593e54503f42e5532d8e Mon Sep 17 00:00:00 2001 From: Maya Berdygylyjova Date: Tue, 24 Feb 2026 13:55:27 +0100 Subject: [PATCH 62/71] =?UTF-8?q?[#71042]=20Update=20portfolio=20managemen?= =?UTF-8?q?t=20use=20case=20=20https://community.open=E2=80=A6=20(#22060)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [#71042] Update portfolio management use case https://community.openproject.org/work_packages/71042 [#71042] Update portfolio management use case https://community.openproject.org/work_packages/71042 * update * Apply suggestions from code review --- docs/use-cases/portfolio-management/README.md | 131 ++++++++++-------- ...penproject_use_case_portfolios_filters.png | Bin 0 -> 205969 bytes ...t_use_case_portfolios_filters_detailed.png | Bin 0 -> 52971 bytes ...use_case_portfolios_hierarchy_examples.png | Bin 0 -> 129439 bytes ...ct_use_case_portfolios_subitems_widget.png | Bin 0 -> 102691 bytes ...ject_use_case_select_portfolios_module.png | Bin 0 -> 205213 bytes 6 files changed, 77 insertions(+), 54 deletions(-) create mode 100644 docs/use-cases/portfolio-management/openproject_use_case_portfolios_filters.png create mode 100644 docs/use-cases/portfolio-management/openproject_use_case_portfolios_filters_detailed.png create mode 100644 docs/use-cases/portfolio-management/openproject_use_case_portfolios_hierarchy_examples.png create mode 100644 docs/use-cases/portfolio-management/openproject_use_case_portfolios_subitems_widget.png create mode 100644 docs/use-cases/portfolio-management/openproject_use_case_select_portfolios_module.png diff --git a/docs/use-cases/portfolio-management/README.md b/docs/use-cases/portfolio-management/README.md index eed43032d7c..7602ba7212b 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 + title: Portfolio management and custom reporting + priority: 990 description: Step-by-step instructions about portfolio management and custom reporting keywords: use-case, portfolio management --- -# 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 0000000000000000000000000000000000000000..2e7941cb8f3507cfb6f5c5d9c74d1673766161b8 GIT binary patch literal 205969 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%0Dsdogs?S0b=PLVp*Gjy;(@mBs!}fz7ho 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%0Dj!Emzc-)i8PpzKGz>H4m|!4lT@e{_N_k z`ovw_kvbE&XfJi8!2Ugs@~VKkMlN>FYk#)_VkXrK)sHBI0q9 zTCRGS*lymJ+Obsj%*W9i$is?j__ShShPOW(yo`KZ#@!Hxfm*!Dok$&U&IP%C-B;ZP zXYfbM2c1UQwohmIu(b(k1Xcwz-pzpWID9uUuHt^#1_pz>BT2JD$UH;{!m<(#jvl2T z_NO;2E^EDhpFT>SfDFlZ`Jh9K+*~ibhMJvZ@8iD565T_%eSJCAPuSy{OlayDS z<6pZ9497v3>!94AlA?>R=YM8$_zd%3HqtO`4GLYr&Ds)>Xb?xLdEUURM$hEctj~-D z>ltO-IM7AwsAu0+!y<=>Dp=GcscdE9vKG=k)gIuXMCY0`NE?lCoXWS9d{S*g^Lh4e z!8Av#z%~Jo4E@qgW~>mBbFa|@aGx>LPH#0S?9ih+``{l#oE|Z>h3IBuD=DK}Z}NOs zhs)AEiRDkSG!LrBmikRVB@yf+5F)ED%isB)-}5!$l&SY9votDS^Ok)})mlpdC#l zlAAix0Hjn=S#APz)}=mHOkG1Z;|C~Df zVsdjNBV}SR$pm)FX#!H+_;e-ZrJXc>h)sDjo*Ptq{5Z6qk!yRX;50>RS|!IA&pk0d zKf9WE{BT;{y?IH@PH)Lt6r0|tCrS9`9%KxBiSZ^QQF6t+2Ucp>f8PYiAlN-?Y zIskbop?6EwZuiKxvyFP@Bk2>n9Ol)o^KL^1$Nbg}|A1?NsK40!Vdg|CWCOCxcmfk^ zsbbZ4n*cKNPpUYt>myhOw$(%qS)YA9nQhBob>PTlw21uHvdR{M52B)-v{vb zLO}v7(hs{SJZ*e#wt1G}Ex(Lx53QXx`-}@K$TzK^) zEqyeZ)oW*Ak{fE$ASwC&by*%whRgIULK<~ipn}zFQ8w^vNnXb@a<`wOAk z&9*Ah@QL*T`_2)x+Sb8{>Je*Yh5g{XtSSI1>?zc{5e#J=L=xA&pqk;{a)`2#()jZ_zAkG_ z4=eT=tup=7WI!c(8{!ak1ddJ2o7t8>lEk0^&N?fIWO}<_iL@a^}NZqOo<3W$iBIDi;e8^ss=tMLC~oZa9Jx%#=!r+ z=CJ8V$oFP5ksYJ+gdrw3Dhm7N`kI%(94z>yKy7h(q~c9G>3gq1&7r#sjWk|2IX1Sj z2*@>7e>_v&U4NW%VnOi-$AsP6v`TMqn(UI^L`_5a9BEgJZjwH{mezwPXy zrQYh}xjB@o(G`jxOC8Uop%9~1q(T$I{9lDSMHr^!?`pmPh`6EscRct%+1g3}S3igD z@Beo>D#5K4hEx%UeqT%tPO!`O^fyUP=G&j2%KA>a4_nSU=cbu+dP^AyPTX4C)>|=3 zVxD9;t2@Ws+S~)Zr(pZ%J}qZP_AOjzaX3}?AMK{D6PMJlBH{gCeE(rAfP>kiMzs(p zPc#j#Yiv2LMSl09RWz#kgJl!k+2!q7_~YO{rgrAX>31bz#?|TsgBjI4_qgP=_@O!9 z?}O-i#ygs@RZVYPg%Zfds`%ssPfzqJ4a9NA=R5UhKrG|m zp{uukoJJkcbAfzS0Q*4_{SPN*M>T1roQCv7O!ulM&=;YF z9x3+=0SBRO!#B#y_5vkQ7O5L+dE-}th11#wlh+k{Cbd42u}yI*Q1PBLtup&Iovae` zPCV;GUFX;8;XUvI4V`$U(zpX0SHCkG`P3%W9x>xMim(Mi^rCK6*4>ieAoiJMMq6Qj ztubzCmIk%LaVoEelI~MBjI=w~dgQYA7wzp|tURHT;kT}3VmQyvp_8v6J7ugF=U6Df z^3A4{Ng7Ys=}T3}dWbj(b}k9kGt0^MtI=#?`9rVI(yy2Mi_8Pm#0MNmTnHW_dRUV? zsN`25KPm}9ZYd)(9k%Rlk38?`Li0KSYfMhYAdZN&-u~P+9NfKdO=2TuWgcH3JT8lr zu(OwJU2|mg%Z7jlpI+6VMgK692nlX!bgP*{sM)Ji-?kz5C=oJ@3~U{);SP*Q^=C{u2QN}Z>iu$m}T;min5W& zJn@}fzv_%Zq|l;t2V+IPZ5Nq1b6SvqvC z)`xVrSs#c_51w?HIgqCwTnoG;`5>A4TL5_55z*HZK2>T!Yqi2kr!wd7bg6lfQ7dh} zI6zZVrY9Ib?E8sML9zfX4%gLY?0JijPc0m+vQW25a4^U198pb+Q~}x~BzBQ+w~v-4p6~2Q%UifAF@n zEfJe*Q}%r-H2KgLl3SQsN~TlrgPe6Ns5L9VC;rh4^*J-+bIN9lY#*JGXwa$b37tWN_?UgOsOm5z>^n2Tk} z>pjp;S3~Lhjb){mXoR7{_2T@vBv<}&;5xm8wYyqR4-4>m~-1HOvi#K!=q|o7QfNb(h%ER zZ|?5yB7D&U#XC23-nSV$V)BB+!!8+m3qa|`1}Y6Q8gaK@T<0op3$wx8>MxJ1sXmX; z*lw>DsFyrbqxc|-p?#iA3rFmGRI)TlXI}&Hh3Bc&iUkl=Ta>~s@3kAO%W6Xsr#rdO z%*gm;(6sO3g_g;(9y90iq=2<&RZh#abxL$J#vwJ<)xzU(lC!~H$nL6VAiutz67ZcQx7B?DZY8%(u1xxu6hYe(1rP4=6=lK6-^+@o&p;s0y{*PJ z4;D`^>P1^~{$p@3+(Ah>DAlTWCh=Tz$ca%WKaO#_7_2UTr>bv11Fz9`T_`ARbeD|bQ1tIVD6b0_Ax?5}8$G9uJt zh3+rR!T|A1>t&71P{yl{i#cCL%^&a~U{#dkSLcLMV{{p8}e=XNmTrQ(^gT;_Cmqjn9oE8%sK_|c^e(s$gua6>K~HoNmgjW zUS+j_OE0uY;}yt|$g8iddfXM##x z8SjEA=Z7;mkLx%XZzloaJF_B?QrqQrS;;B;2?NPoTKNH{$gU{cZC0YD>*}8Y!zsM= zU!euM&KE$7Wg^5e28p|>KvHO89L*W#zuv4GN$FDU`p&i?E z{UctV{QU$Kd+d0LZVMBN1|K-^CMRso>E&M0@PQu0{Z0qhia8It$RQK(LrV$MCXYIm9+;=eN0RY` zbslI2@rQhxz2Sj`E58e);ogWeUqKQ1K@;YKZX?qIV5~da7OS zJ!v*t3|i6|nd>GeLYl63i!On2{6leT2`N1~8T-T#bDw?(APlDvEj{gYyYMgy2S~@b&z4FU>ituh7v?_9QNW zzPubh9F~T7ETt}toTgLIj(POk5yoScxhSr1wcC@C&%!K1OQ%ls!af_tHjTwJRNmlQ zbo(M5XpcERzHtf~W+R2_CZsYwvRHZkV(ivnr{Eiy0Krb-+q z%QQ(ob@B-|Kh$NX*2Cb-$QMxclwoM+xmVuQQrJW%RIw-@rWuk;|4o~@j{DY49i^pR z*?-r6inHZM(oV%6UjHP!kZ zWh~^ydq!@3_OL?fx(KgU?*a^RJt%&hp(e1@*&uvhTMvmU(xUGwc5*M=kch**?YD}) zT2kpp8O9ATjwMzssy#kyG7D8}mTp5?IN6BVG^|wQiyX1qAYEJT`F48>sVa1%KQA@y zPn$(W%x(%1J~3;b>=M#MLIM3MlR>AFkBywumync62BySoDwwy1(YX9m6nBelp!nty z7%V`Tw>kZ>hjA|Ml%V&nQJzU+%rPc(YRoxV-#)U?&@c>x%5%Q?2DpPP|1Wa^B>#dl zVKLB|s%W1<^pvYbs~~gEg&C`OpbA1Y?n%MR(s)b5QHDm~R4mQPLOWMMuc{9Iq>_CK zv?xwsCISPIBaux7W6#uh;;n9)g<$*Q;Rmz2T&D(*R^e8a3|2RWB~BVG(;6wXwrtr5 zlzzj+Qr&oyUg(-;?5FAB4#dV3>-l9(Bm@i$*4q@fx?}NR=>z9J&cHU7T_1hOrczIe zNn$DeAm?U$n}|%b`8^S%efad!#_oW7@qC^`9iYZG;iS+(Qmbt#rVOho@h+9Cf5Qu0 z;^e6kz7i2W7Jhi}zjhTju`&{@k<(mBZje1P{c&LJy37VuINmXFEs|_<6+?U~XU>^Z zzhC2{sh~Z!8!TQ;8jR<7`aO_+Msakw6U~wSidF9;yBMAa1z(j9&oetl$T8{#@l7My zIU8P;by#guOc6VyhT8ZI)f}NGZc`H~T_iFe!n<*5SX`8SNCl)aR8KbCNF6k-rIPJ3 z{(;v`DTH)tMZrm~DYAQDQ0Jqm0EghSy2Eh>(oh+HH6*gh4qjFLO-4|?u^#f>eW(=L z_>!KqwF7_XobShN6Xh`+8dKdp`Vk2$^}bIhA#N7tzwPd8gcbB_h`^=R$g05w>Y#5j znlJSPJ=H`bT&GLxIK%04oVuvj*BlT2Kla`;s)_dP7sY}FL=;7ufQS@BlP(~j^xi}0 zsDMDI(tA_{q)QFbdq@Hay%z=PQbXtvkREyoy`1Rt{NMGyYoE2(I%|J9ANG7p=FVj1 znz_qd*RPzk@72bu9I3R`%!z*$$UAngbU1MzRWrZ+Z^)vge_SB~=-&Yon5M8V%s=h- zO(FXIqP9&aBnN*QhmB^V1H|B;g|MOG(vWoh5C#CY+=|`K%iJ|1S>H46eLzlt33H6d z@-18;Gl(>gT$+5!+%({U?8l~y0{7Y5Ki*98t7lY6+VWV-bMCpk#T#|z75nYb#UjI9 zlw-*d>T{(o{Lyy9<&m}V`$3lBU)0Hk4^ny5C0ay@Wv`E+E=Ut|^_|?>yO}mD-V8b> zYHIm8;S2)DSu2cN_gt zw0v0GuL*NgFm~*emC0y9#&8IiJBGT^P`c>0igErYgr_-w4K0ialxH_pHilQT?h0Fn zOCE{IV+%d*(%rniH!&F(=D7^TJ=g#hw(R~p7`==wlnuGfi2s{C35$wKge~=;)cjok z-rEU>MZKa4Ol+qPo#>~r|42$S_*k{Ixa*ICpvyzo+R9pMX;DAH2 zv}=C8Q^S)gK$`F=ggQZDDoVGkpc-PvI8m%xS5-7pdj(3Ja0eB*x`^-I6a|!kfi!k>r`H_GPt9{#hT*8oXy)HHrm;zZgIUaw^^7;i?vCQwNn?n zz>9fFso#@v?xPel`QTt@Cwe+_Y*phaH@gvZzX&6W;~g4S^*E};=S@eFcPxx+pct!s zuC=>!Wg)!#z*DHeW4i?f2Wc7}d6S`Df~)X)ySw;(Q!UDd69m)N`+*85i*n*y{WQZy zQ)5jNJ->lAp9%~41=WU+#Pu+?LJFBP_&_$88?dpNXTjiU*H=j}s=6r6rfPO`K6~MR zbY@-(L~)N~-hr$AyIjnAVWrCH*zUpjyiu|HT)dzkDPk&BV@m$6x4)b%#0q};f&R$Q zl2#{uhmE?mMGJrnon2*oRP@%lu^S9l-m zIpDslO!s2%ps4Gxos*o|6fO^)zvq~FYIt*F2aA!A%>dQyc;EMa8@Je-l!H5H>%M>= z2%XM;QjvZzhHzoTVSbJzBXt^S(?*L;a2n>h3n4l2P5Re~fUHL18W~1y)UM9|6g{Bo>y!`e4j+18DY>KfCeRLQGM;|;Sv`wcRk z=6esXGLqiv4;CvOcfpJ2+{aNKo>8)sn zh|3;Ni+DFF^>B)(K+eV7#GbSwr^UjN^c#HG^+AyCv}5LF+h&}-Rg_ysIKeW~2% zhrNH9S4b~I5VqLYJaSz}-_cO)pmRq!;1x5akB)kH9l&zQ#b3fAaq-msRbna6-p2Ad zUVBYMv4U6Gm{z8J^0+~-%9XhRJLJ;fz#D)USvHjfA&Y;ho>W+F z5UIGdg?{Qj;M4Aeg~W6Der=EqpI)!fTr{EcjD z78aoYF>Axf6Cf>quh-_7n4Su0C{c*m$P`H2t9tzJfxbhq|1(?tKbWikpAysmTLRvH z=KlZiAOCxz=Rf;MQvWGC{O8o*cmCqD|NZ!XcM=2fe;NeOkEXtqHht%^QLX=p`lBm< zlh|*r-)0^-)NC9aRW`S|$a>ih{!fFM@6XgRZroQ%9!Ug!{@l^t-d^(D_`iS#8gGU^ z_vu{T03-lxX6n$L&u+E%eDKwG-ja``mDK0bN9o(!7g`W_-g|n2kcmBnA2XK#zgJfc z{$e?>X_=mmf#cru&6&sEO(UL6L&sT>blsJc9T<#N>eJMa7O!95$Ofz z7Pgf^ld<<{XkgNbr||PD)6ky8<^LRRhPRzK+!4u+a-NHA8pkre`^-8YAY$KQZ<8|V z5&#K9=!5PE{Iysh(hvk}w#-X+wkJ=PVa$!dxnM#W+wyd|H`3=64YL z%S>5Ixr$M&!(%do>8mVD$7x*N7{MNv&tjyGT{H~|r>AVft`*Y>{tbV=0c#pco*adP zHnM zyyLw~1D-w&vPD6+ww(Qs$7sa8mYusQyjI^*ADZ%Ei%w(Tag4?jkW^x;h3{?$C`$VH zqp&wazokQU%M@0=(S)qDTZpioPpDox_~4ahC;~(`>;&nzO4Q>t`Lu8#Vqu16TBk~l zS(eZ;aCNNuAJK%4yMG?-`OI!mbNL&L$e9SFgdK8TbWA~qylB`#O*0CqoZNnx_{807 z???Py#K^ht=EoKv&n?cg%>+-ZQ-^{2`Kj@rUy0a{=XZk!ulaf+iKj0)R+A$B0t;+9 z6;~zR`;iSvV(`w68*}KS{n6i~FPvN2KLq$&mKc;fv`2B1#^1bqU3j;FL$B#b^;qhe z#>MHa=g4SwNVYPw=0IxAuCB~F7pQrt>f%wu6f6VvIVjIw!gs?@{d`_fJ^6G+ed<|^ zeqk@_)TFPf5Pe<2NlU3-(A1Qg(3Z_eHYqM2y|iVsu_ermoM<8Jb**`gIaVMy-)q-B zu$}*UDY<-Df%)9dy21r7*C1x!7{&f%`e)P4ZL+Z%xqH`c4}UNuaxqx~@68&@5NWHx zb$U9y7FE5e zb(ufq>Tb1YY2%u~!`10vJqjgnjYz~L@NhwT*ztqJH?%5h<*UDH$p187yl7O*&^%H0 zm!>T`{QOy{y_VrBDe3Bw8G1P+Bfncny~ns%i9<6zwk{>*@@0T>Cu=zyoT!oq*19-{ z_ze}9nZ^?Roe~xe=nTiJ8WF=AnU}~v&4b|8rEfg!CR?*((V@k~tD*sN@`|uiPr0Ai z_!QR1tT~p+`OVEOWo|8U;_Zz(AtxK4IH(j>+*qD;Ui9ZeLJ7I?#n7+?h}xBWD&h$= zYE644h_-dQtAgzMQiWZgm}yj{mE^osm6|M$zF<0o2WyssR`Ghb6ToL+tl4y2Kkc0Z zop$%CH|f^p4|PNwcjO>`r3G9lN8QAQ+5bCsx0C)S;kczIq zFGU@%{MN6LJi6X+qQD14>Ai||Tbv{{ZKTToEg#-N1P(YRM`6Lh(6{o`4<2L*F7YZ6 zNzSMOPcsNhyX>5CiQU0Qu5IT@J!{J|9fjU$RHfAU3Omn9Aq-FL+U>PBCH?Iptm_Cg z?CMHI?Y=D9aIPhJ1iGQMvV#RyIPj?((PWo*`!f$$>jQz6MWE=#?CF z9WNzJ4>h|>+scKIEY#He7=V}l>#Yf;9sinIK3F+%;QVJ$9M+lzAltpClt>J z0}Bk~`+7C{TM5i?*E2jo_xzQ=*E1F?hn3GW6v>0g1~3qvKiD~<+Ea;M?RKDH9L|s` z+;%`9O@hpl7m9nGWwx%VdzUnT_28*~kwRGA9MEcng>S6LaBZL2Kp+7O_amMouQ~9N zJJax%`c0J=hcQN^dRSTWbS;@|xbPKlK|O>RMNP=Bx*mdi-Q4|B-$sS~RhYGzm+F++ z8WI$8p1ENCn{oO?`FF2lKZoo!_>!s%Mx+L)D&Bi{iK7+!^aGhwIpQBZKei)sda!Y~alWW-`yaV_<;Q;A=g}kES6rX^f zjcm!!EMMw0HH?DLQRzjDS)p_$kZtj@cfnG$&tCTJ*LpDC8tC5?T`h0%Js4UW@Y&CM zLx-YHe=}unzFGst4JTo@$)?J*SdaI#qs>_NcDHjK>}fof+qYTweseWo*Mq9|_K;Wh zHsYJySy%%5Ze4$GEs95Kvkq|`)l8Xfz&xc5k-GdVu!JhpF|;}Rtr2oQ4u_MbD(N8* z1215!Dpv#oc``jqc!J+a&r5#p=G^5ww*tDpJf(R)bgoKAVEHpN%`|-=sw#YVIFJ9G z9nfH|PXH+EdS0AP`1Kf83w2{S`V6b}q|d}PiAXM^g)scdVz`T|yZhuC>b2%Mb&d0E za}-d6ZFWXZa{NuXtRuX~nSho6g(c z6}AhSc5N7*y_u8n`!+>c*4J-gl0Nn!)k6)|lg1P&%Nby%wwFV-dLZy1Ccg_!#sdUm z!-?2tM7-nRCk(&yQ;+l={jbs3XbAiIHl=^IBVRy1VZdtdCs&KmyP;K|Y`N6RfP*k` z&F5cV^}DMvOK*P)C>Pe<%tFI0K0s0S7c!>FTQ7aywhCs7aU*LhXC=p95g}$!Jf#1qz@mY>h9)}LlH$!2o1ZX2RQT3*P zRa{S^-or+#`0)xq<$Izg`X$4B;JuDFK2>(Y3ldc-7IiPOm#nX!?H>1Lh3}2$pqJ;G zEPy$_%#ln-wY4DEk&R6=ObfbKx0nY@@b=D|cG&%po>s37^)p@}+$CPrOc{8|39nOZ z`M_JtKZFvvnIB|Xp+uc=`Z7o1_Q3Fkl)-Wv4CH65?bzF4+CHfG(COw*1`8< z&zd#-MSsioyl_-?EedfuRd9+i!2Li87=rLPVfsgs!u5AsQ2PIpISSBQw26SaFZ z*8$_Oe5UnrZAoT$UUMbpcz5^d@xpi``7}(!R06-%)Yyl8pIv< zd%XQ9MJuTYG4Cxp8JB{mYl@8clUh_*4?m$eUr2hZBP`%IC+cH8WPirEhH#-P=jT@^ z7xV;ATOThUL`+ES%8duaYXq)!>V>CGGG73f`6tp-vN_)(>p*A5_Giwzx;+n^In0+wVPNKk1GB|f$9jI| zLIgD~P(N59!(p!aN21;aeeQP12LVjAE*q(+PSLUbFB6A)8(?c2CwT2$9 zFA=O+4Fy-OWG>Z8W+vuIx?AC>BI!kdNLdu_2~lT;s1&nV9f~H3n9b-{*H#y7rtl&3 zBN4H=HQ|Z)s-y4={{xt~S4H2agbKyM64jqoFNDQkn8c#oPFKQOyo8zZ<^WQ^?IVS4CRX zR00ag``9O)hmt&5-o=f8kBos*>nbi?{5^MoT9MV zeCayccJb$jR-wPwSE#OAE>6mv+575tgL;M)8zhi+eNxBv11kO_B?S=}obCLtVmDT^ zX!Q$uDAR=!(qnxjG>1#5AQ@R$SwVk(%qNZ?)|%5t4>U(lYgO@Tu0&=@gV$@H`V<;O z3{z|%>7bZspN5Ty#x=r6eIK`>J_hJ=$VW+RQMVnv)Es3`Kx6q^(Wv19U})!hO<@LZ zE$3)ya>#8@fS4=RXsBa1?YsAmdw?UlrYrBImEW7sUC+rkxrb) z(|gCwe4s7TLsD$dKhkGKyTW$NXv%yQ7LBVm4~pnE61Pi3nMsIMN+wJBHa#{u#o7&> z;75vz#>?$t>1Wd;^VVcs>}o0p+v)A*Y1gBN+OO2Yb&>4gvqKC$!Ns4t)MPj;&)2eQ zf~`*nF;W<_!S~HL_)WpfpkVMoeTh*;LE&R!Qq%$6c(bGf8l45^GneKugr-slaf#}MFH-CEgFBr0vc-Go;lS`V zuoiEhx`uqZd*ymKVM$DPVWLu39!bb=v}Yc5R>I=w3$^(q5tTd>D+?@VLk-z=5nmmQ z-hWTu8M{+|sMz!TVcw@6bBr~sIp%_?hB)VAMio`NdE9cO&KJH=Ip$$>YKmu_SAd8-ROthCb5zAItqplfGwR6vh@ziBCr@H>0Vu%k@k@6u z`++<*@`_F&D}YQ{lWG2qoXTkioLNvIyo!&gk+8wp^sBjfHwz(ksw!#D(=@5;q_rYO zv2LiHZfmAwiiO*Ee2LT>B*Vj}VCiUmb?opb@P(GU;Vs!|#_4`c5cgBa^V(^gMo~S* zxAv&z0(c$!^DW-5=0W5q1zsg@0ebx>mFg)O?hAX(Cz^0C)XPu>c3_;x!v)H( zdq1PL5weFZ)(5059lJJLUu3q<>P%I&<3pQyZ7(wUMs-@u;9Q~(BiTW-U3*fW8Kv>< zS3<$ZM&{VwIPpvuV8w;I7PV@shy6IzO|~~{lQ6Y^bpb4>mLaKaz$=zOHT|a5*h32` zgR-4I*#I&Vlk5*xMlU(2KeHl2vmDbZo{Ap2@!at zI(c=jnB%lUG*bPV!>`G(L0tXxv$tjS_Sm`U67!o;?UuU|)VcsG`Sf8!we zMl+d?)j3i>y`&TQio>02DX~(Lx6!8_bGB2d0G`NyIhOX8)3JOQC7DSJ4Fh9->KYnk zJ)t-NQzCT5L~C;Q0-;93TpUPYg)A23C7^h|v}mfXU%I$Yqps~jZDYw3WLTvW`rWj? z{Kjt5_6(?v?oaUnTibMwRZm$*@#hJ|j#1iw&p3aRmjt z`e|4#woqr(Z2g5(*U+1F=~&J(OLc4y^v8Yzpun%T*$!VIZ(J3j$k~>P#Wg`tlxF_2 zY0UP8;=)Mefl3Nh&m7r{`DdIc8)~fEbC1S)?)cmln|Qle37uC4f2O6sY;@!q81dK}^&n-&i@YHszs*J&rHMP$M#BLDhGP zt=ki#eq_ikUTmj0s5E=aWVF(lMmw{~{@90Ew#Pki$k-+YvtVsbe;J+6nl5Fq?QU>U+p&HELlMalN%FYD{N=t3x<&^+~45O~VLh(lpKEwVRSIMsM>M6=gOBX;7=wbbKKe_n;3QyK0q#06^WlA${p&k#_I`)xz_0yliB~ z5j4s~y}D-iZTN6!8dBJGTqu%GN)5dipt*TLmwWiLTO2YRKgBDbaa$+pX#y`u|3S$4 z*Fu2dlZ+OEi<)BZldcWpSHioNTSov&Aiz}Xf$G;N<|9x4zY2lXC(i?1iI1h&GBHsW zoBLbd->UP`jp{$5Up6th6{x;c)$@-GRr2Ze-i+ang>qs*H}3E+{Xk_>|M2I-xtyT< zf?KRAcV}aNk(1pP%+s*~nu*f9gcI9!J$gcBIt+@ms{)Qp>k50~#A*^y@aq{2g}u}~ zbXPCMa`SfC6FfoCUtnk57Y*@!C2P#_yXYj|s}Wx!V&-!mx5mHv@)T}DcSNV7HV-6X zQD$djVt0_+T(Uww^Ujfs2|seI8EXt~5!HSk>&olp7shOFZ;-L+m2RmwdJZ~6LqaSJ z+p^!zStWY|i(l)Xu*ys{(xaf){85@v`;l!`-W?uOc}giDD4^5M$y#2|q+SgJii{gc z4v-2|9Pv?J)#PwU0Zj)9)F_m46MxHuf?oHJsa#JJaPmb>8m@M9EI;qu(+k-AHNMP+ zyBr)*Z6HsC5eIz|T^|wimBQMYXe1WihHAEgD*!FG)p-C4{Q2_$|aa4Spyi@i@ZNo*7+a@f~m?z^P`%=UapeD)5X`tOa2CO@}Ij(RBZO~)+TK$|Yu zO+@Trdz!n{bGcZcVV9q&>HOmr9p|qW>S4RDvSL?qvRic)s6njvx6#?5mN&VC>Ty(Q zT$6@n9_xeC?ydyP&{O)OVO_A+<2mQ9F+J47M?@YeAmPej5PvU+(w`&z&My2lH3P0t zAycNKOX&@IZ7P&^xlvwVJta5q4hjIOx$T38Yv2dP0u?Q!4Y?I|DxuW(=Wpe(u)2xq zePj1q;tTE+YM3|WM6y)49jW9#LAK}(m4t<9oLzh?bI8a+SDNcL2BTM2``YlBEHtt& z#S~kT*Fug98$|HOV2ELVZIv zQ;NFS75RW8m3T{^&brs{MDLtRY_QR<5N0h1*sn}r^M()EmqB*T{hJkRZ61N}LD}-< zr3G<%a|KC-sDbj%K;g9_8iLPAC1Mlm0ss2seOOfQTCKBX0`~FK$7)%(%g9ac(R%>% zSKbGv0RFsmk@SZB$*4>ad-1M-I$Uo1aji4IT%6Q}K6Avm(uBvxz`m)ku;s5O>ZfZA zX_RF8p}!krZ>5QCK73Whb_B&27e8wUz)F2jhHokKE86q~vkLTp!K2T~FB#U*Ba)57 z#!6pJ2%gUR!-gF~V+;$j5%YqU1>k3<9?|`tv{UZOb>83XiAG1_->juiBSO0gP4V zZ0ssA-Z9nE+EQOz;uW#^?kn!$mBHkfsh|0T5 zdY8X&SNF3h+m`XITY;Hd!jP4(Q(8wK_HurppF+$(yr8J_u%Bk%We$GJ?!bWTS@pEq znpWa^dcR5Va@T`|Hzq^rX|r_EycLW?)AvFJ$P;zgG}~wP!lFnOtot+C%a;sN*O>Bv z6>@{+9abub9nW|Szw(@kuXj59Dvo}bDiL7ze*YAK%}sTI+4?3m3x0Dv z8!GvJ3l(Rx{^CA&UBhPpNR=LLTyM|6AVB4I_`T#-CG@_RQpnr&F5|+_J5S8p(`zQ5 z1mRD`+EaLY@xSET1xzVlxf(5BVyw1=dR7>%B+_^2x1P;IyR}ZYTgb{M1aBlp+iq?f zj5UN6sO<=+6NN05XUQ68Mx{f=8SeA?9&2<{9Z4oEsrs&kR~1Zd2r~!_V;i)Kan7tldSGi*i*hD0ePO%tBeCE zCBeW|aihB1*E>-%03aZa?jIzf#;S+kIuQvHtA-h>*;QustJ)lgH3ll$twv47=7ueF zBiEm>BD=$_~0&Q#DPzXHwZR@luu zGD`=T>Ykfj*z!20?oHaSR=G81czvD3enb4htXXT`(SqbFj{Jz>i11(l!AqfnBK%eY zuwAjM`-^ejHnW(0MzMLElo7mRuW9-sM|G)}lLd>BS&Hr-3N$3I{(8>?AI`#c|E;RK zJKNgG;j6Oy@dkRD9>^4zmX65#>!%S$UdiL)@TNsmH(GhRS{2#GQG{3YFn)|bL%@_J zYoG<3Fp^zd(<1Ds89{|H$^MZnxxzup&dyHyt*34|wE02M`Tj=*hiJ9BqQD(1)vjl^ z$AT+v)7l+!aeQgpZOLxa5EE>B)>BZiwsrPH7j-mCX~{PAGh}zW#p&D{*JO+JY^(G`~rBUs})=rAR{H* zRs4c6WDPd1HYeg~<#|qZPvq^-O?P`+VKo|$MKnfIzkAi_Az*FZ+{%J9L-z1RsRyQK zzKQQ1s@65w|45d$3+GK~1drAFb)7Uf*=rj{XgQ2It8BwHoPXA~8u|0{2iol(?=5M6 zi~4weknO=ZZ0qYts>k(Im0c8S84dQS)_!HkTD6j5$U$wVdKN~c<k{gP?02lcz`2Qb#EBc;2u0ZyQ$J>S^DI+N|L?p^r${HZ#*b?_OWP{nze2d zFSi~|&+5_-50|WfDe9ad=1>GsRoK<|e!1hvVe^u_hNyF+OloTfmEF>XBK;n&t`--g z-NuPREo1zuY1k;gantp-?h5cd;e*n}NgLlkgu4lrAsPN@}sGm8hi294~(N%zt5+7Wp8QH}}>8wuje!D2y z99O$?y+V%#DtKIL$0d2%6>{ve=yYtkEC>d|Dvo=lVWaU|D^e7v_0Y?cefSVq3IYht zN`4`@;=Z0m|oRsy;ZDq>@KK`H$HPdfonT=4CIVnPtGOlGiS)9w_H z)zYR(sf0)?%s~wE7gQI)gZ=N5AK>#I(>0*E;g0_Y!9n>DkymKKxmR`W<6o4exR9#xk5gP(XFlMX>@nB8K&S8I-zwqF z(vUPR$Xz8WkqVOe031!tz(q1-O-q|<=K+qa+-=GfTY7zJeWb=H`}A=K>^5C%6-JGCN`Fzu_~|2X z2rsowfv4}Vp5urb!;L?KfwPVu<_gJ-NN7ZDqX9yb#i?Px8AE^e!xsU}0jqcy(F29v zb@=)ejbL`3>OISQA%G13G5xAF>;d)w=a={K?3g$mNN$hj0Dz^Ri6DV;3+uu7b`(mP z++=eC@)F`Sz7rkY;CrAPJ2_ce`pjdWb8CwuS)vf}IN-wQ%5_Kq91?^u2m}#YO5u2| zL-zb!_o8giMZN(~Ct-a!a)Pj)#6th<*te*0IY%+L7n5%rjdaHAVJgC=Q$&q(n7DI( z#x>dPoX84!ENWA4NXd7%iRz-b*)m7;h9ode{oMpId>0O9H#8Hyw;L0SuX~+u#+e}0 zwP`k3>CVL{QLhzHHj zegC6fYIyuemY}yVDc-P0PVw-C+&zdSXO2Ffy0NhN6|m2#g^k%(NpywBv{nV5)4-T8 z@sCer_qJ2N=lZK{79n_t<5#CD@F~eveu!xYeI3yLS$^jx<-i9@m|bfg6~a^m>vo=J z*3ZE@fvBe|pkuy`jc0tnQy-~&<4|%hO3Dx2ketv9`#i%sUSge5xhaX>GLZt09+~SM>Y$2<~lKyN!Kls2E}S7dj^ zh(|+YYuv=)RCAB`oO_cGtLWSS+(5%?ydvwxjntai1au(1JB=|NUH1ej1`mucipsZnk!0dy3 ze_qFeWvgh=Gz|^qUS;Xt3bmc>TPoV175?d7wJqy(9yIs z$|@x(si19rHe#3dghHl=Sg>Zw&jf z$M{g{df*YdVA9tP>-NjcAM+#m)oYjQX%|N8#%jYQ*QJLyl#2vtBadNe2ZtqF;z5p4F|1wx>>d}TIZRehN&;Y{Kh@~?`A}$ z|BD%s0?~}<_djMtA1;Lc7*otY&*u%%t;Xm=`!dk)${Y?Q&kZBj;yX3_OPQCf$ljzf)T{m zhxK(uqd*M9aP<&)_s6z7QLa=Rez6BAaGxqe!6FDgi=ig_y3ErC%#br9|YmQ zq=jJr>!d_WvY`yoJb5Vr{ndYjfl*UKXACGf=4ThPv)NP%7O$jXw_0OL4NM=3{>505 zRNMVeEc_pZ7ng@bC8kNHkKOrp=OIo{MIP(5O6JMba%O1xniYRF6Kz~&&Z(pN4AE@Ixv|jT}j?OqQ!1&#AiS)pF4iI zh}SXGkz}?#2hF)HM>AWE-(&puDh~VpN=%WYPJY^U;(CNumty)nc@QR3lZPP9mA{A6 z&W>~+{d>XR8$yVWD{&3~_D_`FgZR4nC|M2~^5dcLcNkIXnBZqY|2(SIOaCxIH0%#D zn&1Y!5>F2#5!E5(zGF_M90LCTHmdr!(c0;lAYj=s))kjSwix@VH!VR8^H=)rPZa*~ z5ORLYzb)cjEuQ$>Y<4BtjpB~$-@)>OFU4Qos}HxRYnvLtw=Z-LB28E_e4;y_9UlLU z2+?xxN|WB7vsY?AX%s<7IaiWRkLXVe8^Mn=y?0QA4|1dbFdIJ9ku>T2ZP@aR8?iO{ zobCaQe41oIf#CHg%G72_oZ;aqbBq5R^t;1fha{3m6u5U&^M(k7C9Sx?IwNTSA@P@7 z$_v%21xn!?0^IuTuz%WpkzF3l>@9Dx2@Q5`-UlkC(-ktH{lZwax-Jg} z_Hbj~cGy1;7fDsZ|4tEVlm7?3q*stslVw+-iK_K<0eARpHASvck$JVeIg_hyT`vKf zmlR%Fl@hyu@sB@{#+r1veb(`M6{8WY{!2bLWYN2$NC`|-osvxdC5j6|>Zh|VJGqm= zowMlrSI5I1po#45vHbwPfeqoKgJkX&d^V5M$N<(t;t58NsR>aF+3}#`brj#}kq|H2 zy7z5gt{ry4ao>A07XTW|ljU<84ElU^YTZ03F*UKxJ~Rd?A^M0Ij+qTtsrj&Gv;r5) zKz`kMv>nMITJQ<774f3R749{A+2wtWf7$;3>{x$ECMoZI;^>=p%EFq~w!F?jSmp|S zbEYt@`Z-r;Pccs|X6}YeY=|ZTH6W zfvwJ_)vU24YR_mtZ*uAPFLRm>P2Cmj2O8_Zu}NIx)y_HM6lXv_$7c!bcKbY8A>js= zCDm~Ep5Foy_E8P72Tt=t=NKhoX5BV$LZuZuj(ObDs!EAl>S6Ye#gA}uJ0V#{9?N5g zM>;C+_S2Dx6=_;?1CdoAe3e{X>cXl_ry?%iSc>^uW6w{AQqM3mKC z;lC3|6(3x*xfyNx+ViINc7V%)f0J3gnYhHsFTuVSf)aHZ->hj*o<2=b@jE=cIZ$%* zNMvm>BaCR)UgD%fa@OwARwrz_6(G+r$+SX}A#?dg=Fc+0s$zjp=l5s;O-rJt@Q|if={72!sjhyK25vq?A5X6XX)mixH z>#0)>uc~aZw&d{L0Lk4fD(vdabXe~VVo?KdEbYa4F!P$b{(hu&XIO?kt|PtrwYDBu z?BV6C8_XI`ayp~rN@Uc5>;3A*7yevNf>a-g{T7x=9VgKNXw_hymB<)YAM66VynJ3= zV0r6r9g~Ct@5SFGgLHV@%&`7c)`Og>QA;)#x&Dv8^pVkMd!JUvuC)!(r2gXRRXMqK znf#87cKh44bi_DpvWdK;bER2Zw)ix7ndD2$4^?g3oHKzogT~i`?{57h4VQ_D{ql9H z>?Ikg+_fMeAKBGAG~8r2uHG0EB;Uz>{^b^Vj!VkGoi9IYa3lQqYK=+O$)Kj@%b(IMf^D=XCE3~8 zTBVlCQ=Q)UAG@N?rHDcavCSZ}z=Jg!FLly>h`nkAy3iZ6tn0Q4VX(gOs1D#IG5Vmn zYGhVYf);&qYjyCpY))Tx-?!*5TJb*+3GV%Bk3Nu)eV%FxAGmHd3(mc-tU~hkCLoa~ z@MDvnEL7!N#?zG3{ks7ZhqSO}tFJO&n)6gG{4^Opqa>oS%r`BUf~x}8AKfQ8fjYH+ zZNDl|`8`9hekPMYe*b)7f`5w`tqRY%+ z@7gue`ZhwJOGnV#_^0NV;*Npxf)@N&N%qlN=s3rzh@ST@KEUTU0{i{HJEUq;0gi@S zTRp5EQQ6d^tHm^;xWic)Hec^{KY8*rh|}s_SgSvx8CF1r<}+)X$=P(4*1t)`@O_~B zE`x+YR{ax)pp2}O4-?Oe49_Q43&fU4&8!f5*P43MwnD1zJeGgxcuu&pi)w6_j68pB z`zHA5)a0zL9(HLTJ^C>#I$;dGbiz;|d_ro*b+ggd3g&c=?o+RM>lWsX*F&Db`yZ4v zDc`-zPwH=_c2_AFry|+f7YFy~W2dTIeSh(~TyMD|>%1O!%cbOMqQ}0$NHUX*d7FBy`Q62vy(K6rcX||16xYfJYbRwY@&Ju8#Jizc)+Z0-d;-6`E{ah zZh^#3>GS1B!c-C-lRv|*BO2xC{cSZ^1>;8wKnmV@#a>pX<#%ri3Sg^64JsF;hMT@9 zssZw%}ZjNX1bXLR-g#tGj*f2)ag< zMbf#+WT8K!y5@T?#DI<@i&kDWzIpFs#XxqsLW9d2OKSfiF0LN$jFZa^*?C(2XIx)y zkv+ETv#%#hx}gJjtyL<3DDu6XWk3l!F=BnnP5bDREv)mIgY?}e&{?oKSFWEQtoG`m zom<0YoW742E?@kU)%r~F`+F*F=G!{joq?(VBW~R z2+NH1qVAR63Dy+!yb*M;RS5+Cyf}YYDV)8HQWw4oo5D^J$o!1GUvaA0t=D-?AON4N zbmEz)FH4;GdJQ9ank@zHkc1y~7Farvj76?NGtwSEaD3+BboteBY3afCF#k_d+%=`D z>sFg`oX<`a1fCpUtK}nhYhvHtk0uYe7d)>A->@WpmgbL9y4tN`mC125%s}7B%{hNX ztt;VkWBOur6K<)ysr(tS(HfHaS;y&{H0Pzu;^~K9ZqSjiy~p z^6muteA=@Pi2mk!N0P7AF6bLG`jiwccHk^zLan8;_gN|IMoW?yw3tWADOe5K&={%^ z$M?D`QWNpWh%-3LAoMV)ubP?ktI{W`AcD4@CZqJtR!$=WW;U|boTPL|+i^>@Q#0h} z(+C|X&)_}T%Q7x+U-YKchtUf<&7g!<2};!mG=BQm=3E{=x_d>d>+ZS8)hq8(Tc-OE z2eh>K9Jx0)2P;4Pd12bhtRoyenVI8qHX@rdG6ZyL!U^lS(h(l#Dl-$mvf=} zltG%cTNqwl=*SRw*=MW}CI_rq>u7lf88;iExYaskLzgSVf2;L_3_yOX?2%iV%Pm*4 zF2U=|BvMmilo!H3s*{xvGupheTYj^)q@2k>HY)>)VO7*_& zD{XcA%O$#~!luupe6cYqA7musW4Y(q@zF0LwMV39)k~>l(i-O_cdu>azMFyyb;@jH z9u27`9X!8Xk~^*+lm*^(6zqGu$maC;w^)>ERPd1j%TI_QhmubKbmw4m|oLt{$Y-M^bu7 z`*|aSQfKku@8At{5?lNGpM9=9TYrA@vi5_%;q%Mn*F|rbokh~*US%QC?vp0Tmny!K zOJUCRAw-hk>@nb&%PI8;Tyv>cgl6V<1cg}0uU3hy;5jxEo1&1Wh6krmDZC_qqh|qK9USr^S&Kz(d%IOs2XVGRbPM4jC(cWt08t<)t2R|%G2{nSBjH+ z*f(jXZ221$iV>IZBp@Pq1SMt&rMGXGj??bilPW1;P4S{ix0*jJ^lj^1`$CrZGk2h9 z+Xr~P-eBNv)wQH%b>=(qt}Q>j9ZF#DE&-n#|GHYYSo!1Z$f4wyU`SofLRM0_zNyE+ zj)8a)<23i`hp)ox?<=it(%vL7JaoPF;!ccJzt5bu&|31*+)-Egang64tB=Gk&v`Vy z1YC;rsr)hJ-CMw;H`Qrerp_>#HuB)0UGHP0k5QCV>u(^NwTbZ{6Q6#0JCTJ3^psI- zlFMibb#@pd>4SQ_?9egaLJZ_`Ib+<9rb{!B=e{ujn5SzDyBb}WVu9F!|{A@&_qf{u@ z*3s2KeEAn8dEmhVgnWnp$;=aJp@=S0YV8IxR1uw@&Tr0A6~km`2&#QjUkd>_b?Dt&UTKFtU+CdQ`x5ehUR$i5XTu~DpzKJHkr7Qyhmdf zA-qQz@E#%}Ops*&tr0P8m(dZ(e6Gg+T6~65)G!o0pnjn)n+W`14C?&JVt=*(o}E*v zh^z>80fCNQFAriBuDQ+tK4jixQ3IOD9lyO__P3`;wdGB=jm}8h*O5`Yg-Rks1eic} z{Q6?LRb-R%y{NzsWlK{yMX~B!3i3PgPB&iV#W0n?4jk_s0x@Gl1RS582 zIiTdzVJ)aL?>C*}Vz?v-_+I?j>a58rvhG(rePN8$Fa|TN?%#B&ZREq2pEm2MBnUjz z_OVwCVj~m~usrXF=VuN-acDduza}3qZzHU%8(AbuMGxO0sf8v+7Udg(uS?GV9>tTELy;a z^2Spami;(ZqNC_anAL|*ZR>E8M#HTYA&*!OBzv^9{C&cV(X_SCu!l}6BAGMj`6d8A zyv|xZSBa+40(vnd_sSk_S9Z3pskhDa$|KV!Agn^{G~SuIBlw?KX-4=ZFPv>uhh+S} zYUz|5y4>7eBGwO)O4@?EeP*=lhymeZ`jNx{(}Ox|sE$GD@Y6yr*?T1IJV0b( zJCa=D!_MSqY*k&#U)%r{yiq6!6^chnd|H#KE+3u(F*0)w`*RHh5TCWhglZ9w?-K^7 zh7Yn|)yG8mC)0|QHCO4Qw)Z3cEH;D+#M@;V2P`0&!BSNd95hvZynWg{0H^ z5XW#5K18wPMOT-do)}ozf{xyFeP_vp5fvD!;*M9jU9lWMvq4Fw4ghHp!G9$T^QnRm zB~`S`6n)RBd-^1XiAyO110hCQ0$el4a}|K8Pid|{NYJQkf|m1X4-=sqt|nG5Or$xP z2}Z5pCO@wiW=FQJOO?N|9m7gg3sT#@P+eQ1&+zi_5|yoX4V**9eykY0f-jmu7t5pE zPywXEK;0@i-(iYg*Y0ST8Iixpfi>`=XAk9E}s}$S$z-s4@aSVppe! zoF7UE2r$0aCGsmSL>zT+jKa~+J zp%fQ8ufL<5UjIb?{gZ&|1MKe4y}(!X9FvwT5Q`mg7$Tmn@>60Unnxk++u*H3$$`e+ z;?y{mCJF_X#Z8Y5Iu$Q&3KIHPoFWu_;_?Q)B{s1vpD!SBP zD>K`RuK}i$^37fD))47;&Pinj8T(NkT6b| z*WnPs@RFRY;ZJgD1b=o>h0Rm3pqn~o*SwxEdZaJKbD*!#>U{`bQLVN;K9gZklHajW zu$;rtw=Nn$A0SqC{t~qPATd~&DqB9~L-~#(AvO*j2yc~FadcVdxKmDN#w0OF4xMXP zCgVRw~s$md7R}<4cwJe0+hVK+CqQ18 z5D8Aj$3?mSMl!~#|0gM=h#Sge6NV{hxNFHY>4#@yAIW~YCQ~>5$8#5vS~ICRNpyw0 zOO8p%_mO$}1D#pnTZLFuY{l(ioU0TF5I}c=WQiN9%ey7RY}Y8un{mgH6lO3M*o=^v z1YsqVk7rZPX2A;~u{jjUaU(;G@B*yLFL?dx&zSqNg&l`@G_Z9)f?U{VP12OxO{5@4 zcyxKVDS(kf^$tKHoFq7@UZHCDqtEe>WvvjQkG(vK@PIxw@hcbB{)zyLhJ{uX2=X(w z?a##)oBoXK;$&)mZlj4%Cni*)Sy8R54;hZ_%fH1ASyqn#f-j9AYmpV9qC zM`CKbmhj<9th-pbVvTz(H2BtjAvF3MvyHEbYAEK9;h&nS<5{<&4M&?9?qDBVBAAuk z>I5l*EfaM15<2ZisgIvtqw5`hc($NjPjk&oC0NsLmu*H85QaIgDBwFa$ zpJSSM$dIccMzfk%UT*wx`D}S`9{=sBHyDiqau0aGt#Sz1ys}=g`~U+cmcrf9#~Wj! zy0O#59+tuuE~MnISn=c4&&|2ChYzy8{;v3B)J$D7VPs?I3|1w<+TY-fjK3qPS*=$X z3v@vOo?3o21Bup0cB0p(PC7GPeE*>{@zf9Esf|@#V_A}%oMa*fsOJF zd$PofnKBYqWFZCa*p&{eV7a|~(#**82Y^g~8bXqxs2~F#3Q}dem&qOi!bPX)l%S3V za22fRmq5)}u$N8YhG(nah$aD%cuj&7FG&#vF@E6ecxDJgV}a6Cy}}(5f+k{%HOkZz znv$e;FTftU-5Wc?%@IuI0z{arpvy&kcAc};Sj137f+rKfSg93CcqOoed;LZ*bU9H- z1u5WnZb~~&r1T-LE3#@p?YC;?!rXi^1&L2Ek~i1$sk7C;ib z$5gr!jGtdk1$5!Ghzn~AM#6|3hshtivJtbAl zpA)9I{%$U?>`l{7B&(*mt@~Ysb)$RsHn~yS9B*9%76z(kjT5SLoG(uK&l3)A0CP-C zf4ZccgiKq}d5j{ZC?aocO|OYT89K~5I2*ML7bYn-EKGo;vNAQCjufe#Di$TxyewXn zFw$*zMRubDw*&3Elsm_WI(X0+vfL=&xmFFfJLtXfh~`Jo3{n_>6n;^VRYKXc(HMaqLm!c#{T4FlZUGeV69|I zkdTxtvY1HjO1WJMIda;y(A4#E-A3yDaJ9~QosC%dzLt(>%=-+141wlNyxkmflMT_& zlGj)k?)Wt`@O3J|eEgv0yEeSIAb1l%fsl?*A5iUTZZ@4@FRyr`53LW#)n_Ttk;qdR z$r&^WxGc~+o)F0RxqFE1EA-M|sKaBs!e3==Aan7;<#?0t<%jAuU}k#fvq-;>FGPN^&R{HTmn`Wf9A&6l-q?j=Lcp6;(u%Zt5;yD2er#G@-RmdA8#pyED5VWoVbDeu zW)@+C)6OOS5~F5gV|?tfl#3awEm!DTCH56J3!*4U=;P0-NbMtwu<1N!-ww(j3%J>_ zfs$~a9y-;q^`b{UF*yc9tM}}ZeH43yYc(P#*cT#7<3a&%#ZDG_5kE?541Q8}tgrOg_@j?z8 zCQ)1F?nG{+8NXnH(ZZ1A1Al5zVb89$Vf&r!t9cVAH~P&(#v*6|f;AfP!bcbnzNZ45 zzXm|JTJ?XoqaJ8?qkY+9*65Wc318=!G1ctoatJlUL;NM9)m~OW086T(OAPpH<;e%Q{V2qHcZo_1xi|Zh9 z%2zcZR#);ii2IdA{r;i`w2%eaMptr)i~lQ4ErT``4csLGMV=x&LiDoB2}q|!g(^{N zU-2zOKwVL=5I^@aIc|xx!WozLS^+z>@@avMyh;kyh|)Oi!91TW>}Kuooamz=Y}5(W4yU%iN*a& zWSk%ryB)UrXM9f#%qhU^55htSm$q_ z(C!f$Z)6kce`eLsSc89=#ekqFf)D$MtkSn6%x~~bWgIRul@Y;DFzQl%CpN_K?_f&= zx5W#)1wl5{e5=iN?v9r)Q60<`B?`I5mu)JCB>mOO`?CGx?2Penp!goVoEDTBLe2R_ zW0Q|8QLf8}Y9Y1qT|Jx!ypqqtBLj0-GUO6_3uSDb^{96b7lTd-2y%IUAj$q*P{wXN z5YY-#kt?A@-rbov8X=VMeJv6_X}Q}{1e(la<2 zD7|(~K~hi(0}syPHu3PSxQ#ZPmZ^%|_7aG;8)qCI-|w+Y8N`o9Xavj5qLFYJG}NT= zPM+nHj)a*!bZzK7QsAq>o9N6FNRMg-O}~>L3$>^jdZJhx`k#K%1pO8QIr)UUp|gW0 zT{3mD%&pqxI-0IOYDuFre1Y8pke^xC_lMp8&5lChX*G-{xoDcC7|tQz6Tgntn~(0y ze@!BbAcDffVG0H0YvHYKerWqZ@XA;5iN9ji-DY~@qu@}z$x_*up4GK@dHw979;4Bw zek^zBK&C@3Er))CTNOZe{evP*6j9jXsmJJZa^J z9x-|SBkLd49bKD^|EHKg7;szX-Oh8bSJwfkUD!MgX58UIS|X(YQ97yFYAlbP3DjTWU3^7{t}e&eT)^q48Vf}yZ`?P#d}_NnVSZCeB=GX96wx? zu6N?MSy8XwF?L^iep2U8K~|K1oZ;SPR1@wMP4CeXSpd|R91!P!ZUx`3<9I#y3;gcSL(-KCe#_-@ za45vRCrU%ajL3TzExHj=Q^ma7di`i7=(m*tj${6(Tz^CXci#VeQ;2bU^?XXCfO-18 z{9gJf8|c>Si54yK@3hU|dcIcw1Iyp?gtTf`IgfE!;<*uWJ>FMCN1>CUq$+`S451W_ zms9_W2hSke{wKc;?)zCT(oQ_(gW*WI{f6iMeRKBfW6U}CCyL17Z`?m||63&;|ABv^ zD{z5-ay5Hrz6hEyIgUhL)8r3b26WGT-Sx(rE1soW;{;(tk)!I*{11MyjqfD?BnH7P zd&aF?u_FM@Yz2QwVyg>RG{fP74ICWDeX=5nL#d7j-TpZYHt4+Yd@ z6n%RkY@=(a*x1-%$<*?vN;B%H`TapDnwDD=V7FM{?lDBQDcA^2Dkh$kIcf9QFA6Eh zHmOw!kpa3;?VbII((zw$>Gd#SzJaox$#bhy-O>&JAa@>dP?+=IF8_mXzf8t^|JSvDA=2p^{1=`719EJmZ`l7*zyFH(gZRI6`o94G#{L(l|NaEV@E7C1 z9rdq9{*^za{l9GOU!>X6>z)6a$N#Fp-y;8Ch5rTQe-LgA*yLbx_z8vdIKcBeN~DX` zdhevks-dka{7qUY#Pv4ZV5)LzPkGW{`Ij}0&pCI;24BfrGOY?-z;-L|+A5?~x?h8G z{`-c3`rWUvV~u7+CMJ7RqX7E+$+WLsJ+Sr;)kW_`cyy^2 z^o!#wXHWOxdj9>y%v*+6S^;AqffCAFrYj}N1mc$Vd-uLlT!>Q|!*zihbiw;CWXzr( zVguq=wm+w|RFfXYmg|4q&AC9Fl_-Ra{F$qHA$Ido;KO)`oZ^*4^qo+3bbtfJtBw+V zg7J;_)k8(wLtG2%aVeuEuBV?q4Oy6YWY3(1kW;=JRy)3I7D6QYcU^>9Ia z{3vTm{BD-N#~lIqc;p&HG2>EwsJ|xFL_xN1d;-4vRYz4-l}@**^zx^`{q7$^9$n3q zyccL9DdlQ59(^|)BgZc&MP#TY*%d)}pFEMpY->j6(c8WeVhAA*e{ZzA?iytvA~wxv zw`{KWY^aA0&#R69qr6&}##0Z~&y#D&iRT6#J9ji{)`Ai9yK-}rmwywp7d2zBFeT#C zlESdxIVqZtKRsx61G0o9qoF75=Mi%I;OD}y9SoS~nXx#_d)4a*ZBBUp2!+9DH07G@ zM?xa%_w&B*Yr-clm8ZvUl{_G#?T8{RkjlH!%o}tt4!88wz3g$r1)5YhPT*UDP>X-s z0p3qO65716-I-G@Iy1WYyggd%*e%L(Gs^}J97ocRt70EyX0zhoP>vd{cLg5Uwc443 zga25j`6nL9>z7P}cv^rhs3p}{L3;@wf979}jnmuREVsH4&8(A8IW?d2M+Z6}e^)u$r_0<lR4A!Sd3*#8 zRKFVNap1(XLDiznh@7cOnpX15)37!KY%0`vo5k4>!VN5Tpmh*}M~i;rf9Y{=4}Z~H zRUhJCcld52dgSukg$m=R@Ge11Arj9G4~Bs4dcQ87BXM`!Zb$0-l*;H0!UV)*k(p(n z`bdD(+O)9Ct))QnJ1OCd-S8+mNubLg#JQ=dn2@G>qN7;>QJtEP?-_YPjR7hKVu*q4 zl{&KDaq-w5wt{aSJ88eZ({m+|Vh{JpUt7N1^L#8&e>Y5cF?|=jk)>B*kT3CtMVHW9 zwHqcE3KEf=D(vZlFZTWOjU?zfU`t%ZeNbB<{}`L){T0ipMBV>%EFRp1=Rf}r_y611 z|Lf+T9cMqS&XSb39__iDGMg3U*_cFeJgBo2C^5i^N1A#r z%CF{70DDTai)aAx;odo1)y#8=i_@Z0<<;H?HzfT`9iD+AEjy;ZqL<%>91{a;yZ~m~ zF21UC(><67Pzd@Hg{BWa-1E@pBucB5@I(iLfne=(A6Ad>XXxo_{-LEooWuBgw)1hg zQVcSmR#Hm+mEg28fPDhWtur}8%spJ>0!cUvJ~nRV8bEefduln0VV5J(~+VaM9i&=yAyBUdM&lJhg97|5G6A%o#h7~b&nQ@wG%RNrW0RNvGtM) z<*cINXnEp+GKdh>m^Xe5;&53Dh8xJ9)O+nPMu_=&^xI|&TL~(R#`GS{iS;Tf*bAz7 zeJ6pb_|oQwcDU*hnq^6Po<^VO8S=gPPnTuKm()i6ijUa3!;(L)_Tipe9JE`pM@jt? zeB2f%lPMz(C&^;Ihhe|Ye^)egveW+Rkr~s!T~X?kGS}*0>fjKO-Ap1{u3LEy75F||a(jM-kJOcReH`|;=%+Gz=VFS3;mpv_#(4}p_qZM1gx(P#xD(}nto z^ij$c$4iY$cqfEBu6hutwiWT}Z8{wXbAEO95s7j7ce5*u-#L-#iiM!jBo_SECQAB> zC&7IvAmM@}A)TxGyIAi#?-F6CLjoqdL(I=3AGWSm;oS4{g^g0jcf+swxsA1`_g>*) z%XBaFY2ttUk+RpT9>TIYNW~av@6UQ0X*JWUwj)5@7Ty=@!J`dL4`V-`qu{ssKl;Z% zd}_T+fIhjkFnq5~a6e-mI!kGuTc2etVXo#J{0?1|XgPn$%OBnM5dY~mE967+<(}f9G#ew~#qd8R6GC0A0=KO`(A>Q3S9)myJ_7d%w-|0Yn zm6NhjNP~4ddf!KkmbcSc;;zs3k>E4zuJmVdID^vSgsnD}C9eDSDi2B>OR(^K{4K)SI(xQUQ6yCY5_A#Vh1~3d%DS^eR)ziK{3@Iij%rQ zYnB&?^$*Q1e4M(~S1`=>h<1blKRPl3cl{_;Rwp3KS6&;;r2KS%Yo_B>7BbiwY`}T^ zL!7os-UA^u3mZWm*a07!!d7FTkUf$-u5nAAGml#y3Tk*>9(32GoYdL~^|8hltW`Sl zr)O0}i^-DXtDhflFMZGmy`dI?e+E|=Y6FPHWQw7vkOtmdh*bq^r+fKKN7qq-W5N(J z{y|#@e1q-LND9l(vAypiFapb+(C6c5Ya9H9b|x4?1_m*G$by;$^QWGCx1!l6c#sei z1J9q@4E?}+!8_5kT~^MU^Ba|wUN8nS?Xzp$J0WO$7)}%06;0nQ0`BMNS-mXa`#W&_ za#lh^9P~)*!#*$0?)Pdm|2WcF8b{V@wchhQcRMHE!RI1czWB9?<_DEeA>_AVnJ8&6 zT6~GTm)&>OHavHsoJ2O|;80-ha=_345X5XRaWY%{e(2oT98nd9xw*kS_zRC73oE4B zF1*kb+d5MhIJh}L3YKM|N{TTo| z)YAechCUx0hkFKgt{!R^AuBA-52S(E89SCP=bF>~E!vnGvS-ys%%8rVQ4Y&-6j%%X z%V`BsCioQdUhUW=47LLynSFONw<~&fx&J_!rNo;=M({I5kVq>Of4qX~8eE@;q`~p{ z^vm16-11oNn+sYB-*QB=A<%yXqWAOmz?ep!pWrX*2gNt+xBR-(D_Nz6`VG&1RF zS8Ej-0yCN*cC=Z-WPj@%++4s(&%a=M_Jv8d2vz3(Z0dY_+R&gN#!hLZ zECFGTAeZ?UHj2VUJjlrLQ+rEDR2U_AkJ|5`u>#HK(Wv=rS2w9RS?z08I~{IKzwXl2 zRrCiBkXfyiA0z~=bcroCuKOD$RQ=m_Y!S0vsS{zsXLXl5OQ?k|zw60(XD`Q~KVBtC zl^yZ)+sh_)rbsEqZ4@u+u2NY9O7S(wxX(pvxFj)44Teu;*|gw*P}O|jQevcHSsOe@ zm&4W)N1D65F}68?+1IPJ`*J2>0g9X)j7iJey*y7qz%4>ofd!9GXU8YT=O_X z0asSO6c9|a-Tsgsmgm}LN*G*Lq@3>x=VxL9pOxgzfFcwlYO6&{2aZhqnauvaHjIzU zY{`DR^jC?zEG7MkcHhuKok9X9Qv}|SMzD;x&(mU`Birh}Myf9E!+Z^L_m`n_A&teA zbcP(Cxm^zWR3RVW%VDogftakEw|IuvMNyu4c2ZOra_3uDsOR~nB0se9T!=D&TQEyK zL4n!6azhA~eqr((dx0;L!$h%`H9@}x?!`v~(V;V+qi+O;J z%9U4>mfTc{yFc@?h?}jk{`W1En71^XX3}IA@L+^l!@k@t{s2>NWvg@#o5^y7pEfqq zY10msP2Ze=lqo5}rQlS%c~gr!#bY_8%}u;vi|LGs?eU%njn=)*;g*FO-C}xgjubZ- zmKd>t%bhW&ii>C_>S-lg>CHVJwok^bdxzby<{3+nB z=KUz7DEqs`Te#$#2hsRaG51B%B)vn7KwgEe)a@O6m$mMPq`N(_-|fwmnAKq1cZl@b z-8iO6=7Klv1t!{+xY!#jlWe%9vpJd>kwJ164aDShCj##-^la!}@7i&GYpBM&r zE<#4tDV?7L#~DvzmADfSvn)T|$O)C;&bqiu5JlaI(1*R2D}qEub0bBlpzw|4!NT@H z-hqax-o+j+ zdysL&7`5j@W!%wtG4i{e$w#o5dTI}WHdh(-( zwa=8^@-rM?8Vui*CM~|`1tfXI)p42|lGPS0hS;tFlcF@;^gse!z4;Q-l?lC+S#Rul z<oNIL1*fJWw%n1D`2*BZR^ zC-C>JUQ~;E&Bu;%QltgOvr#|%q!#W-maF8k1r&krI#sA3ap zk~#_c)5D(Y6ajJ+rASc~l&HUr>NlZDfJt$6hJKnoptZCJ$!p;u?$$ytyV&fF80GY!a+@0EwBJAhl62(6Zzre*yoITma65rJpO$bZpVRL`3E1c&gsf1% z$kc^i(MU=}Ako(LaCdeZw=}bzr0|dB6KDI=W|c-$F3rwI{Q%yTk${^y;3WTL1N3ca zXg2K!ll{2gk|^S_W{7=HK-r0%*8+%FaAIR7U!f*@KG}MLYI!X)0#MS)KxEXFMWP?x9bX)#nr_&;_ff&sX|LVix^!wHAbt)B}@I;9J+i@>KQg?HXHG2 zoK|5suw-oVpcPzeby>HFgG)nCv_Y=aIbT{&u^?!KsDgiHK11rHYxY9wXfR zV0fLy2}f|bXT2y%-1A~fE5FTa?%U6^FUsYImz=w6v~uZ|Sj>Jc!FnBhNmXgMe^3n- z^xK_^J45u6BJyaLAF8Fw(>=bN)=J3D3l`cS6idc!jxSWedDQEN*?Q#3RzsyPMjmoa z02k7K=+&1WaAYUNxI$q5^*-BinXoOi{ zVsarrm+-En%Fc5R#M`f$0+{)!Q9;tF2TXcg(#OikecgU}0Ez;eeK5E@oT-`_3~gpe zH#DD@dK0M97^C7}q?AxlmP{Ai0S6i}r>MFT`cPVPl{l^Az{&IT66K$ltM(GZ z?xaP?mHd|Hkb3=Mg~YuaF`<& zV=c+A-SCd@Gxcl7gRic>t7oNr2+cj!>?w2Q(ckM{p-v5FLztKy#`|{25o6>PS#?1c z$gQ`o)NYKd0FEz^4#2jkLu3beP2!&Mv37zi-Q^-#Z8JLZjjk#I#;2&Kp;UFH+El_C>XX3zM#edYG(gUte&wVoUEVzR?2qkHsA0!WVLgWWg<|E#Kd6^-2`wXEN_PoTCl`TOGq+R4{cX59k`p(3 z`$Gl7(GVTW#jdXz*0)Yx^==pS$~tLETR~SL2SgoFbKp}_Z!2*YmySrg5KUrkbt((|zUBT? z35Hr8TLMeD9oL=0OB$}}`itDF^}DEkTYYiW7C}l%tzZ_9SDB)+IUW7jtZ?xzHgQ$7 zx*@^47u86WusNr(n|41ABQ-^I1xE>CQcN$VrX8=|ve0dW`c$^#-5IGVR$l7E3?3g_ z6FM({SF%?Heal?}af`h-E8yr*!fM9QW-;ZMbtMM8%5N&a*I${wiVyDB)$re0&qmTq zVNCa&jYRPTXsjx9Cb@cL3RZc2_OpgOvjuULtwQ|^5o7jNm9Uns0jCwwN2%Zp z8OC1G0|1zP4PCj*RiFE>r{?hdjav2(3#}xmCFI(C}DjwTwv5_xpGw+xG)fL!KWRj`q|^Vw0)PVO*I+4w+v@viSk2IC{1|A)xgziFN!6*- zsd^OXm0I&h^7X*~^wdn51{{A7ev^7deQ*I&*Oo*9@dwfNQ?p3HJ%U?#r#ToWANJN$ zF@2$O#aa5}83+8LmaZ_4EZxU5Fmr1ON|JD9Nje%Q_Sb;8*Hi zB~%UNxnfz|V~6BJAxy;$zV75sNjJR*y=;0c?pm&#%L>~oq#Ydh>W3sxyQ}#zi#*VV zM$wG^PGy@@_A&I*mZIVNUtUO~SaT_3#Z|F5eP@y#NtZP^XwJnFJXuQ=?C7R_+HxQ+ z#V_x)Kt;OjS%1zI&oXRnUw`bdHNFCj@3G#jNWE(jA^xk!kD+#Vy4}zVa&?JDJ~YjkLE9`g9bUN^;2yCp&=TGEH+m zv{GV|;mdPb&(E5bJ4s)YC!^>p3O5F67Jrv=mnXH?+Uf!d!7f!=!-Iljy1|f{8G@Vb zJsNyPxP8F7R6Qo=Dn}MeT(x+*7Rr*Rn}j!izwT=Ox&7%v2V+5Nu-8e+pPx=gBAy{i zvCttMFeL3;EYKz|`Ze zJGHd&SXw(@;-h-@bKk0+M(sOybP^r81Z^CV*f7UQx@BpRu1<$wi=FsZuuB%v9KeT+NViTN8 z+0K>ii;w?H;fj||Dh0B$z2wqjZW6D=ND3Qj<-Kkh~d+Yc_z3$iubcVuy z80sw)+ZD6|d7cY5;u-iK3opo{@NwlUGc0YkqCWRbS2`V!&iu4Ek^yrA)mW>{Jd(f- zl{i+IErzi#g*l8_tGRWUdg|VDhU)+l#ZC-yd`Zd0_)E=+Q2+z-J7fI9@5i|~!}0a) zf>DFWg}&>{8w%T>=L(+C@e4;S*fYss5L(D{W^(9{Q>+RUix^V~lY48STaDh+_ZMe& zmPEbcvhT;emQ=kln3s7HdS%T00EuPtUWi}IFq@lvFw#N%(P7kBJJnrCa}zm7k-dbpMn zq|`53Zv{6~^vWFAcBi--^s z;!!jL_Ao)YrDVk$oI^VyH9fTRI|UdV!5VT!*O5GGrCjbfqV0G4RDhFxS>hGpk}uOK zrGt?AqC)V`5l+q72=ZGg>;OA^EqSf7SFXXBhN;xfgA$GV_3puxcwOQCn(lc$l zn~<~pbs}eV*k_FHzM*b;6{RQY{-f^g=ia$Y+Vn-$Nc7PieQhyVOmz}2SWF2ssTsYl z;QNMfS5kKh$8xrN(qJnNLs{be*2LC)5Fqv?MrgG9k=X;h(bqJzY#gog1mZVTMllk@pC2xSITr|3Fr7&2ooVo z>GOQl$yoBikX=%V12>3r-wni91+(~DYk6YE8qx426a5ROCA$06<-$1)xI0G+XmYh- zPjn|Kj?)dZ<@_}P;Ahzq=qO|l+=LGCkn1jF(~)uK1UrgwZd}ZG|!E)fW{W58DgfYW6W4$8X5L0(_D!BjWC8_~=XgERwL$DV5(@o*nI2 z0jK-QL(08nvjlMJ>x50t@<|E!n8o8oZ9>np^UgNHd$ zr^;7UslY4rB@O1*yFG;5Ajt#{5x}zJz{v>gnxc|XL*8@}*)yk1D$ajb3;fxia8MJ= zF4yUX6(EIPL&zy<83gg&&>r~kQNs>mv=d*fQWZ5yT)U+mo9xe}hX4%wWMqQ0!Ki&; z>BmU|JEd{G15IL(X#|X)Ex^C zVuDT>O=1s`^T^P8q@7hppUod%g3Mih&3&`rPhDIDAOJn9;(~+847w?x(b2lCk>W=X z_IT}C^ir{GSGz}a?piQxTeX!4P;-KTzuZ^0hVXyELQxHm+O}PCBTBanS=e9h1)70u z&_^4|EyiS+J5XWc>%&AB^UD4tDRlss#B6~FL1k|Ncv|*W7N!QALzGz z;rX<%Y--8`yP@%|l}SEOrhTBbgUJP;O*|6luNc@8b3cqY3s$F{)XD z=XfVvB2#Qu79lLI(aM&k_v1%}M$r&pov&UA0R+*Dk7@hUFMBi? z`>#_+WDEGu^K+yn(%nT3Qo=!{oL#6kLCO2GJOLoKRJrNLP)eX9#b-~i1;-L$B}yC? zJfRAFIe7`avBrH5T9d*IAs8>E-D_*uRH`6lb0;i}@ZF_(Zk1BhXS{?1+AA@DHgTNq z9XXZ_Rx&m2$GokNWt{1>H(%g|&C!pC2t$(r$|DyW;Rgi>n&C4NF$);AZw5WGz(>5}6A2p-D%uoi2XW%qsjJv##n$s=hHz(<@^qcu| z>*Lut{9tfy#uiP@j_gn%wVJ_WZ>FC-j^O}R`amyz#t}!uiJoh52yo(W6P6X7Pfz)@ z*_pUv-8ft)>|bvV^V;HnWRnm~zoT6Q5ZGC#b7fYAs)lKm)Mu>g|c?@j7h)m}cj zN9tKPk6GzAG2rIMT3$SkNCuaQF3O}RLe#{=nkYE3PonP3(1qo1*o*+Z8CUo|@~TrN z_ippWXaz0#E*uan)f!8V?k_UVU*`S1GuHOqT83re^SN~4%t2;%B+xAJ7xD1&+u#aSXCr~pL(K`R;)vZ!R%rERP^SUhJ+DI z_v2K}6xP;gixhROP<4XsP}jhsO0dt-ETwLpAjuU!bO2MH3f#&F^+~~wON8gHMGKJ< zbVbp(PNy&R9KFmN&367f{Lt~f%hYcd?O~A zeWrbqpV}^ZVkDLZmtdz=ixQ0v&1bly`CBk)OJYh!cC{)sB#cY8qKm$DXVU@RKeRiQ zlGU!CA`>ASV^Y>0xUgx%rKUtCVd2*M8OTLR=N>*%9+$XR4We23P~ayFL0Y35)*ytp zRl!q2Pls`e!W9&Mg71ttqj&w{IP&=6O^36N#a{0C*M4&)%ZJ$eAe;GE^G_-t9pf#@ zlBg(!SR9Nec7fLH+1hluE(S>*#s1UDbGTs#)BMy}h!Chy=ucGWusQJ~DyzkGm5qe5 z_CDr`#x7V2@0W@Eb;Mmmoz9S@*qSv#qy~5QWNfyfeZ-T+g+*Or7}`s>EC|r^`aNHn zBg9Z3P#eu>IjFF{gvyE=s2DVhB^J8Y@65NUkyGXKN99yi@m=WRB8{$uIZVVp#PT;G zRTRh0xtqvqu-FzZdrkwFBwU76HaoLER?RrC;_tgyJoEZ80pUaRwZ$4+x}ZfGN52?X z;~|D_55-wj&6xk_OL)0cBJwVX{M|n@;&e%^Lbv*CDu8mu6G2z<0dJ{`J(|$gcLvuk z)U!3ZNaXlc;hq`zmFGh@j`ZH$FD!9rVK_Xz;RA-p0U-(HfmpW=L41@I?V~rdafJ5JI85 znj{j*SycTa%O%yHY`&AVRA0q0uqZuU)L=-|qu*(G9c}_b>n^s)WI7{DtThLiAq;lj zX(_@kkAK@yYks=jqq&qo$HE;Qkb3oPi4_+9;a3CW2R|TB@x79q?+GhtcaLJpiI3on zHH_kY$sKf-0NRTkV=+^im`2>gHSz4LQr zLEHBm+qNgRZB2}cojtK_W5>x%Y)x$2nmC!*n%H*o?z!*hR6VEa)cFhE^~2s(Ypq`B z)wsI*x;|elka8=_u>ijtK5r{d&_zv#bQpqopkrv~#3V`~y+liFECMbRS{$0&yD6!N z3>N&(-;44&STIotn_G4rg=z6+ru9gw-z+h}?AR`)+{d9Tpfjy-XJ5q~>C2ZZVjc)C zWFzDEKqJ<7SNig}j2P-6j{f0FJT7ZUBT8}-M%&0?O73k_S+yKn3oua~A6^T7C2X;s zUtFumEzc z@ZBcsRJ9*Nd!ubtX*|xX?aif-9;jNJhPMRlLjq8+`xpHY4*SQautHVQm>0VTlzd4J zEqQI(fP)v(h%kj`oEjsHh^*P>BD%ZXxB`a(|Fo`4aN(klys=fjK1JG@6C)`ET$OVM zX6e&LyVtAB8uYV4$- zRTBjh)221FxpPXdCt4SbsfVMjG`3>?T`R*FFxd@(V$q9*!KzuL=Qaz<$p@ z38KD-rhjybe9r0nVi(r$O9;V1-KB(Dg9%1que- z9?70z8v8rnF_=nSF)4j?JzN`j?;<)g|2{r8Js9{{@1|4<@xnWU3m^NSNLL^e;;@Nm zd~>Nr6Tw+Vjf6@RQhrS^hk>if>0hM$sRd-ID6CLXvfeeVng#L~$aR0(kb(l#6Ac^- zLWHml??Bliq(ho-VX?6f!ZvCy!uDUjUSBBcrW?^c3svW8N0)^F^CSjzuN>Wm6GSk7 z6u|XGAM%r{>j>~-FLY^_vPNNnLG9rCah^RuO48qL@0>%Z7Td9ih20F1|DNA*RvAW7 z;(wLa)m!D|KV~dsMZo=iz?*wRvZP!&r@Pcgv-g7$wJ(}1+bvmS;AitjEq^Iq2FpgE zs)jSEGh~s;p>2}WPMq$KerR%0s?AO{7KRJ_;6kjxYzBSF^mud(296|5vnEns!c>Ay zRy6s>;;-{|j}P}Yw8;o$Z_F1`^kiMU*cVfvNAR*!=)sRqdUUwfO^?p&D8(%Qh9Pm= zP6;N8uEm>*D|W{WRf8>8XdE z<@Y{Z&n?;yXSVWr$8hZepZq{5>@jR0YezN{&+f%;pAY`;F77v^FEev~?*i)`k63EY zdT+CB^k7SHfaZE#%}Xw10FJ!=Lh^;j}r?L%f?M&aD?rwrszU3>TB#-6PUs zJA=hpt_&hU|5V;-(F|ebxZD5C)y?0?ok9@?DRGsvV&mkkH3Jo9r_3(TaC)O`vK-QC?jBsO4 zqEjmcg9@e%4KF=F?YV{f!Opf`&L>t-vT-=~5-5A>@+mLZxA?W>6PqqHSezVc@xc)% zI=>A|!;kX-xd#aQgFV8abDME?E<+Aq8XH%t8Se;{5fSCt>$yd@e1^r{z+bS6n8Ro*Tm8e1BMkG4e&bpYPh!}f&bkrnHKj!;+Eix%J#mu z>zN?RxRS7xYx$Hvi4!R*Wsz)~AZ(N1hHE$ElQC$lAK~+el8?~#z_IYpkzXZUnEsy& z^+Rmvs?k(HY0%0QDa|3@>-D`}Y6)&z8{w}Pn8X{EpR2fy?rVPQEh^kG~MFb1wy zk%;ddUc3dyG7IPD5XdE5w2t7N)n}M*^6 zAHBJSuJXcGS}`^m7To-8isXv(RvQ!O3dM?5cc2(=dSro1O}x!5!Y|ziBj-fPz@pfT z_!XxJWf?KX;F$t=3V54m#oZYoAW3`sL%bLyvV=;2{X zl2B8wmxCGRjqQAV4E&bCGyex!6t7s>Ip(zG;?NiSKq5Szp)pFEnJo!aav7Z6)b<=l z8$k?#+!9M@wJk~7F^vG!_$Q=1h>322nJFb{(o8m{j%K2uVr(fi1hADtWJ~oz0)odJ z;N+npMOh=VbrV6b7?;az&Q_HmW0mU~ISM=-ly>v)wvXgNG~l97AE$)R6=XYXV#C@fe{b$InwMQpM3BMP!Kow zi9ZOzXTgYdct06iew7E9-ACc9U{+op!L}T{v)PRs!O79WXPz)!`xsp+c)XSTh^RL# zXm*tJr&f`|OhasBxtqblO$93`D4q3yF#qnc`DYIDMc1P@Kxe{eZ(FMgoFB1_un6qSO0c;+{|xNpeYLwh$)nemtmO z2Y<3?$#tRPRINg)7b}dePn#U1aPp^`(ze^HlT7{^7+L(yrxvy4CYXn9gmB!~V zdY$&8Y(pae01BlI%t1>K8aSv%-d^Zrp?2U8qp2G*(P_dDzblJ$4$l}Ng+B|o?aQ%J zG-2oh+F^F|j(0J)PY8wajIvk#U~BwBMlJ=8TF3vs?xH9#&Ht7nVTO6%W>7s;@ls$# z8AF)s;>QI`f)?F<0L5NIMMfT$$H^S|hr%QU*Eda*8MRJ3e9&`@)3mRJAsnXRqxS?{ zm=@cQWE`d?*rLfCul1T)n0i#>@a+0pR)t=5a1S>omzOV^J1d-z!>4bp)HuhmcK{Rw zXyNswI{wc@4~RKKDvlA1Lp9?!zI=c6e0mhN)$4+o12#%D-54b+eV;5v&-jLJfn?-w z&x7L6F8XeyJ3xCb;}VHy3Rd@WV7XL{%-`scIb_6j&w2<(Wwy=X%}~810t#5j0GSsE zuOnPC;|t_`;T{f)F$2M{YTt5iaB~D(!{<6QO(*+*94++1NOgM%^#q^T6Z6TnHc7v_ zx1W~1*!;Epg)T`&#>^c}F4!Y}w0fQPNrt^plGV5Oy5wlSBv)y`i_k@|oadP0F}9Bl zKS%+_4rpz%3wh9~v;jUKZ-2X!StPG13vc;gJ`pA@v50fGYdF1KP}M1&Wv)j|YuSOn zL&OR`T2QLw&CIAE1yB`rv9WJMkQl7SeXXh@^Y%jmWV-i`r8@tLKYAPZLLtnJ%d;4e z*eI+?WN)Wuu{9C}Pu{CnN!)_up)SaJzpg0Q?nUIYSUp%!w=p}@%^JD_zB6Kc_ZE3Bw3i4-B17bCs) zX_-$*4Vdks@{AFQd-bT?xBqTFOJ|_e8?tq8h)hCZ|2?!Fpyfi*EF_1z1kuLeBd%Jb`u+4iW2oi3X&{0^NWf-*jVwBMNiExeyEDKrA&b#2F4eYomt?<{pt-k#F)WEyt;_T*Y>8t zQKCfIJ~*%7?eIWeWFZOZR?IT4Eb#%46K2$%9Nsxyx;nfGvi!z}&clr&wYNia#YWzt zCpUzK2pw37mzBXIvj$p*&i9jOO_?8GaGy96`bcXgXc1lm%>U?dx;*%LwpB|YKl~`o zakvW-vlr)j>lMCg8Z?a{7ghL9m9!#gN+EcHi50b5?kHU0`Yq%{n*~^>j@dX?o!go; zu>(QOCMgb1#sV`^JUL4p{pex2g;rfu-*$9NSk|5BFZn(6BKyUNB1k()bIW{XajD9c z8<+XLa1bpTtZ#%?@+e082Q|eBCDvF!t&VOf|JDSHvN5ALq`av0%f;0AIQ6p5toy^Q!{Xw@U0P@e`#s_No>6z zIxZZ7_R?_V-c~$@@#|P`*9Z}j2p_gEG`uqEnT#V!PtLmE69$w=ke&Qt02 z7U|@|5C%ja;d&>=eOfp8I z+4NO?2q*UzYda#`8eS|k zQLjt>>O-{R?+H8WX->SLVAXCPp2C)|>~V@YVHI?3_$O*oYOBaX2Nxr`9M=7C({%w` z^0W(D;YXY4KU2@D7$nHx`l|@lssv2I!0LM(FU+|`k`8rsNjpL+a=CJ)vXXr~pMpg% z6ld})yEPrLa7YrR9dj!j6JjYBJ?h{G+sf?SN2zJpaz z57h2_2lLL9OK?2_Q(|b!{qd&m>4?jdl)l7p(XRnTm*iNcD>Uc$C!KF#MI3G?x}x<; z$H&t)nODE{4MHx6&bEpx9Nxn&>;PNJ)9VFqvr`~uPZ`_MW&@wvbfb6Yby-IvT;++V z#o@ctb>tc2)YdnO%&pmmhYxUjKTYJm>ir8dePy-&-(3WZq2h28*(OG!aP0w@JdHU6 z*}#cxV{P zJt!J^a><(N-CYZ*W?aa0fwjB@xK^J%oFt=El<$`dkb-jG^7J=S9GMtrd6y7mB){Xl z?=Wbu>55WB4ZQ0)GstFNC?WQ<>_bRIMP)6{H2D!Q=xHBEqP=c&i(}s?yuY8fF~B98<>YW_7=Lbbgr-BD zAw&eiwm8avWDhL)dqxQ4?*G2STYuGZ;t&fLw>MzSkB}0NDZX{&Qq2b8l((z(UK-P_UUJW^nG!jI$t>O5M_Qo&XL7K_m z+nk5@M_|d(h0{6?e?AFWd%FvZ5=%;xmdhhfDo(~9p%6@Vry$;a@yH9pMPtcoTTTc#DzX- z)fo1CJ0_3djrt4UUQ2BwTSzxAIO=6*m_l*KR`ujp?FKYm{1^RMDFqVSB%n_dvT$E*T$n zSt3|V2p-t%d8SvQZL< zm-2+qh9K5;xd~tkUHzZrx`AHfp9-IBqXll1%W!Jfhy6vZx#fH{NT|3heWSk^n^l{I z$vD5=@(snaIb4)6E^Z*1Cqtu=k)vMpGet`ySCONy4-}{Z(TZ*%eQq|wwB6Y&wohpG zw}|c$5zsr$P*9|;fWostbHLo~SqQov*UbD9T>o-N8t*5$TH_{_#8bD~6Q%Z#P3HRB zJ+`-Hi=eG~VZU@*lzMdPPPZm2%fP@Fc_;QApMe#2%jI4ej2$6Hd_O!@ehE*SErZbg z0Q$y|Vbsej={*R@l|rdB7k2P;{{SGJ!Z-q%tej}&6g0U#GdCKXgp=zuWz{)uLjW6u zxXT)9UU`6@jjZHj-S;~a$uZZH00*{|Pdcq7CYT*`_H#4?HFRuwc(NG|(P5nTNMGSl zJ_aKS9S)sHzNjDa_GD6O#OE0}lS2vDW;6K}JP3#vraNf^5eZgC+vqOlFq($SVPYFb zv&Cr#*)@xnOY0B?OSo4f;fbEI)t2)UvTjS?Na`&Yz_=;slvoUcUPm*?(~#m*muYTRq~2Yb?*t#^VU_0x;VO38y%5P`z`4ylq1zzTTIv?J1)AD&wyM^}wZ_gvZ3z7seF;cv`06a`ig~WxZ)hqkygBXfyOkBxi=;6lJS#JD!>yq z{h*yBJ0vz<%>_;|s&KSl;|KtVKn3rbao@4?otmrd;LccJM(Ua|Nb}((qRX&SpKzK$ zh7)&wNS-r+NMxsgQ^!C{Jr3@#QN>2~ixOwK#w%v=4{XJnyiX?IouRiylpCe!936wS zO>@IB86~iIt1r>!cO%`TP8PjwzL=StCf`?Om}{r>jTvn1amGOLlMU7s#&H4XSlSQ` zZpHDpvO@Cn1K(bQb3;7cnVgd1B}%E}3Ye%HT7KcgZ_liYNhFSu>Wv+cL8D7H_cavJ ziCn~f|H$(RXW}` za@CG(brRE+)j3(Y#h8-*>yK=RvBvu!LId?hCFt2nbtt_wl9F;wEJq1RGGRX{B9M@b z^VoFste%#d!t*}>l^MQRmzZizacp#`nx^MvR8)Qrx;P);mr3{@-D5e`gS#Stgr^}3 z$^ATci6{JHO4lU}qFJjE<&jk5wK++u!~NWo3vz1J+Z#CiOYjvTabgh$EY9EE-KWa` z2FZY3tumxU1k(sf(A_u*AL_!%2X~CZ7F0;cQ6ipM&ezk5_C;w@Y|@6!BFfaWvO6CprJ()>?^#5&km^ z7hmrfR7iehwS{+Q&iLVu?^7-#W1`t;>lAB2`i1 zwcW6k?*OmA=wyd`0Agl#61n@l#MElr9?Cu=9vm6Q#K%=^{$i0}-=+0#t)-163eM2f zYYhjl+m*+iIWz9zBO5KxBF>qwlVw`tB0RkdWG;Kc5BR;!+W$c~LwbXBfo%@5NLJEz zW1%Y)Vt5?R$|*(*w-+P4#5of2;c)+V6Gh({=Mrp!Z{r&a7u}QAg%R7>nvFJ*=<*(B zl^ugvW(}vfRB(QTZx6*s*Ro9KVWt=%p-BV8_T>Nv;=osv9bdvN1Me&l2(y<6_AzC zpssNfSDpK!|BruzqvS^yc%yESVcNm~(r%01Y_+C5l3wNN2V~}=bmqq5Rn5!$^jYP+ zANz|gKE?4a)coBmH0HdYt?{_7qg4xd%if>GA=pX3XwfHYp;a!@kS_x6fR!X8)s}bW z2t_%wI*#`b;6Eun9@ z`JvIo-6vC;lP^o;`Vfxe1~^PzMp*AuO9(q+tK!@G*8Ma+a86Jej> z9Yn_?l*CpEq%JPzzO|Q8k(%cH`Z7OtM&6>9QNMe?w|15F$D6Ax*VZOFVdn$zW&0Hlf^z zF*7LczC8cnNWwGo?tMh-l-y-@YZc6)#Wl?0T{x|d8SP*B%F}e4b-eb2t56yY?>^u| z8qIk0&3MWyeL~jsu#ZNM_Ani($3L3SLu^*@N*eS)T0qH@edSBjXu-nLDlYWTZaxwK zKsH~l$L_1Y7DycywWd;psHq_WhM)42K74FnibyiGBEX-}oTB`b(joH)jtr@NdI$;YRR$)e80qpE(V+{KmK` z%BLtITi0-I6>&GFUzr2m*n<-7z$13!P1hYN>ijlbV+=pRM%yDVaKEb93_8v;00JMu zg?5%q(Tb~9B8kT>3gjUj<_X$JYj$0k1ILu`m6yA-g1PO0#xEJ!%XPkjx=Y8wW~i7d z&9@3j?u=jqEE`kEU{VC8C$0RnK)tk?QWD*_W;ng(%l;@UA(gEF+J{ay=@M*pl^Shu z4N=;%*ja#0{;ypeYHsUblsz24X^M0565IYZZb`zk+M@r zz;ra3_SgC1CbCUI+J}Jc(I!46uDwKWefm?;HOq368pIIUpCg_nwu(qpYtD0ES%rtO z&$hL~`8+c7zPHg^oG3B)a75&UOnAmpHi8nAPTm4Y-^OHQe4vki< zA2-CguUs>&=4?kjYf<*Z!=^ha#M<0lGz9JlqH^$*aNUP^cRM`C`Ae^h(I`l%^mJy^D+Z#cF+yYKPN1h4L+51 z#}C5psxtXThI?N=1HR|qB;~ZPs9?WHL_ESgNV>)Y_;AQpovWb4F^CcUgUhWDrc^ky z(T#UBtiWl%j$L?QC(%1Bg}2(xyuMc5yJa%!6;R@_gOkS9%}oFG!c;TU+;YVSM$;Z~ za0_m^q)rFgY=Qv8$rNv}zSn8?D&!DwMF9?woqo<-9rI~sr zQE+H=>(JzVaUYNKlvQ|WKPfA-x33-T?x&LJSSinIyU`;H)=pGWuF?e5>|342jt;GtapX0!=K2KmxYV zFpMH%oeK?l4Q22zHaMKNFrh)_eDL&@sNi#2dQf6j^~*y0>q#=Srgpt1h9_$e`VA+nEfG3} z;+q<1025=7a2OhWdG~s(>q-Wx^}?1$g@VE&8zDoO)PRX~;#P4by!H>W#b)|+KRDiH2~Mw;h=<4#A(vq&WW!0H+A|ES zrh;mfPZu^X#}c7;#&nQo#k>dn0XJ*V&5Hd*Rx60CgLvS}CZR<2faiV%#r^mo*%m6Z zTxW_fJhd9tQz)M|n)I%{DR<{aKJ%IGbX&w*S4~r%8Zor5c*|yg1M`-?9ZUyo&Tkpj zTA#IC$Euh*AF7l#N9*ngbaSBwoQ=JEQacXxas?Q-hQLt-dH=qd4!i)qDh2Ju;TIixOa>cO9D1hPf z*8R!L{dg_z6)Wt%1Aynu=)@h@D}oM@nz0;ppRlq-@peIyr%@a@5y38PxnVt6VE?__ zR*UhZtQ+HR_~q)kpCBf;hm@SQ(%PmFMjL|{S+Nq4;^YQrBBK>=O}vWL>QIf7<;&Ey zc2eXZNzb`J+@xQZitsg?d-Z%(4+#|on4|Pwu_o=+1qSy-c5vO^l5s#9I)U^+%5O1g9Goj=UQ=)x;ZC<664{923~$|LeEdmp;@bsfcpYbO zIdlp$wyVNKgs0)+8Q{v-)Y?wwsOCI%p??RYGk&ckptzSWu1WSn%Z4URudlPDu>vc7cT*#w^S@EUfd1~&cLc8;<(D3@W@kT zv4ajY8uQn@giWm`cburgW74=6<8s z;)1Xy65+Ql}FGU(1Y*a1p#GSX3@bGLTO zec6Ap8(ZtSg@9C9R=t*VtnIanNhEDABkF*NH9vhGp3v(sA$V72h!dI0V|>%^=Sj{J z%03tA#*F0|BB6Acz`pksig&EDiT+Ls_hl6AUdtDr)|TsF|}I zeXS8~6Lv5}I6d^S(6yr{j3574HS(+M)Jbp{hZ3^IIL~c|@x&oA=rlDiU^1$AOi7z2_*>w8>a+$Z2xiT>qu(gU0`a;6oIq))HN953ogn*K2goQY+ z5|ap{t&Ju)2j^b!W!>x**KrF}wS=wEBTEkw*<xtdQY|)}mvatv2+?X2^DOWswo--mguj_FBkmeg6W~!w zeiE|^;%0#`^JKui&_NhThzG!cC~4Z)T)<^}dK&91>f3!j>zg#Rv6CJp*5Z<%x;=LI z=NWR^$^^-aiAAmV3u7gJ3f$$209!>ITchRTyqDU;4$bAz&A$ z`$Ja(wkJA*ONE>naGDrk3R$qOnWDI|!U}`tW)`|=DXO7O`{qFv)Kr9oS~Jwl%IV<{ z%@kDb>TwBSp8MrM7vY{59$d+EdNB~2C+UVpTq7(z@<8JHwzG7cHvbz1S|Z+bG{&u! zfHIKrl^8n^Pk5lDP-{{yVRJb+J^FE1rydUh4h%~ogwLnWLX*ZbIijy^(uK|I54&ww zBGT!Ui<9e?_B_2hFK!R4cHZS!*wLj0%qWmUo5zM*Gh3tIW7rw#mNt?iqr?8V-10yJ zzMhAs*8RXWCbI$BVo%}fw9`&|WfWuRk3BJqCaMdH?Lb!Z6l+v>P+$=Uo5>LKx?*!b z%dMtKYbqN;z+pGYS@i4_j+STd;~(mLi=3Vw;fs9Ae0Ej|iC_REO~qJyB0k_OdJ6eb z{F2g)?QX&lsq!h=`*Yi% zc{Q$}W`H;AAuE)|1|&P2JpTzB1~ABkN&D>NPT5F{_lDsM+XP|F!~`mptm3-@;sO)> z?JtSUL>g+5vsEmt$Q9aKk?f)f$bfLs?`I!5f#o46h1&6c%X3>qVVt=GZ|aD{Kh3ji z`K$SwiAjR!rI4I?QT{EIfpE$q{|hf%5qvX_FAUlXIwaC-@a9JeFGmU6nu7hE1UbkI z#|Jz$he3>c$`KRp2~o<>(-pEMajd`)lMv3uuZ8o~WcOpZ$oLD^qSE@vXX5u(?^C{Q z4P0cTK_cfvmq+F;vCdAkv+Oa^q}=cVlmFZ!W*;GU|G!EdpQTvq*&Of!6j-!d^^oP| zz-BYP1S#XoBePce1&OL=MOB5t$JG6>T85UZ?n+XEHH6R4ft1gsQHolJ31@vpoI>-p zVp{4C3*e)ZB%_lvrt>&i0)ikkxq`k*iItaZh`s6K4IQ3<2?owZXC$gr*No35iN5|Cf*J2$ z^XCkXw~Qnp;}gz9Wmd2gCbh{<9HFPh-^d&n_r2ZKMcT)qgiN*v4qEjt=1v5uz0J1s zG$AjLskR;niK=G&#}TeMdp(3L=b7p6|3Jz2akgebkY`XP3;a`l{%@)KSM=&WnY#=B z-xTZre3bx_|4W}}|4+{S&(Ao-e@fEw z=SpIp@q|$`iVIH7@SerAo6-LQ=7W-?p@5_RxR3;;Vq}r}|IlXXW_9Tsy9->~9LCrS zM+Nyb&t%84rG!L8$SUM=!CHLq1Cx@lHd1>cySs(od~EO_C_@7L`@~7(6_>w0ER@TSo$aFCJ+fCD=U`!ngp|CYl9I;5c1vwAc zuj1m}!BG)5^E1ic`_BKHx(|W_%lOLt2WSv?c{H_|HB@m8%7$g#^bjyXpAQVB2X4G~ zJ(2E;%(|NGqm!VgC#V>r4*`9HtlRIOZJ*oV2Q2y!kdS`s9d5`NUpEcx#{MV2|2NRc}eARtu4(j-$Wb z_4NoIq4eJr6W0lL@*iYt2b+Hx6ug`JTtH?#BaP@tsjBmm<&qr~*;`aunl0esMy`VC z>MY;ezdvfeXR8#L8u_h@@mHhn)7{}=s{hv<`%^3taOwafqURIs-+JqR3dMdIDZ1+h$`|3>e&l~Xe~=-Zzy z@R;CkuyIK#z4sI;;)3lgA>?{@jTvBimj>TviHL7_P8@x(X$Z z)4G4>5kmBi+h@?)aj~sjyn}>A-Rtce(DkF|=EfP{p9f>~IeZXOrM0s~HKc z4}N#aWay@t?bB?)zI__59@Nz=7(yCt%l!VlVfRKs$VT zk!1fZ|I+K0?(qO+^O`9;VMAHOzK6#i0(EY6n(R4X<4F#~ADMRdFj?`C{(3kS`)3n;E8os}<4C4& zRqt^}zUklvu;N9;D{q5NLm?5GDtF6#Hd*w2K_yjIu(d4*2%-A=y&pY@Df?&lpf#*a zj4jx-dEXQ0oSiv(upoXFY6PeE_PkP?#3g+x`&LH*W@55a7MDESl>6-bfL*^xI8f}i z#a6necbCx_#e{j9NaKq-AZ%&65X*ia27+ zjfq9{mLQG2I9P9Xr`;TeUGQ3P|0evx!S8lDoi&Mgtn(+_#szq@m+VRMh4huB!_5*C z$Q*`Ic6yBk?KpKyu!+u=wlHy++p#k=ODGmP^5eR~%adH_70KkPe_(y|G&r#@a3O28 zj{frDOnPftcpEW#T1Mk6G6p25ddowbdw`^2B=43**}0ehiE^xRGptiu3O z$#EDKwf0{=e>x{)J#C)6nWgm*Hw$?`eMk+<)FOAk-p248u}2S>R2&;N3l4QuQ8Bma z)ec?za48D=?{e3zuRcQ>7*$BM)kluF-Aw8BCr5(CavYZ_qwf9T^n-SMd-0Rk#k(m_P6{UE>dQ_2qSMvlih5FOCK)XlCcdHOOd5mUC=;A;%zGc{9FI>v&C%5@h zOuScHYC;NY5G{2mbpbd$6zrWaoe94>Y`euNy~^mUzETybJvzwxv`*B!hHg7mr!*>rueBr{Uj8sf5fv}r_L zJ5*47&%VnYX7O<-_Ubm$;|`i4joV#{X0w_S@Y)D_J-uLw{yRM7<$Ha6*tr3q7AOEj z(&Xp?Q`r2k@_t=DxU}C9j5iu|;4gH&lm?-5 z4jyY{=H3f`d4D`>y>%XPAEw5Xi~|ePJjlbBzb%l3XA0o3nO>3c2PCw9K8g_bMYO@T z;yg#V8UDAk$Y~#WO8nPxu#YVchX?ry7nnW|=6B#S>I~g*5poD}-hlqnKS$&y%sEK* z$7Xu8P|s}bn0S1c*H5cX&dzMxPUr+C(-}nRJOnF|kWY6EF_60T+bruSi0dp$uB1;R zv)t_l0@(_WUzNTP*?I6|n^SI! z2WjB`?wJq|mr!W4%z<#g00dSKT}n&W$M2faH1O{=vT$9c|gJFZ%t06x&fpwDjcmmjM3pkeV& z8+|O=Ae`Obk;jk;Cw^%ay>ro*zQlA6h6yCpel?Y#s2?lWOGGMTiVU}NIa^h>^)BH4 z+;KA|Lz&L{z_I!8do;GE_|HXhy!HX1MUk)`oc1`4!`KT5W7(GdSYQ37>mNy95Ju3qSsl&eP=2)089tZb-%r9PoR_--k;z_>ddMl;Hs1#2+ z4#yHt4muf>+5-k1?fECSo`Scg%=d~sE{GXuc$9M~pYoyo3_}|(fi=$$qEJPI-Ct9= zkxO^80;hkNA(1p?$UD*X^q@Vwv8I?r3$~f5C#C8&6`k5FIJS0VTB(2Rh9}S1Pfx3- z`^=E8{p7ORDg#+QNxhq&oVV1|W*&~Fycgf9@D2F86o%HNky=$sBnaEPH{$FS8ROYHT*(Ewhv65$i3&*`i;o}9nrOPU+2)FaM7EeHEP1NAu^*+t!n3cTU*OiFbVf-!(Ip}|#x z-@{hErDZEGF8cW62w8cZnA`4_qQ9HnVZM3f4lbYx_#p&jlV|=}WeV7|TL>IOfq%-@ zpGr#)z)WJF?dk%;hjg1i1nH$AF8!^Fvpb&uMZ|o~v%=mf!H;wD2tkhG}i){yrn=H~K_VCOT>WwSm#}V7eYJ{*P1wMcuig zIfnVE)ZTq!7yOnp1U9-cA`ds@f$&N5^JiJ2v4vv4kpoU9v+d+68g%tvI8l07?;Q;P}S$J+4%rXM{Yqo1ML6UK0gAcDc?pJLDoY20I0F}=<-%6 zQiBH5tlaqhQ{$ABz@5e5o`zwOI(%1yUL!A{hGba~D3S7c>g#(AsZb2d19o-Wm7FhX zKHbSQ|7^#jcZihd19ZP5;LELAgHf4d&g!97ICKn_s!goTZp(~jvwQ}hGU6RySgB&g zN7jEWV*v?q7iy=55v|>DgmiRCja0usnQSOE-&F@5zts5he#kmQ1}&ds;KJZOmtPh%pUlcvp3G%7xW-u|Tb_}Tof=G!sJM9XYZ;oQZacLDqk z&oE%rB-+sLe%IuXx3AtcCMNnU&9-{O^TOxH%T1&li}q%QYj1ef(}1&GvR(Be#%tU6 zdzPpNy@?-l7OmdbxT`BGXgvTTAzXvu>M1<;OM~IYe@S-$(k!d}-K0h(#(#yo-`+eR z)GjCbk^N7Ev#M6s>Wwd9{E63-m57B+?g~V77OU`7eMUI!_4L)eAhc&HNGrO|OL_b` zU^NwYZ{s4}K2D_w(>DL$WOnX*HW}y{I!#B;8j}SyaW@835EHL16{OAn&L3iECEA-1 z5Mi+x&hr^r?XTMsP@i8+D@Gr88U3VP+skBk@(mESQjbqHvKcz01kTl@K!$g8Xt#0+ zKEFoa>JRnryREN2X5#acU{b7w;_uLeh8A)cp3@lcE0{KXR(Q6 zn^UojY=_IW@66XYv0hVWf?=;ElxgccPOoi%0A?^SDz~qaV&Awg?+;F$m}pbKgzr@> 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

TZbu)& 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3ffb6d7eefdcef0cababfd3166e94012eb328cc5 GIT binary patch literal 102691 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

(<;*rg(znV$Rn@BNgE-Q?ELYkm( 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 e_&;>Kfx)#__~amwo@9mtexPz{vSrdHFaHS)g^C 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 Date: Tue, 24 Feb 2026 14:38:56 +0100 Subject: [PATCH 63/71] update use case --- docs/use-cases/README.md | 2 +- docs/use-cases/portfolio-management/README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) 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 7602ba7212b..817ed3bbad3 100644 --- a/docs/use-cases/portfolio-management/README.md +++ b/docs/use-cases/portfolio-management/README.md @@ -1,9 +1,9 @@ --- sidebar_navigation: - title: Portfolio management and custom reporting + title: Portfolio management priority: 990 -description: Step-by-step instructions about portfolio management and custom reporting -keywords: use-case, portfolio management +description: Step-by-step instructions on portfolio management with OpenProject +keywords: use-case, portfolio management, portfolio --- # Portfolio management with OpenProject From eb1c296f7fc425b8a85bfc1d833f6a997a31746b Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Tue, 24 Feb 2026 14:59:20 +0100 Subject: [PATCH 64/71] Adapt test to correct title --- spec/features/projects/favorite_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 6dead66a519b8fda95367ecfba51e493d5656cec Mon Sep 17 00:00:00 2001 From: Jan Sandbrink Date: Tue, 24 Feb 2026 15:04:33 +0100 Subject: [PATCH 65/71] Update openproject-token Pushing mcp_server down to enterprise professional plan. --- Gemfile | 2 +- Gemfile.lock | 11 ++++++----- spec/features/admin/mcp_configuration_spec.rb | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Gemfile b/Gemfile index 14aec14508e..4e5198f35a8 100644 --- a/Gemfile +++ b/Gemfile @@ -202,7 +202,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" diff --git a/Gemfile.lock b/Gemfile.lock index 54235568b40..9d7faa55d82 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -821,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) @@ -892,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) @@ -1668,7 +1669,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! @@ -1994,7 +1995,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 @@ -2046,7 +2047,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) 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") From 26e0a23f1f06290ee68016b156e3531ae0f4c720 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 18:44:24 -0300 Subject: [PATCH 66/71] Bump minimatch in /frontend (#22095) Bumps and [minimatch](https://github.com/isaacs/minimatch). These dependencies needed to be updated together. Updates `minimatch` from 10.1.1 to 10.2.2 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v10.1.1...v10.2.2) Updates `minimatch` from 3.1.2 to 3.1.3 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v10.1.1...v10.2.2) Updates `minimatch` from 9.0.5 to 9.0.6 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v10.1.1...v10.2.2) Updates `minimatch` from 5.1.6 to 5.1.7 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v10.1.1...v10.2.2) --- updated-dependencies: - dependency-name: minimatch dependency-version: 10.2.2 dependency-type: indirect - dependency-name: minimatch dependency-version: 3.1.3 dependency-type: indirect - dependency-name: minimatch dependency-version: 9.0.6 dependency-type: indirect - dependency-name: minimatch dependency-version: 5.1.7 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/package-lock.json | 466 ++++++++++++++++++++++++------------- 1 file changed, 309 insertions(+), 157 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9d5fe5d0543..5bebbff1c2c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -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", @@ -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" @@ -15128,6 +15175,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 +15203,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" @@ -16020,16 +16086,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 +19276,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" @@ -24221,13 +24305,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": { @@ -24269,12 +24365,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" @@ -29112,19 +29208,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", @@ -31047,12 +31130,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" } } } @@ -31604,13 +31700,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": { @@ -31635,12 +31737,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" } } } @@ -31792,13 +31894,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": { @@ -31817,12 +31925,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" } } } @@ -31850,13 +31958,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": { @@ -31869,12 +31983,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" } } } @@ -31951,13 +32065,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": { @@ -31976,12 +32096,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" } } } @@ -35086,9 +35206,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" @@ -35342,17 +35462,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": { @@ -35975,12 +36108,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" } } } @@ -38096,9 +38242,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" @@ -41541,13 +41687,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": { @@ -41572,12 +41724,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" } } } From 69a6494e8d2e229602e9999a7116a9933079848c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 18:47:47 -0300 Subject: [PATCH 67/71] Bump the angular group in /frontend with 14 updates (#22056) Bumps the angular group in /frontend with 14 updates: | Package | From | To | | --- | --- | --- | | [@angular/animations](https://github.com/angular/angular/tree/HEAD/packages/animations) | `21.1.3` | `21.1.4` | | [@angular/cdk](https://github.com/angular/components) | `21.1.3` | `21.1.4` | | [@angular/cli](https://github.com/angular/angular-cli) | `21.1.3` | `21.1.4` | | [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `21.1.3` | `21.1.4` | | [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `21.1.3` | `21.1.4` | | [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `21.1.3` | `21.1.4` | | [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `21.1.3` | `21.1.4` | | [@angular/elements](https://github.com/angular/angular/tree/HEAD/packages/elements) | `21.1.3` | `21.1.4` | | [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `21.1.3` | `21.1.4` | | [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `21.1.3` | `21.1.4` | | [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) | `21.1.3` | `21.1.4` | | [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `21.1.3` | `21.1.4` | | [@angular-devkit/build-angular](https://github.com/angular/angular-cli) | `21.1.3` | `21.1.4` | | [@angular/language-service](https://github.com/angular/angular/tree/HEAD/packages/language-service) | `21.1.3` | `21.1.4` | Updates `@angular/animations` from 21.1.3 to 21.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/v21.1.4/packages/animations) Updates `@angular/cdk` from 21.1.3 to 21.1.4 - [Release notes](https://github.com/angular/components/releases) - [Changelog](https://github.com/angular/components/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/components/compare/v21.1.3...v21.1.4) Updates `@angular/cli` from 21.1.3 to 21.1.4 - [Release notes](https://github.com/angular/angular-cli/releases) - [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular-cli/compare/v21.1.3...v21.1.4) Updates `@angular/common` from 21.1.3 to 21.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/v21.1.4/packages/common) Updates `@angular/compiler` from 21.1.3 to 21.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/v21.1.4/packages/compiler) Updates `@angular/compiler-cli` from 21.1.3 to 21.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/v21.1.4/packages/compiler-cli) Updates `@angular/core` from 21.1.3 to 21.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/v21.1.4/packages/core) Updates `@angular/elements` from 21.1.3 to 21.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/v21.1.4/packages/elements) Updates `@angular/forms` from 21.1.3 to 21.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/v21.1.4/packages/forms) Updates `@angular/platform-browser` from 21.1.3 to 21.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/v21.1.4/packages/platform-browser) Updates `@angular/platform-browser-dynamic` from 21.1.3 to 21.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/v21.1.4/packages/platform-browser-dynamic) Updates `@angular/router` from 21.1.3 to 21.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/v21.1.4/packages/router) Updates `@angular-devkit/build-angular` from 21.1.3 to 21.1.4 - [Release notes](https://github.com/angular/angular-cli/releases) - [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular-cli/compare/v21.1.3...v21.1.4) Updates `@angular/language-service` from 21.1.3 to 21.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/v21.1.4/packages/language-service) --- updated-dependencies: - dependency-name: "@angular/animations" dependency-version: 21.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: angular - dependency-name: "@angular/cdk" dependency-version: 21.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: angular - dependency-name: "@angular/cli" dependency-version: 21.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: angular - dependency-name: "@angular/common" dependency-version: 21.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: angular - dependency-name: "@angular/compiler" dependency-version: 21.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: angular - dependency-name: "@angular/compiler-cli" dependency-version: 21.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: angular - dependency-name: "@angular/core" dependency-version: 21.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: angular - dependency-name: "@angular/elements" dependency-version: 21.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: angular - dependency-name: "@angular/forms" dependency-version: 21.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: angular - dependency-name: "@angular/platform-browser" dependency-version: 21.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: angular - dependency-name: "@angular/platform-browser-dynamic" dependency-version: 21.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: angular - dependency-name: "@angular/router" dependency-version: 21.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: angular - dependency-name: "@angular-devkit/build-angular" dependency-version: 21.1.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: angular - dependency-name: "@angular/language-service" dependency-version: 21.1.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: angular ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/package-lock.json | 545 ++++++++++++++++++------------------- frontend/package.json | 28 +- 2 files changed, 286 insertions(+), 287 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5bebbff1c2c..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" } }, @@ -7046,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", @@ -8345,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": { @@ -8360,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", @@ -8386,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", @@ -8508,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" @@ -15049,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" }, @@ -26135,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", @@ -26155,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", @@ -26198,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", @@ -26355,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", @@ -26573,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", @@ -26617,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", @@ -26680,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" @@ -26704,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", @@ -26730,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", @@ -26752,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", @@ -26840,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" } } } @@ -26904,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", @@ -27021,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" } @@ -30042,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": { @@ -30736,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", @@ -30759,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", @@ -30832,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": { @@ -35406,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", 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", From 98a166e3ba0a4c7c67c1e9fede7712f8d3ae9460 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 18:48:20 -0300 Subject: [PATCH 68/71] Bump nokogiri from 1.19.0 to 1.19.1 (#22043) Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.19.0 to 1.19.1. - [Release notes](https://github.com/sparklemotion/nokogiri/releases) - [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md) - [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.19.0...v1.19.1) --- updated-dependencies: - dependency-name: nokogiri dependency-version: 1.19.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile | 2 +- Gemfile.lock | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Gemfile b/Gemfile index 929e3db4973..f8359035c31 100644 --- a/Gemfile +++ b/Gemfile @@ -192,7 +192,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" diff --git a/Gemfile.lock b/Gemfile.lock index 29ca2169f99..8470177c7f8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -843,21 +843,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) @@ -1638,7 +1638,7 @@ DEPENDENCIES 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! @@ -2005,14 +2005,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) From 3e057ae25f491ccb371fe04a23d604fb3a7a1fbd Mon Sep 17 00:00:00 2001 From: OpenProject Actions CI Date: Wed, 25 Feb 2026 03:56:02 +0000 Subject: [PATCH 69/71] update locales from crowdin [ci skip] --- config/locales/crowdin/af.yml | 10 ++++--- config/locales/crowdin/ar.yml | 10 ++++--- config/locales/crowdin/az.yml | 10 ++++--- config/locales/crowdin/be.yml | 10 ++++--- config/locales/crowdin/bg.yml | 10 ++++--- config/locales/crowdin/ca.yml | 10 ++++--- config/locales/crowdin/ckb-IR.yml | 10 ++++--- config/locales/crowdin/cs.yml | 10 ++++--- config/locales/crowdin/da.yml | 10 ++++--- config/locales/crowdin/de.yml | 12 ++++++--- config/locales/crowdin/el.yml | 10 ++++--- config/locales/crowdin/eo.yml | 10 ++++--- config/locales/crowdin/es.yml | 10 ++++--- config/locales/crowdin/et.yml | 10 ++++--- config/locales/crowdin/eu.yml | 10 ++++--- config/locales/crowdin/fa.yml | 10 ++++--- config/locales/crowdin/fi.yml | 10 ++++--- config/locales/crowdin/fil.yml | 10 ++++--- config/locales/crowdin/fr.yml | 11 +++++--- config/locales/crowdin/he.yml | 10 ++++--- config/locales/crowdin/hi.yml | 10 ++++--- config/locales/crowdin/hr.yml | 10 ++++--- config/locales/crowdin/hu.yml | 10 ++++--- config/locales/crowdin/id.yml | 10 ++++--- config/locales/crowdin/it.yml | 10 ++++--- config/locales/crowdin/ja.yml | 10 ++++--- config/locales/crowdin/ka.yml | 10 ++++--- config/locales/crowdin/kk.yml | 10 ++++--- config/locales/crowdin/ko.yml | 10 ++++--- config/locales/crowdin/lt.yml | 10 ++++--- config/locales/crowdin/lv.yml | 10 ++++--- config/locales/crowdin/mn.yml | 10 ++++--- config/locales/crowdin/ms.yml | 10 ++++--- config/locales/crowdin/ne.yml | 10 ++++--- config/locales/crowdin/nl.yml | 10 ++++--- config/locales/crowdin/no.yml | 10 ++++--- config/locales/crowdin/pl.yml | 10 ++++--- config/locales/crowdin/pt-BR.yml | 10 ++++--- config/locales/crowdin/pt-PT.yml | 10 ++++--- config/locales/crowdin/ro.yml | 10 ++++--- config/locales/crowdin/ru.yml | 12 ++++++--- config/locales/crowdin/rw.yml | 10 ++++--- config/locales/crowdin/si.yml | 10 ++++--- config/locales/crowdin/sk.yml | 10 ++++--- config/locales/crowdin/sl.yml | 10 ++++--- config/locales/crowdin/sr.yml | 10 ++++--- config/locales/crowdin/sv.yml | 10 ++++--- config/locales/crowdin/th.yml | 10 ++++--- config/locales/crowdin/tr.yml | 10 ++++--- config/locales/crowdin/uk.yml | 10 ++++--- config/locales/crowdin/uz.yml | 10 ++++--- config/locales/crowdin/vi.yml | 10 ++++--- config/locales/crowdin/zh-CN.yml | 10 ++++--- config/locales/crowdin/zh-TW.yml | 10 ++++--- .../backlogs/config/locales/crowdin/js-he.yml | 4 +-- .../backlogs/config/locales/crowdin/sk.yml | 26 +++++++++---------- modules/boards/config/locales/crowdin/ru.yml | 2 +- .../budgets/config/locales/crowdin/js-ru.yml | 2 +- modules/budgets/config/locales/crowdin/ru.yml | 14 +++++----- .../costs/config/locales/crowdin/js-ru.yml | 2 +- modules/costs/config/locales/crowdin/ru.yml | 4 +-- .../config/locales/crowdin/he.seeders.yml | 12 ++++----- .../documents/config/locales/crowdin/ru.yml | 6 ++--- .../grids/config/locales/crowdin/js-af.yml | 1 + .../grids/config/locales/crowdin/js-ar.yml | 1 + .../grids/config/locales/crowdin/js-az.yml | 1 + .../grids/config/locales/crowdin/js-be.yml | 1 + .../grids/config/locales/crowdin/js-bg.yml | 1 + .../grids/config/locales/crowdin/js-ca.yml | 1 + .../config/locales/crowdin/js-ckb-IR.yml | 1 + .../grids/config/locales/crowdin/js-cs.yml | 1 + .../grids/config/locales/crowdin/js-da.yml | 1 + .../grids/config/locales/crowdin/js-de.yml | 1 + .../grids/config/locales/crowdin/js-el.yml | 1 + .../grids/config/locales/crowdin/js-eo.yml | 1 + .../grids/config/locales/crowdin/js-es.yml | 1 + .../grids/config/locales/crowdin/js-et.yml | 1 + .../grids/config/locales/crowdin/js-eu.yml | 1 + .../grids/config/locales/crowdin/js-fa.yml | 1 + .../grids/config/locales/crowdin/js-fi.yml | 1 + .../grids/config/locales/crowdin/js-fil.yml | 1 + .../grids/config/locales/crowdin/js-fr.yml | 1 + .../grids/config/locales/crowdin/js-he.yml | 1 + .../grids/config/locales/crowdin/js-hi.yml | 1 + .../grids/config/locales/crowdin/js-hr.yml | 1 + .../grids/config/locales/crowdin/js-hu.yml | 1 + .../grids/config/locales/crowdin/js-id.yml | 1 + .../grids/config/locales/crowdin/js-it.yml | 1 + .../grids/config/locales/crowdin/js-ja.yml | 1 + .../grids/config/locales/crowdin/js-ka.yml | 1 + .../grids/config/locales/crowdin/js-kk.yml | 1 + .../grids/config/locales/crowdin/js-ko.yml | 1 + .../grids/config/locales/crowdin/js-lt.yml | 1 + .../grids/config/locales/crowdin/js-lv.yml | 1 + .../grids/config/locales/crowdin/js-mn.yml | 1 + .../grids/config/locales/crowdin/js-ms.yml | 1 + .../grids/config/locales/crowdin/js-ne.yml | 1 + .../grids/config/locales/crowdin/js-nl.yml | 1 + .../grids/config/locales/crowdin/js-no.yml | 1 + .../grids/config/locales/crowdin/js-pl.yml | 1 + .../grids/config/locales/crowdin/js-pt-BR.yml | 1 + .../grids/config/locales/crowdin/js-pt-PT.yml | 1 + .../grids/config/locales/crowdin/js-ro.yml | 1 + .../grids/config/locales/crowdin/js-ru.yml | 1 + .../grids/config/locales/crowdin/js-rw.yml | 1 + .../grids/config/locales/crowdin/js-si.yml | 1 + .../grids/config/locales/crowdin/js-sk.yml | 1 + .../grids/config/locales/crowdin/js-sl.yml | 1 + .../grids/config/locales/crowdin/js-sr.yml | 1 + .../grids/config/locales/crowdin/js-sv.yml | 1 + .../grids/config/locales/crowdin/js-th.yml | 1 + .../grids/config/locales/crowdin/js-tr.yml | 1 + .../grids/config/locales/crowdin/js-uk.yml | 1 + .../grids/config/locales/crowdin/js-uz.yml | 1 + .../grids/config/locales/crowdin/js-vi.yml | 1 + .../grids/config/locales/crowdin/js-zh-CN.yml | 1 + .../grids/config/locales/crowdin/js-zh-TW.yml | 1 + modules/meeting/config/locales/crowdin/af.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/ar.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/az.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/be.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/bg.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/ca.yml | 17 ++++++++++++ .../meeting/config/locales/crowdin/ckb-IR.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/cs.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/da.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/de.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/el.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/eo.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/es.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/et.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/eu.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/fa.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/fi.yml | 17 ++++++++++++ .../meeting/config/locales/crowdin/fil.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/fr.yml | 17 ++++++++++++ .../config/locales/crowdin/he.seeders.yml | 18 ++++++------- modules/meeting/config/locales/crowdin/he.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/hi.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/hr.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/hu.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/id.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/it.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/ja.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/ka.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/kk.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/ko.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/lt.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/lv.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/mn.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/ms.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/ne.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/nl.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/no.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/pl.yml | 17 ++++++++++++ .../meeting/config/locales/crowdin/pt-BR.yml | 17 ++++++++++++ .../meeting/config/locales/crowdin/pt-PT.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/ro.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/ru.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/rw.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/si.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/sk.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/sl.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/sr.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/sv.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/th.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/tr.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/uk.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/uz.yml | 17 ++++++++++++ modules/meeting/config/locales/crowdin/vi.yml | 17 ++++++++++++ .../meeting/config/locales/crowdin/zh-CN.yml | 17 ++++++++++++ .../meeting/config/locales/crowdin/zh-TW.yml | 17 ++++++++++++ .../config/locales/crowdin/js-he.yml | 2 +- 173 files changed, 1398 insertions(+), 211 deletions(-) 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 ec45868f2d1..7a03a59a514 100644 --- a/config/locales/crowdin/fr.yml +++ b/config/locales/crowdin/fr.yml @@ -123,7 +123,6 @@ fr: server_form: description_caption: "Comment le serveur MCP sera décrit aux autres applications qui s'y connectent." 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: > @@ -372,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" @@ -399,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é @@ -4812,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é." @@ -4827,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}." 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 b7f06d69f15..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 @@ -4811,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." @@ -4826,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/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/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/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/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/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/ru.yml b/modules/documents/config/locales/crowdin/ru.yml index e35a01ad7ff..eb3fbbb3e73 100644 --- a/modules/documents/config/locales/crowdin/ru.yml +++ b/modules/documents/config/locales/crowdin/ru.yml @@ -24,7 +24,7 @@ ru: name: "Документы OpenProject" description: "Плагин OpenProject позволяет создавать документы в проектах." attributes: - collaborative_editing_hocuspocus_url: "Hocuspocus server URL" + collaborative_editing_hocuspocus_url: "URL-адрес сервера Hocuspocus" activerecord: errors: models: @@ -127,8 +127,8 @@ ru: some_unwritable: Некоторые значения настраиваются через переменные окружения и не могут быть отредактированы здесь. hocuspocus_server_url: label: "URL-адрес сервера Hocuspocus" - caption: "The WebSocket address of a working Hocuspocus server." - invalid_scheme: "Must use a WebSocket protocol (ws:// or wss://)." + caption: "Адрес WebSocket работающего сервера Hocuspocus." + invalid_scheme: "Должен использоваться протокол WebSocket (ws:// или wss://)." hocuspocus_server_secret: label: "Закрытый ключ клиента" caption: "Вставьте ключ, предоставленный сервером Hocuspocus." 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 674c862520b..2584a367f4c 100644 --- a/modules/grids/config/locales/crowdin/js-fr.yml +++ b/modules/grids/config/locales/crowdin/js-fr.yml @@ -2,6 +2,7 @@ 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: 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/meeting/config/locales/crowdin/af.yml b/modules/meeting/config/locales/crowdin/af.yml index a40cde2366d..37d02ad1c6e 100644 --- a/modules/meeting/config/locales/crowdin/af.yml +++ b/modules/meeting/config/locales/crowdin/af.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 adf6d511f2b..0dd3f765308 100644 --- a/modules/meeting/config/locales/crowdin/ar.yml +++ b/modules/meeting/config/locales/crowdin/ar.yml @@ -126,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" @@ -299,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?" @@ -506,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: "إنشاء الاجتماعات" @@ -520,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?" @@ -625,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 d337236b72c..aa91fd51091 100644 --- a/modules/meeting/config/locales/crowdin/az.yml +++ b/modules/meeting/config/locales/crowdin/az.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 43bfe94048e..14c5baff942 100644 --- a/modules/meeting/config/locales/crowdin/be.yml +++ b/modules/meeting/config/locales/crowdin/be.yml @@ -122,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" @@ -289,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?" @@ -492,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" @@ -506,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?" @@ -611,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 dda898327b8..da322856098 100644 --- a/modules/meeting/config/locales/crowdin/bg.yml +++ b/modules/meeting/config/locales/crowdin/bg.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 d0e43e1f758..9ef63cd5ade 100644 --- a/modules/meeting/config/locales/crowdin/ca.yml +++ b/modules/meeting/config/locales/crowdin/ca.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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ó?" @@ -597,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 b2d43e94609..a792629bb9f 100644 --- a/modules/meeting/config/locales/crowdin/ckb-IR.yml +++ b/modules/meeting/config/locales/crowdin/ckb-IR.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 758989888ab..d06d16f4f2f 100644 --- a/modules/meeting/config/locales/crowdin/cs.yml +++ b/modules/meeting/config/locales/crowdin/cs.yml @@ -122,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" @@ -289,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" @@ -492,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" @@ -506,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?" @@ -611,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 dec53c9c5b8..a02e03c4668 100644 --- a/modules/meeting/config/locales/crowdin/da.yml +++ b/modules/meeting/config/locales/crowdin/da.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 936f9c6f2f0..d85927f983f 100644 --- a/modules/meeting/config/locales/crowdin/de.yml +++ b/modules/meeting/config/locales/crowdin/de.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 7526acd66d3..d15def16097 100644 --- a/modules/meeting/config/locales/crowdin/el.yml +++ b/modules/meeting/config/locales/crowdin/el.yml @@ -118,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: "Δημιουργία συνάντησης" @@ -279,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?" @@ -478,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: "Δημιουργία συναντήσεων" @@ -492,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?" @@ -597,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 df1c07ad2dc..7255cb343dd 100644 --- a/modules/meeting/config/locales/crowdin/eo.yml +++ b/modules/meeting/config/locales/crowdin/eo.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 829560f4eb6..00325fe1e17 100644 --- a/modules/meeting/config/locales/crowdin/es.yml +++ b/modules/meeting/config/locales/crowdin/es.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 8b05c51fafe..aa7b72eca9c 100644 --- a/modules/meeting/config/locales/crowdin/et.yml +++ b/modules/meeting/config/locales/crowdin/et.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 90d66561484..c76935de7db 100644 --- a/modules/meeting/config/locales/crowdin/eu.yml +++ b/modules/meeting/config/locales/crowdin/eu.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 25e7e22066a..4417db3e544 100644 --- a/modules/meeting/config/locales/crowdin/fa.yml +++ b/modules/meeting/config/locales/crowdin/fa.yml @@ -118,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" @@ -279,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?" @@ -478,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: "ایجاد جلسات" @@ -492,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?" @@ -597,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 5540dcddc17..08b829522a8 100644 --- a/modules/meeting/config/locales/crowdin/fi.yml +++ b/modules/meeting/config/locales/crowdin/fi.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 05a1d97adc9..585e5aad842 100644 --- a/modules/meeting/config/locales/crowdin/fil.yml +++ b/modules/meeting/config/locales/crowdin/fil.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 88f565a698e..7ae2029cc9c 100644 --- a/modules/meeting/config/locales/crowdin/fr.yml +++ b/modules/meeting/config/locales/crowdin/fr.yml @@ -118,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" @@ -279,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 ?" @@ -478,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" @@ -492,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 ?" @@ -597,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 ae832af120d..a3e95a24b71 100644 --- a/modules/meeting/config/locales/crowdin/he.yml +++ b/modules/meeting/config/locales/crowdin/he.yml @@ -122,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" @@ -289,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?" @@ -492,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: "צור פגישות" @@ -506,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?" @@ -611,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 d362be60179..c5ce01bbb70 100644 --- a/modules/meeting/config/locales/crowdin/hi.yml +++ b/modules/meeting/config/locales/crowdin/hi.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 6e197064ac8..6808bd82efa 100644 --- a/modules/meeting/config/locales/crowdin/hr.yml +++ b/modules/meeting/config/locales/crowdin/hr.yml @@ -120,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" @@ -284,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?" @@ -485,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" @@ -499,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?" @@ -604,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 f238931fb9b..b60274c9397 100644 --- a/modules/meeting/config/locales/crowdin/hu.yml +++ b/modules/meeting/config/locales/crowdin/hu.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 87e839ed3ef..70c8b50eb78 100644 --- a/modules/meeting/config/locales/crowdin/id.yml +++ b/modules/meeting/config/locales/crowdin/id.yml @@ -116,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" @@ -274,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?" @@ -471,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" @@ -485,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?" @@ -590,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 4b6966b5ddd..1f132fcd611 100644 --- a/modules/meeting/config/locales/crowdin/it.yml +++ b/modules/meeting/config/locales/crowdin/it.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 7b19e8340f4..cf508ab71d3 100644 --- a/modules/meeting/config/locales/crowdin/ja.yml +++ b/modules/meeting/config/locales/crowdin/ja.yml @@ -116,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: "会議を作成" @@ -274,6 +282,9 @@ ja: heading: "この会議を削除しますか?" confirmation_message_html: > この操作は元に戻せません。続行しますか? + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "会議のキャンセル" heading: "この会議をキャンセルしますか?" @@ -471,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: "会議を作成" @@ -485,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: "会議アジェンダを終了してもよろしいですか?" @@ -590,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 dff9498ec66..2efc4cdf9c5 100644 --- a/modules/meeting/config/locales/crowdin/ka.yml +++ b/modules/meeting/config/locales/crowdin/ka.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 6a6c5d17f68..2a621f920f6 100644 --- a/modules/meeting/config/locales/crowdin/kk.yml +++ b/modules/meeting/config/locales/crowdin/kk.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 0f9ef52f1dc..8a9352355c2 100644 --- a/modules/meeting/config/locales/crowdin/ko.yml +++ b/modules/meeting/config/locales/crowdin/ko.yml @@ -116,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: "미팅 생성" @@ -274,6 +282,9 @@ ko: heading: "이 미팅을 삭제하시겠습니까?" confirmation_message_html: > 이 작업은 되돌릴 수 없습니다. 주의하여 진행하세요. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "미팅 항목 취소" heading: "이 미팅 항목을 취소하시겠습니까?" @@ -471,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: "미팅 생성" @@ -485,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: "미팅 어젠더를 닫으시겠습니까?" @@ -590,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 9880ae00b62..dbc68a9267e 100644 --- a/modules/meeting/config/locales/crowdin/lt.yml +++ b/modules/meeting/config/locales/crowdin/lt.yml @@ -122,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" @@ -289,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?" @@ -492,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" @@ -506,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ą?" @@ -611,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 2ae8d08589c..5fbe4e3b3c9 100644 --- a/modules/meeting/config/locales/crowdin/lv.yml +++ b/modules/meeting/config/locales/crowdin/lv.yml @@ -120,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" @@ -284,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?" @@ -485,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" @@ -499,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?" @@ -604,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 b29cc67fad5..7d40a116f67 100644 --- a/modules/meeting/config/locales/crowdin/mn.yml +++ b/modules/meeting/config/locales/crowdin/mn.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 f5f45b61984..7e718524ab4 100644 --- a/modules/meeting/config/locales/crowdin/ms.yml +++ b/modules/meeting/config/locales/crowdin/ms.yml @@ -116,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" @@ -274,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?" @@ -471,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" @@ -485,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?" @@ -590,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 800a3caf119..d086140dee7 100644 --- a/modules/meeting/config/locales/crowdin/ne.yml +++ b/modules/meeting/config/locales/crowdin/ne.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 98bff768dc6..3e9ee2a2133 100644 --- a/modules/meeting/config/locales/crowdin/nl.yml +++ b/modules/meeting/config/locales/crowdin/nl.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 16164b38af8..d1d844f21d9 100644 --- a/modules/meeting/config/locales/crowdin/no.yml +++ b/modules/meeting/config/locales/crowdin/no.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 508c28c61dd..56a3c1c0cd9 100644 --- a/modules/meeting/config/locales/crowdin/pl.yml +++ b/modules/meeting/config/locales/crowdin/pl.yml @@ -122,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" @@ -289,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?" @@ -492,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" @@ -506,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?" @@ -611,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 7b585539dc9..fe8478ef851 100644 --- a/modules/meeting/config/locales/crowdin/pt-BR.yml +++ b/modules/meeting/config/locales/crowdin/pt-BR.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 0e78e1c96cc..02f5468c954 100644 --- a/modules/meeting/config/locales/crowdin/pt-PT.yml +++ b/modules/meeting/config/locales/crowdin/pt-PT.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 da44cce76ab..36217f5af22 100644 --- a/modules/meeting/config/locales/crowdin/ro.yml +++ b/modules/meeting/config/locales/crowdin/ro.yml @@ -120,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" @@ -284,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?" @@ -485,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" @@ -499,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?" @@ -604,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 055ff3f9f21..c656ca15357 100644 --- a/modules/meeting/config/locales/crowdin/ru.yml +++ b/modules/meeting/config/locales/crowdin/ru.yml @@ -122,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: "Создать совещание" @@ -289,6 +297,9 @@ ru: heading: "Удалить это совещание?" confirmation_message_html: > Это действие не обратимо. Пожалуйста, действуйте с осторожностью. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Отменить это событие" heading: "Отменить это событие?" @@ -492,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: "Создание совещания" @@ -506,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: "Вы уверены, что хотите закрыть повестку дня собрания?" @@ -611,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 626d2afb1f0..a052d210e4d 100644 --- a/modules/meeting/config/locales/crowdin/rw.yml +++ b/modules/meeting/config/locales/crowdin/rw.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 bed1300411d..c3a20286a91 100644 --- a/modules/meeting/config/locales/crowdin/si.yml +++ b/modules/meeting/config/locales/crowdin/si.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 99d4610909c..0099b207b19 100644 --- a/modules/meeting/config/locales/crowdin/sk.yml +++ b/modules/meeting/config/locales/crowdin/sk.yml @@ -122,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" @@ -289,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?" @@ -492,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" @@ -506,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?" @@ -611,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 491cd259615..a26338b80c7 100644 --- a/modules/meeting/config/locales/crowdin/sl.yml +++ b/modules/meeting/config/locales/crowdin/sl.yml @@ -122,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" @@ -289,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?" @@ -492,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" @@ -506,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?" @@ -611,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 e8ccdd6c26f..571c9d929f3 100644 --- a/modules/meeting/config/locales/crowdin/sr.yml +++ b/modules/meeting/config/locales/crowdin/sr.yml @@ -120,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" @@ -284,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?" @@ -485,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" @@ -499,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?" @@ -604,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 c4aff08b2f5..d57feb9f9ff 100644 --- a/modules/meeting/config/locales/crowdin/sv.yml +++ b/modules/meeting/config/locales/crowdin/sv.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 4fc4705bd5e..46962101485 100644 --- a/modules/meeting/config/locales/crowdin/th.yml +++ b/modules/meeting/config/locales/crowdin/th.yml @@ -116,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" @@ -274,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?" @@ -471,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" @@ -485,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?" @@ -590,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 d65bf9b46f6..07b6c8b277b 100644 --- a/modules/meeting/config/locales/crowdin/tr.yml +++ b/modules/meeting/config/locales/crowdin/tr.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 0ba19692399..35cead4401c 100644 --- a/modules/meeting/config/locales/crowdin/uk.yml +++ b/modules/meeting/config/locales/crowdin/uk.yml @@ -122,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: "Створити нараду" @@ -289,6 +297,9 @@ uk: heading: "Видалити цю нараду?" confirmation_message_html: > Зверніть увагу: цю дію не можна відмінити. + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "Скасувати нараду серії" heading: "Скасувати цю нараду серії?" @@ -492,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: "Створюйте зустрічі" @@ -506,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: "Справді закрити порядок денний заходу?" @@ -611,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 f131a50b775..96d23784fc1 100644 --- a/modules/meeting/config/locales/crowdin/uz.yml +++ b/modules/meeting/config/locales/crowdin/uz.yml @@ -118,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" @@ -279,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?" @@ -478,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" @@ -492,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?" @@ -597,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 cba07c32882..32967296f72 100644 --- a/modules/meeting/config/locales/crowdin/vi.yml +++ b/modules/meeting/config/locales/crowdin/vi.yml @@ -116,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" @@ -274,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?" @@ -471,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" @@ -485,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?" @@ -590,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 503e521a38b..216357e7fde 100644 --- a/modules/meeting/config/locales/crowdin/zh-CN.yml +++ b/modules/meeting/config/locales/crowdin/zh-CN.yml @@ -116,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: "创建会议" @@ -274,6 +282,9 @@ zh-CN: heading: "删除这个会议?" confirmation_message_html: > 此操作不可逆。请谨慎处理。 + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "取消会议事件" heading: "取消此会议事件?" @@ -471,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: "创建会议" @@ -485,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: "确定要关闭该会议议程吗?" @@ -590,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 1466eec8375..354846abb56 100644 --- a/modules/meeting/config/locales/crowdin/zh-TW.yml +++ b/modules/meeting/config/locales/crowdin/zh-TW.yml @@ -116,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: "新增會議" @@ -274,6 +282,9 @@ zh-TW: heading: "刪除這次會議?" confirmation_message_html: > 此動作無法還原。請小心操作。 + template: + title: "Delete template" + heading: "Delete this template?" occurrence: title: "取消會議事件" heading: "取消此會議事件?" @@ -471,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: "建立會議" @@ -485,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: "確實要關閉議程嗎?" @@ -590,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/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: 'סקירה' From 81f37c8656f17cb00f467495025dd569d063a5e5 Mon Sep 17 00:00:00 2001 From: OpenProject Actions CI Date: Wed, 25 Feb 2026 04:03:31 +0000 Subject: [PATCH 70/71] update locales from crowdin [ci skip] --- config/locales/crowdin/de.yml | 2 +- modules/backlogs/config/locales/crowdin/sk.yml | 6 +++--- .../config/locales/crowdin/he.seeders.yml | 12 ++++++------ .../config/locales/crowdin/he.seeders.yml | 18 +++++++++--------- .../overviews/config/locales/crowdin/js-he.yml | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/config/locales/crowdin/de.yml b/config/locales/crowdin/de.yml index bf3db88b9c6..9ce011e60e1 100644 --- a/config/locales/crowdin/de.yml +++ b/config/locales/crowdin/de.yml @@ -3235,7 +3235,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" diff --git a/modules/backlogs/config/locales/crowdin/sk.yml b/modules/backlogs/config/locales/crowdin/sk.yml index ee26eb2b7a6..53b99e7ca9f 100644 --- a/modules/backlogs/config/locales/crowdin/sk.yml +++ b/modules/backlogs/config/locales/crowdin/sk.yml @@ -41,7 +41,7 @@ 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: add_new_story: "Nový príbeh" any: "akékoľvek" @@ -146,8 +146,8 @@ sk: label_webcal: "Webcal Feed" label_wiki: "Wiki" 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" points_accepted: "body prijaté" points_committed: "body odovzdané" 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/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/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: 'סקירה' From 8eb8403a98c1cac73e19901f4a49b0a537b4ec98 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 05:34:52 +0000 Subject: [PATCH 71/71] Bump rspec-rails from 8.0.2 to 8.0.3 Bumps [rspec-rails](https://github.com/rspec/rspec-rails) from 8.0.2 to 8.0.3. - [Changelog](https://github.com/rspec/rspec-rails/blob/main/Changelog.md) - [Commits](https://github.com/rspec/rspec-rails/compare/v8.0.2...v8.0.3) --- updated-dependencies: - dependency-name: rspec-rails dependency-version: 8.0.3 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Gemfile | 2 +- Gemfile.lock | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Gemfile b/Gemfile index f8359035c31..3ec1ae5c6b5 100644 --- a/Gemfile +++ b/Gemfile @@ -268,7 +268,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 8470177c7f8..ebdfd28896c 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) @@ -1181,7 +1181,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) @@ -1231,8 +1231,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) @@ -1300,7 +1300,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) @@ -1529,7 +1529,7 @@ GEM yabeda (~> 0.8) yaml (0.4.0) yard (0.9.38) - zeitwerk (2.7.4) + zeitwerk (2.7.5) PLATFORMS aarch64-linux @@ -1709,7 +1709,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 @@ -1882,7 +1882,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 @@ -2149,7 +2149,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 @@ -2163,7 +2163,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 @@ -2194,7 +2194,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 @@ -2291,7 +2291,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