to so pandoc can generate a code block with correct language
- def move_class_from_code_to_pre(textile)
- textile.gsub!(/()/, '\\1\\3\\2\\4')
- end
-
- # Remove the directly inside , because pandoc would incorrectly preserve it
- def remove_code_inside_pre(textile)
- textile.gsub!(/(]*>)/, '\\1')
- textile.gsub!(/<\/code>(<\/pre>)/, '\\1')
- end
-
- # Some malformed textile content make pandoc run extremely slow,
- # so we convert it to proper textile before hitting pandoc
- # see https://github.com/jgm/pandoc/issues/3020
- def convert_malformed_textile(textile)
- textile.gsub!(/- # (\d+)/, "* \\1")
- end
-
- # Remove empty paragraph blocks which trip up pandoc
- def remove_empty_paragraphs(textile)
- textile.gsub!(/\np(=|>)?\.[\s.]*\n/, '')
- end
-
- # Replace numbered headings as they are not supported in commonmark/gfm
- def replace_numbered_headings(textile)
- textile.gsub!(/h(\d+)#./, 'h\\1.')
- end
-
- # Add an additional newline before:
- # * every pre block prefixed by only one newline
- # as indented code blocks do not interrupt a paragraph and also do not have precedence
- # compared to a list which might also be indented.
- # * every table prefixed by only one newline (if the line before isn't a table already)
- # * every blockquote prefixed by one newline (if the line before isn't a blockquote)
- def add_newline_to_avoid_lazy_blocks(textile)
- textile.gsub!(/(\n[^>|]+)\r?\n(\s*)(|\||>)/, "\\1\n\n\\2\\3")
- end
-
- # Remove spaces before a table as that would lead to the table
- # not being identified
- def remove_spaces_before_table(textile)
- textile.gsub!(/^\s+(\|.+?\|)/, "\n\n\\1")
- end
-
- ##
- # Redmine introduced hard breaks to support multiline tables that are not official
- # textile. We detect tables with line breaks and replace them with
- # https://www.redmine.org/projects/redmine/repository/revisions/2824/diff/
- def hard_breaks_within_multiline_tables(textile)
- content_regexp = %r{
- (?<=\|) # Assert beginning table pipe lookbehind
- ([^|]{5,}) # Non-empty content
- (?=\|) # Assert ending table pipe lookahead
- }mx
-
- # Match all textile tables
- textile.gsub!(/^(\|.+?\|)$/m) do |table|
- table.gsub(content_regexp) { |table_content| table_content.gsub("\n", "
") }
- end
- end
-
- # Wrap all blockquote blocks into boundaries as `>` is not valid blockquote syntax and would thus be
- # escaped
- def wrap_blockquotes(textile)
- textile.gsub!(/((\n>[^\n]*)+)/m) do
- "\n#{BLOCKQUOTE_START}\n" + $1.gsub(/(\n)> *([^\n]*)/, '\1\2') + "\n\n#{BLOCKQUOTE_END}\n"
- end
- end
-
- class OldForum < ApplicationRecord
- self.table_name = 'boards'
- end
- end
- end
-end
diff --git a/lib/redmine/menu_manager/menu_helper.rb b/lib/redmine/menu_manager/menu_helper.rb
index ae26aefc2a2..9adb563613a 100644
--- a/lib/redmine/menu_manager/menu_helper.rb
+++ b/lib/redmine/menu_manager/menu_helper.rb
@@ -31,6 +31,7 @@ module Redmine::MenuManager::MenuHelper
include ::Redmine::MenuManager::WikiMenuHelper
include AccessibilityHelper
include IconsHelper
+ include IconsHelper
delegate :current_menu_item, to: :controller
@@ -170,7 +171,7 @@ module Redmine::MenuManager::MenuHelper
class: 'toggler main-menu-toggler',
type: :button,
data: { action: 'menus--main#descend' }) do
- spot_icon('arrow-right3', size: '1')
+ render(Primer::Beta::Octicon.new("arrow-right", size: :small))
end
end
@@ -210,7 +211,7 @@ module Redmine::MenuManager::MenuHelper
def render_children_back_up_link
content_tag(
:a,
- spot_icon('arrow-left1', size: '1_25'),
+ render(Primer::Beta::Octicon.new("arrow-left", size: :small)),
title: I18n.t('js.label_up'),
class: 'main-menu--arrow-left-to-project',
data: {
diff --git a/lib/services/remove_watcher.rb b/lib/services/remove_watcher.rb
index 92bb78113d1..f0dad9d43c5 100644
--- a/lib/services/remove_watcher.rb
+++ b/lib/services/remove_watcher.rb
@@ -33,12 +33,12 @@ class Services::RemoveWatcher
end
def run(success: -> {}, failure: -> {})
- watcher = @work_package.watchers.find_by_user_id(@user.id)
+ watcher = @work_package.watchers.find_by(user_id: @user.id)
if watcher.present?
@work_package.watcher_users.delete(@user)
success.call
- OpenProject::Notifications.send(OpenProject::Events::WATCHER_REMOVED,
+ OpenProject::Notifications.send(OpenProject::Events::WATCHER_DESTROYED,
watcher:,
watcher_remover: User.current)
else
diff --git a/lib/tabular_form_builder.rb b/lib/tabular_form_builder.rb
index 09b30c678cc..0a2106ca596 100644
--- a/lib/tabular_form_builder.rb
+++ b/lib/tabular_form_builder.rb
@@ -112,6 +112,10 @@ class TabularFormBuilder < ActionView::Helpers::FormBuilder
inputs['show-ignore-non-working-days'] = options[:show_ignore_non_working_days]
end
+ if options[:required]
+ inputs[:required] = options[:required]
+ end
+
label = label_for_field(field, label_options)
input = angular_component_tag('op-basic-single-date-picker',
class: options[:class],
diff --git a/lib_static/open_project/configuration/helpers.rb b/lib_static/open_project/configuration/helpers.rb
index 018406951fb..98e44561058 100644
--- a/lib_static/open_project/configuration/helpers.rb
+++ b/lib_static/open_project/configuration/helpers.rb
@@ -190,6 +190,10 @@ module OpenProject
Integer(ENV['STATSD_PORT'].presence || statsd['port'].presence)
end
+ def lookbook_enabled?
+ self['lookbook_enabled']
+ end
+
private
##
diff --git a/lib_static/plugins/acts_as_journalized/lib/acts/journalized/file_link_journal_differ.rb b/lib_static/plugins/acts_as_journalized/lib/acts/journalized/file_link_journal_differ.rb
new file mode 100644
index 00000000000..9b0bbefd20c
--- /dev/null
+++ b/lib_static/plugins/acts_as_journalized/lib/acts/journalized/file_link_journal_differ.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2023 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write 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 Acts::Journalized
+ module FileLinkJournalDiffer
+ class << self
+ def get_changes_to_file_links(predecessor, storable_journals)
+ if predecessor.nil?
+ storable_journals.each_with_object({}) do |journal, hash|
+ change_key = "file_links_#{journal.file_link_id}"
+ new_values = { link_name: journal.link_name, storage_name: journal.storage_name }
+ hash[change_key] = [nil, new_values]
+ end
+ else
+ current_storables = storable_journals.map(&:attributes)
+ previous_storables = predecessor.storable_journals.map(&:attributes)
+
+ changes_on_file_links(previous_storables, current_storables)
+ end
+ end
+
+ def changes_on_file_links(previous, current)
+ ids = all_file_link_ids(previous, current)
+
+ cleanup_changes(
+ pair_changes(ids, previous, current)
+ ).transform_keys! { |key| "file_links_#{key}" }
+ end
+
+ def all_file_link_ids(previous, current)
+ current.pluck('file_link_id') | previous.pluck('file_link_id')
+ end
+
+ def cleanup_changes(changes) = changes.reject { |_, (first, last)| first == last }
+
+ def pair_changes(ids, previous, current)
+ ids.index_with do |id|
+ [select_journals(previous.select { |attributes| attributes['file_link_id'] == id }),
+ select_journals(current.select { |attributes| attributes['file_link_id'] == id })]
+ end
+ end
+
+ def select_journals(journals)
+ return if journals.empty?
+
+ journals.sort.map { |hash| hash.slice('link_name', 'storage_name') }.last
+ end
+ end
+ end
+end
diff --git a/lib_static/plugins/acts_as_journalized/lib/acts/journalized/journable_differ.rb b/lib_static/plugins/acts_as_journalized/lib/acts/journalized/journable_differ.rb
index fbfe65915b8..86f7fad034a 100644
--- a/lib_static/plugins/acts_as_journalized/lib/acts/journalized/journable_differ.rb
+++ b/lib_static/plugins/acts_as_journalized/lib/acts/journalized/journable_differ.rb
@@ -38,8 +38,8 @@ module Acts::Journalized
.with_indifferent_access
end
- def association_changes(original, changed, *association_params)
- get_association_changes(original, changed, *association_params)
+ def association_changes(original, changed, *)
+ get_association_changes(original, changed, *)
end
private
@@ -90,12 +90,12 @@ module Acts::Journalized
def added_references(merged_references)
merged_references
- .select { |_, (old_value, new_value)| old_value.nil? && new_value.present? }
+ .select { |_, (old_value, new_value)| old_value.to_s.empty? && new_value.present? }
end
def removed_references(merged_references)
merged_references
- .select { |_, (old_value, new_value)| old_value.present? && new_value.nil? }
+ .select { |_, (old_value, new_value)| old_value.present? && new_value.to_s.empty? }
end
def changed_references(merged_references)
diff --git a/lib_static/plugins/acts_as_journalized/lib/journal_changes.rb b/lib_static/plugins/acts_as_journalized/lib/journal_changes.rb
index f157f3814f1..10d6e9e7820 100644
--- a/lib_static/plugins/acts_as_journalized/lib/journal_changes.rb
+++ b/lib_static/plugins/acts_as_journalized/lib/journal_changes.rb
@@ -63,13 +63,9 @@ module JournalChanges
if has_file_links?
@changes.merge!(
- ::Acts::Journalized::JournableDiffer.association_changes(
+ ::Acts::Journalized::FileLinkJournalDiffer.get_changes_to_file_links(
predecessor,
- self,
- 'storable_journals',
- 'file_links',
- :file_link_id,
- :link_name
+ storable_journals
)
)
end
diff --git a/modules/bim/app/views/bim/bcf/issues/configure_unknown_mails.html.erb b/modules/bim/app/views/bim/bcf/issues/configure_unknown_mails.html.erb
index c85b0d27456..81bb290e0df 100644
--- a/modules/bim/app/views/bim/bcf/issues/configure_unknown_mails.html.erb
+++ b/modules/bim/app/views/bim/bcf/issues/configure_unknown_mails.html.erb
@@ -20,6 +20,7 @@
title: t(:label_role_search),
tabIndex: 0,
container_class: '-slim',
+ class: 'option-label--select',
id: 'unknown_mails_invite_role_ids' %>
(<%= t('bcf.recommended') %>)
@@ -59,4 +60,3 @@
defaults_bcf_project_ifc_models_path(@project),
class: 'button' %>
<% end %>
-
diff --git a/modules/bim/config/locales/crowdin/cs.seeders.yml b/modules/bim/config/locales/crowdin/cs.seeders.yml
index cbb0a97d082..8824b96c2e6 100644
--- a/modules/bim/config/locales/crowdin/cs.seeders.yml
+++ b/modules/bim/config/locales/crowdin/cs.seeders.yml
@@ -215,7 +215,7 @@ cs:
description: This type is hierarchically a parent of the types "Clash" and "Request", thus represents a general note.
children:
item_0:
- subject: Gathering first project information
+ subject: Shromažďování informací o prvním projektu
description: |-
## Goal
@@ -228,7 +228,7 @@ cs:
* Each need shall represent a task with its corresponding work packages
* Derive the cost estimation and time frame
item_1:
- subject: Summarize the results
+ subject: Shrňte výsledky
description: |-
## Goal
@@ -243,7 +243,7 @@ cs:
* This overview informs all participants about the decisions made
* ...
item_2:
- subject: End of basic evaluation
+ subject: Konec základního hodnocení
description: This type is hierarchically a parent of the types "Clash" and "Request", thus represents a general note.
item_2:
subject: Preliminary planning
@@ -504,7 +504,7 @@ cs:
subject: Completion of the BIM execution plan
description: This type is hierarchically a parent of the types "Clash" and "Request", thus represents a general note.
item_2:
- subject: End of preparation phase
+ subject: Konec přípravné fáze
description: This type is hierarchically a parent of the types "Clash" and "Request", thus represents a general note.
item_3:
subject: Creating initial BIM model
diff --git a/modules/bim/config/locales/crowdin/ne.seeders.yml b/modules/bim/config/locales/crowdin/ne.seeders.yml
index b05682d1a0b..b6ee74299dd 100644
--- a/modules/bim/config/locales/crowdin/ne.seeders.yml
+++ b/modules/bim/config/locales/crowdin/ne.seeders.yml
@@ -603,13 +603,13 @@ ne:
description: This type is hierarchically a parent of the types "Clash" and "Request", thus represents a general note.
item_6:
subject: Modelling & coordinating, second cycle
- description: "## Goal\\r\n\\r\n* ...\\r\n\\r\n## Description\\r\n\\r\n* \\ ..."
+ description: "## Goal\r\n\r\n* ...\r\n\r\n## Description\r\n\r\n* \\ ..."
item_7:
subject: Modelling & coordinating, ... cycle
description: This type is hierarchically a parent of the types "Clash" and "Request", thus represents a general note.
item_8:
subject: Modelling & coordinating, (n-th minus 1) cycle
- description: "## Goal\\r\n\\r\n* ...\\r\n\\r\n## Description\\r\n\\r\n* \\ ..."
+ description: "## Goal\r\n\r\n* ...\r\n\r\n## Description\r\n\r\n* \\ ..."
item_9:
subject: Modelling & coordinating n-th cycle
description: This type is hierarchically a parent of the types "Clash" and "Request", thus represents a general note.
diff --git a/modules/bim/config/locales/crowdin/nl.seeders.yml b/modules/bim/config/locales/crowdin/nl.seeders.yml
index 2e69d3d1ff8..95ad8d5e521 100644
--- a/modules/bim/config/locales/crowdin/nl.seeders.yml
+++ b/modules/bim/config/locales/crowdin/nl.seeders.yml
@@ -13,7 +13,7 @@ nl:
item_2:
name: Hoog
item_3:
- name: Critical
+ name: Kritiek
statuses:
item_0:
name: Nieuw
@@ -42,7 +42,7 @@ nl:
item_4:
name: Remark
item_5:
- name: Request
+ name: Verzoeken
item_6:
name: Clash
global_queries:
@@ -55,7 +55,7 @@ nl:
group_name: Subdocumenten
groups:
item_0:
- name: Architects
+ name: Architecten
item_1:
name: BIM Coordinators
item_2:
@@ -228,7 +228,7 @@ nl:
* Each need shall represent a task with its corresponding work packages
* Derive the cost estimation and time frame
item_1:
- subject: Summarize the results
+ subject: Vat de resultaten samen
description: |-
## Goal
@@ -265,7 +265,7 @@ nl:
* This overview informs all participants about the decisions made
* ...
item_1:
- subject: Summarize results
+ subject: Resultaten samenvatten
description: |-
## Goal
@@ -376,7 +376,7 @@ nl:
* Electrical installation
* ...
item_5:
- subject: Final touches
+ subject: Laatste details
description: |-
## Goal
@@ -417,7 +417,7 @@ nl:
item_0: Category 1 (to be changed in Project settings)
queries:
item_0:
- name: Project plan
+ name: Project abonnement
item_1:
name: Mijlpalen
item_2:
@@ -468,7 +468,7 @@ nl:
The next step could be to check out the timetable and adjusting the appointments, by looking at the [Gantt chart]({{opSetting:base_url}}/projects/demo-bim-project/work_packages?query_props=%7B%22c%22%3A%5B%22id%22%2C%22subject%22%2C%22startDate%22%2C%22dueDate%22%5D%2C%22tv%22%3Atrue%2C%22tzl%22%3A%22weeks%22%2C%22hi%22%3Atrue%2C%22g%22%3A%22%22%2C%22t%22%3A%22startDate%3Aasc%22%2C%22f%22%3A%5B%7B%22n%22%3A%22status%22%2C%22o%22%3A%22o%22%2C%22v%22%3A%5B%5D%7D%5D%2C%22pa%22%3A1%2C%22pp%22%3A100%7D).
item_1:
- subject: Project preparation
+ subject: Voorbereiding project
description: This type is hierarchically a parent of the types "Clash" and "Request", thus represents a general note.
children:
item_0:
@@ -686,11 +686,11 @@ nl:
item_1:
name: Clashes
item_2:
- name: Requests
+ name: Verzoeken
item_3:
name: Remarks
item_4:
- name: Project plan
+ name: Project abonnement
item_5:
name: Mijlpalen
item_6:
diff --git a/modules/bim/config/locales/crowdin/ru.seeders.yml b/modules/bim/config/locales/crowdin/ru.seeders.yml
index 5243ae25bb4..902ce0d24f5 100644
--- a/modules/bim/config/locales/crowdin/ru.seeders.yml
+++ b/modules/bim/config/locales/crowdin/ru.seeders.yml
@@ -89,7 +89,7 @@ ru:
demo-construction-project:
name: "(Демо) Конструкторский проект"
status_explanation: Все задачи и подпроекты выполняются по графику. Вовлеченные люди знают свои задачи. Система полностью настроена.
- description: This is a short summary of the goals of this demo construction project.
+ description: Это краткое изложение целей этого демонстрационного строительного проекта.
news:
item_0:
title: Добро пожаловать в демо-проект
@@ -217,31 +217,31 @@ ru:
item_0:
subject: Сбор первичной информации о проекте
description: |-
- ## Goal
+ ## Цель
- * Define tasks based on the customer needs
- * Time frame and cost estimation shall be defined
+ * Определите задачи на основе потребностей клиента
+ * Должны быть определены временные рамки и оценка затрат
- ## Description
+ ## Описание
- * Identify the customer needs by having a workshop with him/ her
- * Each need shall represent a task with its corresponding work packages
- * Derive the cost estimation and time frame
+ * Определите потребности клиента, проведя с ним семинар
+ * Каждая потребность должна представлять собой задачу с соответствующими пакетами работ
+ * Определите оценку затрат и временные рамки
item_1:
subject: Подвести итоги
description: |-
- ## Goal
+ ## Цель
- * Create a useful overview of the results
- * Check what has been done and summarize the results
- * Communicate all the relevant results with the customer
- * Identify the fundamental boundary conditions of the project
+ * Создать полезный обзор результатов
+ * Проверить, что было сделано, и обобщить результаты
+ * Сообщить все соответствующие результаты заказчику
+ * Определить основные граничные условия проекта
- ## Description
+ ## Описание
- * Each topic gets its own overview which will be used as a catalogue of results
- * This overview informs all participants about the decisions made
- * ...
+ * Каждая тема получает свой собственный обзор, который будет использоваться в качестве каталога результатов
+ * Этот обзор информирует всех участников о принятых решениях
+ * ...
item_2:
subject: Окончание базовой оценки
description: Этот тип является иерархически родителем типов "Clash" и "Request", таким образом представляет общую заметку.
@@ -267,18 +267,18 @@ ru:
item_1:
subject: Суммировать результаты
description: |-
- ## Goal
+ ## Цель
- * Create a useful overview of the results
- * Check what has been done and summarize the results
- * Communicate all the relevant results with the customer
- * Identify the fundamental boundary conditions of the project
+ * Создать полезный обзор результатов
+ * Проверить, что было сделано, и обобщить результаты
+ * Сообщить все соответствующие результаты заказчику
+ * Определить основные граничные условия проекта
- ## Description
+ ## Описание
- * Each topic gets its own overview which will be used as a catalogue of results
- * This overview informs all participants about the decisions made
- * ...
+ * Каждая тема получает свой собственный обзор, который будет использоваться в качестве каталога результатов
+ * Этот обзор информирует всех участников о принятых решениях
+ * ...
item_3:
subject: Прохождение предварительного планирования
description: Этот тип иерархически является родителем типов "Clash" и "Request", таким образом представляет общую заметку.
@@ -289,17 +289,17 @@ ru:
item_0:
subject: Завершение дизайна
description: |-
- ## Goal
+ ## Цель
- * Design is done
- * All parties are happy with the results of the design planning phase
+ * Проектирование завершено
+ * Все стороны довольны результатами этапа планирования проектирования
- ## Description
+ ## Описание
- * The design of the project will be finished
- * All parties agree on the design
- * The owner is happy with the results
- * ...
+ * Разработка проекта будет завершена
+ * Все стороны согласны с дизайном
+ * Владелец доволен результатами
+ * ...
item_1:
subject: Заморозка дизайна
description: Этот тип является иерархически родителем типов "Clash" и "Request", таким образом представляет общую заметку.
@@ -393,15 +393,15 @@ ru:
item_6:
subject: Вечеринка по случаю новоселья
description: |-
- ## Goal
+ ## Цель
- * Have a blast!
+ * Повеселись!
- ## Description
+ ## Описание
- * Invite the construction team
- * Invite your friends
- * Bring some drinks, snacks and your smile
+ * Пригласите строительную команду
+ * Пригласите своих друзей
+ * Принесите напитки, закуски и свою улыбку
demo-bim-project:
name: "(Demo) Проект BIM"
status_explanation: Все задачи и подпроекты выполняются по графику. Вовлеченные люди знают свои задачи. Система полностью настроена.
@@ -474,32 +474,32 @@ ru:
item_0:
subject: Сбор специфичных для проекта данных и информации для BIM-модели
description: |-
- ## Goal
+ ## Цель
- * Identify the information strategy for the customer (e.g. by using plain language questions)
- * If provided, analyze the customer information requirements for the BIM model
- * Define an information delivery strategy according to the customers needs
+ * Определите информационную стратегию для клиента (например, с помощью вопросов на простом языке)
+ * Если предусмотрено, проанализируйте требования к информации клиента для BIM-модели
+ * Определите стратегию предоставления информации в соответствии с потребностями клиентов
- ## Description
+ ## Описание
- * Analyzing the customers needs and goals for using the BIM methodology
- * Results of this tasks should be:
- * The requirements for the project
- * A strategy for the delivery phase
- * ...
+ * Анализ потребностей и целей клиентов для использования методологии BIM
+ * Результатами этих задач должны быть:
+ * Требования к проекту
+ * Стратегия для этапа доставки
+ * ...
item_1:
subject: Создание плана выполнения BIM
description: |-
- # Goal
+ # Цель
- * A BIM execution plan will be defined according to the exchange requirements specifications (ERS)
- * All team members and partners have a plan on how to reach each of the project goals
+ * План выполнения BIM будет определен согласно спецификациям требований (ERS)
+ * Все члены команды и партнеры имеют план достижения каждой из целей проекта
- # Description
+ # Описание
- * Depending on the identifies use cases, the individual Information Delivery Manuals will be defined
- * To handle the technological interfaces, a software topology will be defined and analyzed and verified
- * ...
+ * В зависимости от вариантов использования, индивидуальные Руководства по доставке информации будут определены
+ * Для обработки технологических интерфейсов, Программная топология будет определена и проанализирована и проверена
+ * ...
item_2:
subject: Завершение выполнения плана выполнения БИМ
description: Этот тип иерархически является родителем типов "Clash" и "Request", таким образом представляет общую заметку.
@@ -513,16 +513,16 @@ ru:
item_0:
subject: Моделирование исходной BIM-модели
description: |-
- # Goal
+ # Цель
- * Modelling the initial BIM model
- * Creating a BIM model for the whole project team
+ * Моделирование начальной модели BIM
+ * Создание модели BIM для всей команды проекта
- # Description
+ # Описание
- * According to the gathered data from the customer, the initial model will be modelled
- * The model shall be modelled according to the LOD Matrices and contain the information needed
- * ...
+ * По собранным данным от клиента, начальная модель будет смоделирована
+ * Модель должна быть смоделирована в соответствии с матрицами LOD и содержать необходимую информацию
+ * ...
item_1:
subject: Первоначальная внутренняя проверка и пересмотр модели
description: |-
diff --git a/modules/boards/app/components/boards/add_button_component.rb b/modules/boards/app/components/boards/add_button_component.rb
new file mode 100644
index 00000000000..b07bfdd11bc
--- /dev/null
+++ b/modules/boards/app/components/boards/add_button_component.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+# -- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2023 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write 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 Boards
+ class AddButtonComponent < ::AddButtonComponent
+ def render?
+ if current_project
+ User.current.allowed_to?(:manage_board_views, current_project)
+ else
+ User.current.allowed_to_globally?(:manage_board_views)
+ end
+ end
+
+ def dynamic_path
+ polymorphic_path([:new, current_project, :work_package_board])
+ end
+
+ def id
+ 'add-board-button'
+ end
+
+ def accessibility_label_text
+ I18n.t('boards.label_create_new_board')
+ end
+
+ def label_text
+ I18n.t('boards.label_board')
+ end
+ end
+end
diff --git a/modules/boards/app/components/boards/row_component.rb b/modules/boards/app/components/boards/row_component.rb
index 5256834460c..92e8d3bd05a 100644
--- a/modules/boards/app/components/boards/row_component.rb
+++ b/modules/boards/app/components/boards/row_component.rb
@@ -35,7 +35,7 @@ module Boards
end
def name
- link_to model.name, project_work_package_boards_path(model.project, model)
+ link_to model.name, project_work_package_board_path(model.project, model)
end
def created_at
@@ -50,5 +50,31 @@ module Boards
t('boards.board_types.free')
end
end
+
+ def button_links
+ [delete_link].compact
+ end
+
+ def delete_link
+ if render_delete_link?
+ link_to(
+ '',
+ work_package_board_path(model),
+ method: :delete,
+ class: 'icon icon-delete',
+ data: {
+ confirm: I18n.t(:text_are_you_sure),
+ 'qa-selector': "board-remove-#{model.id}"
+ },
+ title: t(:button_delete)
+ )
+ end
+ end
+
+ private
+
+ def render_delete_link?
+ table.current_project && table.current_user.allowed_to?(:manage_board_views, table.current_project)
+ end
end
end
diff --git a/modules/boards/app/components/boards/table_component.rb b/modules/boards/app/components/boards/table_component.rb
index 90f202e0893..a73d9a7d596 100644
--- a/modules/boards/app/components/boards/table_component.rb
+++ b/modules/boards/app/components/boards/table_component.rb
@@ -30,7 +30,7 @@
module Boards
class TableComponent < ::TableComponent
- columns :name, :project_id, :type, :created_at
+ options :current_project, :current_user
sortable_columns :name, :project_id, :created_at
def initial_sort
@@ -46,5 +46,14 @@ module Boards
[attr, { caption: Boards::Grid.human_attribute_name(attr) }]
end
end
+
+ def columns
+ @columns ||= [
+ :name,
+ (:project_id if current_project.blank?),
+ :type,
+ :created_at
+ ].compact
+ end
end
end
diff --git a/modules/boards/app/contracts/boards/create_contract.rb b/modules/boards/app/contracts/boards/create_contract.rb
new file mode 100644
index 00000000000..9e5f1b7d95a
--- /dev/null
+++ b/modules/boards/app/contracts/boards/create_contract.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Boards
+ class CreateContract < ::Grids::CreateContract
+ private
+
+ def validate_allowed
+ unless edit_allowed?
+ # "project" is what is exposed to the user in the global form, not "scope"
+ errors.add(:project, :blank)
+ end
+ end
+ end
+end
diff --git a/modules/boards/app/controllers/boards/boards_controller.rb b/modules/boards/app/controllers/boards/boards_controller.rb
index d2f819043d4..976de7feaef 100644
--- a/modules/boards/app/controllers/boards/boards_controller.rb
+++ b/modules/boards/app/controllers/boards/boards_controller.rb
@@ -1,20 +1,23 @@
module ::Boards
class BoardsController < BaseController
before_action :find_optional_project
+ before_action :build_board_grid, only: %i[new]
- with_options only: [:index] do
+ with_options only: %i[index show] do
# The boards permission alone does not suffice
# to view work packages
before_action :authorize
before_action :authorize_work_package_permission
end
- before_action :authorize_global, only: :overview
+ before_action :authorize_global, only: %i[overview new create destroy]
+ before_action :find_board_grid, only: %i[destroy]
+ before_action :ensure_board_type_not_restricted, only: %i[create]
menu_item :board_view
def index
- render layout: 'angular/angular'
+ @board_grids = Boards::Grid.includes(:project).where(project: @project)
end
def overview
@@ -31,12 +34,88 @@ module ::Boards
:boards
end
+ def show
+ render layout: 'angular/angular'
+ end
+
+ def new; end
+
+ def create
+ service_result = service_call
+
+ @board_grid = service_result.result
+
+ if service_result.success?
+ flash[:notice] = I18n.t(:notice_successful_create)
+ redirect_to project_work_package_board_path(@project, @board_grid)
+ else
+ @errors = service_result.errors
+ render action: :new
+ end
+ end
+
+ def destroy
+ @board_grid.destroy
+
+ flash[:notice] = I18n.t(:notice_successful_delete)
+
+ respond_to do |format|
+ format.json do
+ render json: { redirect_url: project_work_package_boards_path(@project) }
+ end
+ format.html do
+ redirect_to action: 'index', project_id: @project
+ end
+ end
+ end
+
private
+ def find_board_grid
+ @board_grid = Boards::Grid.find(params[:id])
+ @project = @board_grid.project
+ end
+
def authorize_work_package_permission
unless current_user.allowed_to?(:view_work_packages, @project, global: @project.nil?)
deny_access
end
end
+
+ def build_board_grid
+ @board_grid = Boards::Grid.new
+ end
+
+ def ensure_board_type_not_restricted
+ render_403 if restricted_board_type?
+ end
+
+ def restricted_board_type?
+ !EnterpriseToken.allows_to?(:board_view) && board_grid_params[:attribute] != 'basic'
+ end
+
+ def service_call
+ service_class.new(user: User.current)
+ .call(
+ project: @project,
+ name: board_grid_params[:name],
+ attribute: board_grid_params[:attribute]
+ )
+ end
+
+ def service_class
+ {
+ 'basic' => Boards::BasicBoardCreateService,
+ 'status' => Boards::StatusBoardCreateService,
+ 'assignee' => Boards::AssigneeBoardCreateService,
+ 'version' => Boards::VersionBoardCreateService,
+ 'subproject' => Boards::SubprojectBoardCreateService,
+ 'subtasks' => Boards::SubtasksBoardCreateService
+ }.fetch(board_grid_params[:attribute])
+ end
+
+ def board_grid_params
+ params.require(:boards_grid).permit(%i[name attribute])
+ end
end
end
diff --git a/modules/boards/app/helpers/boards_helper.rb b/modules/boards/app/helpers/boards_helper.rb
new file mode 100644
index 00000000000..1e6d3669038
--- /dev/null
+++ b/modules/boards/app/helpers/boards_helper.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module BoardsHelper
+ BoardTypeAttributes = Struct.new(:radio_button_value,
+ :title,
+ :description,
+ :image_path,
+ :disabled?)
+
+ def board_types
+ [
+ build_board_type_attributes('basic', 'lists', false),
+ build_board_type_attributes('status', 'status'),
+ build_board_type_attributes('assignee', 'assignees'),
+ build_board_type_attributes('version', 'version'),
+ build_board_type_attributes('subproject', 'subproject'),
+ build_board_type_attributes('subtasks', 'parent-child')
+ ]
+ end
+
+ def build_board_type_attributes(type_name, image_name, disabled = !EnterpriseToken.allows_to?(:board_view))
+ BoardTypeAttributes.new(type_name,
+ I18n.t("boards.board_type_attributes.#{type_name}"),
+ I18n.t("boards.board_type_descriptions.#{type_name}"),
+ "assets/images/board_creation_modal/#{image_name}.svg",
+ disabled)
+ end
+
+ def global_board_create_context?
+ global_board_new_action? || global_board_create_action?
+ end
+
+ def global_board_new_action?
+ request.path == new_work_package_board_path
+ end
+
+ def global_board_create_action?
+ request.path == work_package_boards_path && @project.nil?
+ end
+end
diff --git a/modules/boards/app/services/boards/assignee_board_create_service.rb b/modules/boards/app/services/boards/assignee_board_create_service.rb
new file mode 100644
index 00000000000..3083d6b86d9
--- /dev/null
+++ b/modules/boards/app/services/boards/assignee_board_create_service.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Boards
+ class AssigneeBoardCreateService < BaseCreateService
+ private
+
+ def no_widgets_initially?
+ true
+ end
+ end
+end
diff --git a/modules/boards/app/services/boards/base_create_service.rb b/modules/boards/app/services/boards/base_create_service.rb
new file mode 100644
index 00000000000..d3538f94906
--- /dev/null
+++ b/modules/boards/app/services/boards/base_create_service.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+module Boards
+ class BaseCreateService < ::Grids::CreateService
+ protected
+
+ def instance(attributes)
+ Boards::Grid.new(
+ name: attributes[:name],
+ project: attributes[:project],
+ row_count: row_count_for_board,
+ column_count: column_count_for_board
+ )
+ end
+
+ def before_perform(params, _service_result)
+ return super(params, _service_result) if no_widgets_initially?
+
+ create_query_result = create_query(params)
+
+ return create_query_result if create_query_result.failure?
+
+ super(params.merge(query_id: create_query_result.result.id), create_query_result)
+ end
+
+ def set_attributes_params(params)
+ {}.tap do |grid_params|
+ grid_params[:options] = options_for_grid(params)
+ grid_params[:widgets] = options_for_widgets(params)
+ end
+ end
+
+ def attributes_service_class
+ BaseSetAttributesService
+ end
+
+ private
+
+ def no_widgets_initially?
+ false
+ end
+
+ def create_query(params)
+ Queries::CreateService.new(user: User.current)
+ .call(create_query_params(params))
+ end
+
+ def create_query_params(params)
+ default_create_query_params(params).merge(
+ name: query_name,
+ filters: query_filters
+ )
+ end
+
+ def default_create_query_params(params)
+ {
+ project: params[:project],
+ public: true,
+ sort_criteria: query_sort_criteria
+ }
+ end
+
+ def query_name
+ raise 'Define the query name'
+ end
+
+ def query_filters
+ raise 'Define the query filters'
+ end
+
+ def query_sort_criteria
+ [[:manual_sorting, 'asc'], [:id, 'asc']]
+ end
+
+ def options_for_grid(params)
+ {}.tap do |options|
+ if params[:attribute] == 'basic'
+ options[:type] = 'free'
+ else
+ options[:type] = 'action'
+ options[:attribute] = params[:attribute]
+ end
+ end
+ end
+
+ def options_for_widgets(_params)
+ return [] if no_widgets_initially?
+
+ raise 'Define the options for the grid widgets'
+ end
+
+ def row_count_for_board
+ 1
+ end
+
+ def column_count_for_board
+ 4
+ end
+ end
+end
diff --git a/modules/boards/app/services/boards/base_set_attributes_service.rb b/modules/boards/app/services/boards/base_set_attributes_service.rb
new file mode 100644
index 00000000000..b625d791fa0
--- /dev/null
+++ b/modules/boards/app/services/boards/base_set_attributes_service.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Boards
+ class BaseSetAttributesService < Grids::SetAttributesService
+ end
+end
diff --git a/modules/boards/app/services/boards/basic_board_create_service.rb b/modules/boards/app/services/boards/basic_board_create_service.rb
new file mode 100644
index 00000000000..65705f1576e
--- /dev/null
+++ b/modules/boards/app/services/boards/basic_board_create_service.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Boards
+ class BasicBoardCreateService < BaseCreateService
+ private
+
+ def query_name
+ 'Unnamed list'
+ end
+
+ def query_filters
+ [{ manual_sort: { operator: 'ow', values: [] } }]
+ end
+
+ def options_for_widgets(params)
+ [
+ Grids::Widget.new(
+ start_row: 1,
+ start_column: 1,
+ end_row: 2,
+ end_column: 2,
+ identifier: "work_package_query",
+ options: {
+ "queryId" => params[:query_id],
+ "filters" => query_filters
+ }
+ )
+ ]
+ end
+ end
+end
diff --git a/modules/boards/app/services/boards/status_board_create_service.rb b/modules/boards/app/services/boards/status_board_create_service.rb
new file mode 100644
index 00000000000..500dfdd170c
--- /dev/null
+++ b/modules/boards/app/services/boards/status_board_create_service.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Boards
+ class StatusBoardCreateService < BaseCreateService
+ private
+
+ def query_name
+ default_status.name
+ end
+
+ def query_filters
+ [{ status_id: { operator: '=', values: [default_status.id.to_s] } }]
+ end
+
+ def default_status
+ @default_status ||= ::Status.default
+ end
+
+ def options_for_widgets(params)
+ [
+ Grids::Widget.new(
+ start_row: 1,
+ start_column: 1,
+ end_row: 2,
+ end_column: 2,
+ identifier: "work_package_query",
+ options: {
+ "queryId" => params[:query_id],
+ "filters" => query_filters
+ }
+ )
+ ]
+ end
+ end
+end
diff --git a/modules/boards/app/services/boards/subproject_board_create_service.rb b/modules/boards/app/services/boards/subproject_board_create_service.rb
new file mode 100644
index 00000000000..e3bb27489c5
--- /dev/null
+++ b/modules/boards/app/services/boards/subproject_board_create_service.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Boards
+ class SubprojectBoardCreateService < BaseCreateService
+ private
+
+ def no_widgets_initially?
+ true
+ end
+ end
+end
diff --git a/modules/boards/app/services/boards/subtasks_board_create_service.rb b/modules/boards/app/services/boards/subtasks_board_create_service.rb
new file mode 100644
index 00000000000..a3e00f67b78
--- /dev/null
+++ b/modules/boards/app/services/boards/subtasks_board_create_service.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Boards
+ class SubtasksBoardCreateService < BaseCreateService
+ private
+
+ def no_widgets_initially?
+ true
+ end
+ end
+end
diff --git a/modules/boards/app/services/boards/version_board_create_service.rb b/modules/boards/app/services/boards/version_board_create_service.rb
new file mode 100644
index 00000000000..e689fdbfe72
--- /dev/null
+++ b/modules/boards/app/services/boards/version_board_create_service.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+module Boards
+ class VersionBoardCreateService < BaseCreateService
+ protected
+
+ def before_perform(params, _service_result)
+ create_queries_results = create_queries(params)
+
+ return create_queries_results.find(&:failure?) if create_queries_results.any?(&:failure?)
+
+ set_attributes(params.merge(query_ids: create_queries_results.map(&:result).map(&:id)))
+ end
+
+ private
+
+ def create_queries(params)
+ versions(params).map do |version|
+ Queries::CreateService.new(user: User.current)
+ .call(create_query_params(params, version))
+ end
+ end
+
+ def versions(params)
+ @versions ||= Version.includes(:project)
+ .where(projects: { id: params[:project].id })
+ .with_status_open
+ end
+
+ def create_query_params(params, version)
+ default_create_query_params(params).merge(
+ name: query_name(version),
+ filters: query_filters(version)
+ )
+ end
+
+ def query_name(version)
+ version.name
+ end
+
+ def query_filters(version)
+ [{ version_id: { operator: '=', values: [version.id.to_s] } }]
+ end
+
+ def options_for_widgets(params)
+ query_ids_with_versions = params[:query_ids].zip(versions(params))
+ query_ids_with_versions.map.with_index do |(query_id, version), index|
+ Grids::Widget.new(
+ start_row: 1,
+ start_column: 1 + index,
+ end_row: 2,
+ end_column: 2 + index,
+ identifier: "work_package_query",
+ options: {
+ "queryId" => query_id,
+ "filters" => query_filters(version)
+ }
+ )
+ end
+ end
+ end
+end
diff --git a/modules/boards/app/views/boards/boards/_form.html.erb b/modules/boards/app/views/boards/boards/_form.html.erb
new file mode 100644
index 00000000000..dd3de65dde6
--- /dev/null
+++ b/modules/boards/app/views/boards/boards/_form.html.erb
@@ -0,0 +1,106 @@
+<%#-- copyright
+OpenProject is an open source project management software.
+Copyright (C) 2012-2023 the OpenProject GmbH
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License version 3.
+
+OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+Copyright (C) 2006-2013 Jean-Philippe Lang
+Copyright (C) 2010-2013 the ChiliProject Team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+See COPYRIGHT and LICENSE files for more details.
+
+++#%>
+
+<%= error_messages_for_contract @board_grid, @errors %>
+
+
diff --git a/modules/boards/app/views/boards/boards/index.html.erb b/modules/boards/app/views/boards/boards/index.html.erb
index d314de3539e..56b59b37eaa 100644
--- a/modules/boards/app/views/boards/boards/index.html.erb
+++ b/modules/boards/app/views/boards/boards/index.html.erb
@@ -1 +1,40 @@
+<%#-- copyright
+OpenProject is an open source project management software.
+Copyright (C) 2012-2023 the OpenProject GmbH
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License version 3.
+
+OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+Copyright (C) 2006-2013 Jean-Philippe Lang
+Copyright (C) 2010-2013 the ChiliProject Team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write 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('boards.label_boards')) -%>
+
+<%= toolbar title: t('boards.label_boards') do %>
+ <%= render Boards::AddButtonComponent.new(current_project: @project) %>
+<% end %>
+
+<% if @board_grids.empty? -%>
+ <%= no_results_box %>
+<% else -%>
+ <%= render Boards::TableComponent.new(rows: @board_grids, current_user: User.current, current_project: @project) %>
+<% end -%>
diff --git a/modules/boards/app/views/boards/boards/new.html.erb b/modules/boards/app/views/boards/boards/new.html.erb
new file mode 100644
index 00000000000..921875a654a
--- /dev/null
+++ b/modules/boards/app/views/boards/boards/new.html.erb
@@ -0,0 +1,37 @@
+<%#-- copyright
+OpenProject is an open source project management software.
+Copyright (C) 2012-2023 the OpenProject GmbH
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License version 3.
+
+OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+Copyright (C) 2006-2013 Jean-Philippe Lang
+Copyright (C) 2010-2013 the ChiliProject Team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write 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('boards.label_create_new_board') %>
+<%= toolbar title: t('boards.label_create_new_board') %>
+<%= labelled_tabular_form_for @board_grid, url: { controller: '/boards/boards', action: 'create', project_id: @project }, :html => {:id => 'board-form'} do |f| -%>
+ <%= render :partial => 'form', :locals => {:f => f} %>
+ <%= styled_button_tag t(:button_create), class: '-highlight' %>
+ <%= link_to t(:button_cancel), { controller: 'boards/boards', action: (@project ? 'index' : 'overview') },
+ class: 'button' %>
+<% end %>
diff --git a/modules/boards/app/views/boards/boards/overview.html.erb b/modules/boards/app/views/boards/boards/overview.html.erb
index ae876b38d89..9d9a1934869 100644
--- a/modules/boards/app/views/boards/boards/overview.html.erb
+++ b/modules/boards/app/views/boards/boards/overview.html.erb
@@ -29,10 +29,12 @@ See COPYRIGHT and LICENSE files for more details.
<% html_title(t('boards.label_boards')) -%>
-<%= toolbar title: t('boards.label_boards') -%>
+<%= toolbar title: t('boards.label_boards') do %>
+ <%= render Boards::AddButtonComponent.new %>
+<% end %>
<% if @board_grids.empty? -%>
<%= no_results_box %>
<% else -%>
-<%= render Boards::TableComponent.new(rows: @board_grids) %>
+<%= render Boards::TableComponent.new(rows: @board_grids, current_user: User.current) %>
<% end -%>
diff --git a/modules/boards/app/views/boards/boards/show.html.erb b/modules/boards/app/views/boards/boards/show.html.erb
new file mode 100644
index 00000000000..3655423d098
--- /dev/null
+++ b/modules/boards/app/views/boards/boards/show.html.erb
@@ -0,0 +1,30 @@
+<%#-- copyright
+OpenProject is an open source project management software.
+Copyright (C) 2012-2023 the OpenProject GmbH
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License version 3.
+
+OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+Copyright (C) 2006-2013 Jean-Philippe Lang
+Copyright (C) 2010-2013 the ChiliProject Team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write 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('boards.label_boards')) -%>
diff --git a/modules/boards/config/locales/crowdin/af.yml b/modules/boards/config/locales/crowdin/af.yml
index 876a434d21f..d5c7a472659 100644
--- a/modules/boards/config/locales/crowdin/af.yml
+++ b/modules/boards/config/locales/crowdin/af.yml
@@ -6,6 +6,8 @@ af:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ af:
subproject: Sub-projek
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/ar.yml b/modules/boards/config/locales/crowdin/ar.yml
index f451a91c40e..2669b9a075b 100644
--- a/modules/boards/config/locales/crowdin/ar.yml
+++ b/modules/boards/config/locales/crowdin/ar.yml
@@ -6,6 +6,8 @@ ar:
boards:
label_board: "لوحة"
label_boards: "لوحات"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ ar:
subproject: مشروع فرعي
subtasks: الاصل والفرع
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/az.yml b/modules/boards/config/locales/crowdin/az.yml
index 342462eba31..db9f9bcbcff 100644
--- a/modules/boards/config/locales/crowdin/az.yml
+++ b/modules/boards/config/locales/crowdin/az.yml
@@ -6,6 +6,8 @@ az:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ az:
subproject: Subproject
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/be.yml b/modules/boards/config/locales/crowdin/be.yml
index e8b96709fa8..56f30460882 100644
--- a/modules/boards/config/locales/crowdin/be.yml
+++ b/modules/boards/config/locales/crowdin/be.yml
@@ -6,6 +6,8 @@ be:
boards:
label_board: "Дошка"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ be:
subproject: Subproject
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/bg.yml b/modules/boards/config/locales/crowdin/bg.yml
index 9847982538c..fc83342e608 100644
--- a/modules/boards/config/locales/crowdin/bg.yml
+++ b/modules/boards/config/locales/crowdin/bg.yml
@@ -6,6 +6,8 @@ bg:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Основен
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ bg:
subproject: Подпроект
subtasks: Parent-child
basic: Основен
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/ca.yml b/modules/boards/config/locales/crowdin/ca.yml
index 4a5c49ac7b3..ec3d1330096 100644
--- a/modules/boards/config/locales/crowdin/ca.yml
+++ b/modules/boards/config/locales/crowdin/ca.yml
@@ -6,6 +6,8 @@ ca:
boards:
label_board: "Taulell"
label_boards: "Taulells"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Bàsic
action: "Panell d'acció (%{attribute})"
@@ -16,3 +18,19 @@ ca:
subproject: Subprojecte
subtasks: Pare-fill
basic: Bàsic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/ckb-IR.yml b/modules/boards/config/locales/crowdin/ckb-IR.yml
index d50b3f4acbb..ed5676d29b0 100644
--- a/modules/boards/config/locales/crowdin/ckb-IR.yml
+++ b/modules/boards/config/locales/crowdin/ckb-IR.yml
@@ -6,6 +6,8 @@ ckb-IR:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ ckb-IR:
subproject: Subproject
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/cs.yml b/modules/boards/config/locales/crowdin/cs.yml
index 394558773c9..b367eb2a558 100644
--- a/modules/boards/config/locales/crowdin/cs.yml
+++ b/modules/boards/config/locales/crowdin/cs.yml
@@ -6,6 +6,8 @@ cs:
boards:
label_board: "Tabule"
label_boards: "Tabule"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Základní
action: "Akční tabule (%{attribute})"
@@ -16,3 +18,19 @@ cs:
subproject: Podprojekt
subtasks: rodič-potomek
basic: Základní
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgradovat nyní'
diff --git a/modules/boards/config/locales/crowdin/da.yml b/modules/boards/config/locales/crowdin/da.yml
index 1b61559eaa2..3e7c3b9223b 100644
--- a/modules/boards/config/locales/crowdin/da.yml
+++ b/modules/boards/config/locales/crowdin/da.yml
@@ -6,6 +6,8 @@ da:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Grundlæggende
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ da:
subproject: Underprojekt
subtasks: Parent-child
basic: Grundlæggende
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/de.yml b/modules/boards/config/locales/crowdin/de.yml
index 0b69e6b6f15..1d12c07c2ba 100644
--- a/modules/boards/config/locales/crowdin/de.yml
+++ b/modules/boards/config/locales/crowdin/de.yml
@@ -6,6 +6,8 @@ de:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Aktionsboard (%{attribute})"
@@ -16,3 +18,19 @@ de:
subproject: Unterprojekt
subtasks: Eltern-Kind
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/el.yml b/modules/boards/config/locales/crowdin/el.yml
index f718b4e58b9..40c4ce5e4b3 100644
--- a/modules/boards/config/locales/crowdin/el.yml
+++ b/modules/boards/config/locales/crowdin/el.yml
@@ -6,6 +6,8 @@ el:
boards:
label_board: "Πίνακας"
label_boards: "Πίνακες"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Βασικό
action: "Πίνακας ενεργειών (%{attribute})"
@@ -16,3 +18,19 @@ el:
subproject: Υποέργο
subtasks: Parent-child
basic: Βασικό
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/eo.yml b/modules/boards/config/locales/crowdin/eo.yml
index ee92f6bc663..c993332e53e 100644
--- a/modules/boards/config/locales/crowdin/eo.yml
+++ b/modules/boards/config/locales/crowdin/eo.yml
@@ -6,6 +6,8 @@ eo:
boards:
label_board: "Tabulo"
label_boards: "Tabuloj"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Agpanelo (%{attribute})"
@@ -16,3 +18,19 @@ eo:
subproject: Subprojekto
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/es.yml b/modules/boards/config/locales/crowdin/es.yml
index 61a24f0caac..ea3b6c82a1d 100644
--- a/modules/boards/config/locales/crowdin/es.yml
+++ b/modules/boards/config/locales/crowdin/es.yml
@@ -6,6 +6,8 @@ es:
boards:
label_board: "Tablero"
label_boards: "Tableros"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Básico
action: "Tablero de acciones (%{attribute})"
@@ -16,3 +18,19 @@ es:
subproject: Subproyecto
subtasks: Padre-hijo
basic: Básico
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/et.yml b/modules/boards/config/locales/crowdin/et.yml
index 486f5ff3334..b86657bc6a2 100644
--- a/modules/boards/config/locales/crowdin/et.yml
+++ b/modules/boards/config/locales/crowdin/et.yml
@@ -6,6 +6,8 @@ et:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ et:
subproject: Alamprojekt
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/eu.yml b/modules/boards/config/locales/crowdin/eu.yml
index 6a1c1836d20..a229be94ce7 100644
--- a/modules/boards/config/locales/crowdin/eu.yml
+++ b/modules/boards/config/locales/crowdin/eu.yml
@@ -6,6 +6,8 @@ eu:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ eu:
subproject: Subproject
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/fa.yml b/modules/boards/config/locales/crowdin/fa.yml
index 51952b1aedb..060acf71f09 100644
--- a/modules/boards/config/locales/crowdin/fa.yml
+++ b/modules/boards/config/locales/crowdin/fa.yml
@@ -6,6 +6,8 @@ fa:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ fa:
subproject: زیر پروژه
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/fi.yml b/modules/boards/config/locales/crowdin/fi.yml
index 3ff72f4cec9..c01c8f224d4 100644
--- a/modules/boards/config/locales/crowdin/fi.yml
+++ b/modules/boards/config/locales/crowdin/fi.yml
@@ -6,6 +6,8 @@ fi:
boards:
label_board: "Taulu"
label_boards: "Taulut"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ fi:
subproject: Aliprojekti
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/fil.yml b/modules/boards/config/locales/crowdin/fil.yml
index 3ef2c41d71d..d2ddc126ad6 100644
--- a/modules/boards/config/locales/crowdin/fil.yml
+++ b/modules/boards/config/locales/crowdin/fil.yml
@@ -6,6 +6,8 @@ fil:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ fil:
subproject: Kahaliling proyekto
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/fr.yml b/modules/boards/config/locales/crowdin/fr.yml
index 4f95abfcf0a..d39a4c6984b 100644
--- a/modules/boards/config/locales/crowdin/fr.yml
+++ b/modules/boards/config/locales/crowdin/fr.yml
@@ -6,6 +6,8 @@ fr:
boards:
label_board: "Tableau"
label_boards: "Tableaux"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Tableau d'action (%{attribute})"
@@ -16,3 +18,19 @@ fr:
subproject: Sous-projet
subtasks: Parent-enfant
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/he.yml b/modules/boards/config/locales/crowdin/he.yml
index c55d740468a..5124744f2d8 100644
--- a/modules/boards/config/locales/crowdin/he.yml
+++ b/modules/boards/config/locales/crowdin/he.yml
@@ -6,6 +6,8 @@ he:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ he:
subproject: תת פרוייקט
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/hi.yml b/modules/boards/config/locales/crowdin/hi.yml
index 75f16d43b51..93a36d48680 100644
--- a/modules/boards/config/locales/crowdin/hi.yml
+++ b/modules/boards/config/locales/crowdin/hi.yml
@@ -6,6 +6,8 @@ hi:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ hi:
subproject: परियोजना की उपपरियोजना
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/hr.yml b/modules/boards/config/locales/crowdin/hr.yml
index 7095698d41c..eec003136c4 100644
--- a/modules/boards/config/locales/crowdin/hr.yml
+++ b/modules/boards/config/locales/crowdin/hr.yml
@@ -6,6 +6,8 @@ hr:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ hr:
subproject: Potprojekt
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/hu.yml b/modules/boards/config/locales/crowdin/hu.yml
index d515385dde3..e037a53f036 100644
--- a/modules/boards/config/locales/crowdin/hu.yml
+++ b/modules/boards/config/locales/crowdin/hu.yml
@@ -6,6 +6,8 @@ hu:
boards:
label_board: "Tábla"
label_boards: "Táblák"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Alap
action: "Feladattábla"
@@ -16,3 +18,19 @@ hu:
subproject: Alprojekt
subtasks: Szülő-gyerek
basic: Alap
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/id.yml b/modules/boards/config/locales/crowdin/id.yml
index 2336437d704..41bf85573ed 100644
--- a/modules/boards/config/locales/crowdin/id.yml
+++ b/modules/boards/config/locales/crowdin/id.yml
@@ -6,6 +6,8 @@ id:
boards:
label_board: "Papan"
label_boards: "Papan"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Dasar
action: "Papan aksi (%{attribute}"
@@ -16,3 +18,19 @@ id:
subproject: Sub-Project
subtasks: Orangtua-anak
basic: Dasar
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/it.yml b/modules/boards/config/locales/crowdin/it.yml
index 9d9470e7e19..f872a64d8bf 100644
--- a/modules/boards/config/locales/crowdin/it.yml
+++ b/modules/boards/config/locales/crowdin/it.yml
@@ -6,6 +6,8 @@ it:
boards:
label_board: "Tabellone"
label_boards: "Tabelloni"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Base
action: "Scheda d'azione (%{attribute})"
@@ -16,3 +18,19 @@ it:
subproject: Sottoprogetto
subtasks: genitore-figlio
basic: Base
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/ja.yml b/modules/boards/config/locales/crowdin/ja.yml
index 2192a79ab7b..b3ee66e3922 100644
--- a/modules/boards/config/locales/crowdin/ja.yml
+++ b/modules/boards/config/locales/crowdin/ja.yml
@@ -6,6 +6,8 @@ ja:
boards:
label_board: "ボード"
label_boards: "ボード"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: 基本
action: "アクションボード (%{attribute})"
@@ -16,3 +18,19 @@ ja:
subproject: 子プロジェクト
subtasks: 親子
basic: 基本
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/ka.yml b/modules/boards/config/locales/crowdin/ka.yml
index 0bd4862da97..6d65d69fded 100644
--- a/modules/boards/config/locales/crowdin/ka.yml
+++ b/modules/boards/config/locales/crowdin/ka.yml
@@ -6,6 +6,8 @@ ka:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: ძირითადი
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ ka:
subproject: Subproject
subtasks: Parent-child
basic: ძირითადი
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/ko.yml b/modules/boards/config/locales/crowdin/ko.yml
index 98ab862c395..02af7bc514f 100644
--- a/modules/boards/config/locales/crowdin/ko.yml
+++ b/modules/boards/config/locales/crowdin/ko.yml
@@ -6,6 +6,8 @@ ko:
boards:
label_board: "게시판"
label_boards: "보드"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: 기본
action: "작업 보드(%{attribute})"
@@ -16,3 +18,19 @@ ko:
subproject: 하위 프로젝트
subtasks: 부모-자식
basic: 기본
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/lt.yml b/modules/boards/config/locales/crowdin/lt.yml
index faf04e61c61..d47aa81c030 100644
--- a/modules/boards/config/locales/crowdin/lt.yml
+++ b/modules/boards/config/locales/crowdin/lt.yml
@@ -6,6 +6,8 @@ lt:
boards:
label_board: "Lenta"
label_boards: "Lentos"
+ label_create_new_board: "Kurti naują lentą"
+ label_board_type: "Lentos tipas"
board_types:
free: Pagrindinis
action: "Veiksmų lenta (%{attribute})"
@@ -16,3 +18,19 @@ lt:
subproject: Sub-projektas
subtasks: Tėvo-vaiko
basic: Pagrindinis
+ board_type_descriptions:
+ basic: >
+ Pradėkite iš pradžių nuo tuščios lentos. Rankiniu būdu pridėkite korteles ir stulpelius į šią lentą.
+ status: >
+ Paprasta kanban stiliaus lenta su stulpeliais statusams, tokiems kaip To Do, Vykdoma, Įvykdyta.
+ assignee: >
+ Lenta su automatiniais stulpeliais pagrįstais priskirtu naudotoju. Ideali darbo paketų paskirstymui.
+ version: >
+ Lenta su automatiniais stulpeliais, pagrįstais versijos atributu. Ideali produkto vystymo planavimui.
+ subproject: >
+ Lenta su automatiniais stulpeliais sub-projektams. Darbų paketo perkėlimas į kitą sąrašą atitinkamai pakeičia (sub-)projektą.
+ subtasks: >
+ Lenta su automatiniais stulpeliais sub-elementams. Darbų paketo perkėlimas į kitą sąrašą atitinkamai atnaujina tėtį.
+ upsale:
+ teaser_text: 'Ar norite automatizuoti jūsų procesus su Lentomis? Išmaniosios lentos yra Enterprise priedas. Prašome pasikeisti į mokamą planą.'
+ upgrade: 'Užsisakykite dabar'
diff --git a/modules/boards/config/locales/crowdin/lv.yml b/modules/boards/config/locales/crowdin/lv.yml
index 51c54496f66..2a3eba54c6a 100644
--- a/modules/boards/config/locales/crowdin/lv.yml
+++ b/modules/boards/config/locales/crowdin/lv.yml
@@ -6,6 +6,8 @@ lv:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ lv:
subproject: Subproject
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/mn.yml b/modules/boards/config/locales/crowdin/mn.yml
index e167896e74c..7b331bde312 100644
--- a/modules/boards/config/locales/crowdin/mn.yml
+++ b/modules/boards/config/locales/crowdin/mn.yml
@@ -6,6 +6,8 @@ mn:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ mn:
subproject: Subproject
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/ne.yml b/modules/boards/config/locales/crowdin/ne.yml
index 1eb5eb0c5d8..c475ceb8ed2 100644
--- a/modules/boards/config/locales/crowdin/ne.yml
+++ b/modules/boards/config/locales/crowdin/ne.yml
@@ -6,6 +6,8 @@ ne:
boards:
label_board: "बोर्ड"
label_boards: "बोर्डहरु"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ ne:
subproject: Subproject
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/nl.yml b/modules/boards/config/locales/crowdin/nl.yml
index 731030c538f..8969b6f9b3d 100644
--- a/modules/boards/config/locales/crowdin/nl.yml
+++ b/modules/boards/config/locales/crowdin/nl.yml
@@ -6,6 +6,8 @@ nl:
boards:
label_board: "Bord"
label_boards: "Borden"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basis
action: "Actiebord (%{attribute})"
@@ -16,3 +18,19 @@ nl:
subproject: Subproject
subtasks: Ouder-kind
basic: Basis
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/no.yml b/modules/boards/config/locales/crowdin/no.yml
index 1087a93f6ea..cf0fa31530a 100644
--- a/modules/boards/config/locales/crowdin/no.yml
+++ b/modules/boards/config/locales/crowdin/no.yml
@@ -6,6 +6,8 @@
boards:
label_board: "Tavle"
label_boards: "Tavler"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Enkel
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@
subproject: Underprosjekt
subtasks: Parent-child
basic: Enkel
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/pl.yml b/modules/boards/config/locales/crowdin/pl.yml
index 9b621328471..0337805f8d5 100644
--- a/modules/boards/config/locales/crowdin/pl.yml
+++ b/modules/boards/config/locales/crowdin/pl.yml
@@ -6,6 +6,8 @@ pl:
boards:
label_board: "Tablica"
label_boards: "Tablice"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Podstawowe
action: "Tablica działania (%{attribute})"
@@ -16,3 +18,19 @@ pl:
subproject: Podprojekt
subtasks: Nadrzędny-podrzędny
basic: Podstawowe
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/pt.yml b/modules/boards/config/locales/crowdin/pt.yml
index 27dd60a91cc..d7874fa8e8f 100644
--- a/modules/boards/config/locales/crowdin/pt.yml
+++ b/modules/boards/config/locales/crowdin/pt.yml
@@ -6,6 +6,8 @@ pt:
boards:
label_board: "Quadro"
label_boards: "Quadros"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Básico
action: "Quadro de ação (%{attribute})"
@@ -16,3 +18,19 @@ pt:
subproject: Subprojeto
subtasks: Primeiro-secundário
basic: Básico
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/ro.yml b/modules/boards/config/locales/crowdin/ro.yml
index dd4a750341d..6e40409a71a 100644
--- a/modules/boards/config/locales/crowdin/ro.yml
+++ b/modules/boards/config/locales/crowdin/ro.yml
@@ -6,6 +6,8 @@ ro:
boards:
label_board: "Tablă"
label_boards: "Panouri"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: De bază
action: "Tablă de acțiune (%{attribute})"
@@ -16,3 +18,19 @@ ro:
subproject: Subproiect
subtasks: Părinte-copil
basic: De bază
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/ru.yml b/modules/boards/config/locales/crowdin/ru.yml
index 6c443d07de6..56550d3c2fb 100644
--- a/modules/boards/config/locales/crowdin/ru.yml
+++ b/modules/boards/config/locales/crowdin/ru.yml
@@ -6,6 +6,8 @@ ru:
boards:
label_board: "Доска"
label_boards: "Доски"
+ label_create_new_board: "Создать новую доску"
+ label_board_type: "Тип доски"
board_types:
free: Базовый
action: "Доска действий (%{attribute})"
@@ -16,3 +18,19 @@ ru:
subproject: Подпроект
subtasks: Родитель-Потомок
basic: Базовый
+ board_type_descriptions:
+ basic: >
+ Начните с нуля с чистой доски. Вручную добавьте карточки и столбцы на эту доску.
+ status: >
+ Базовая доска в стиле канбан со столбцами для определения статуса, такими как "Сделано", "В процессе", "Готово".
+ assignee: >
+ Доска с автоматическими колонками на основе назначенных пользователей. Идеально подходит для отправки пакетов работ.
+ version: >
+ Доска с автоматизированными колонками на основе атрибута версии. Идеально подходит для планирования разработки продукта.
+ subproject: >
+ Доска с автоматизированными колонками для подпроектов. Перетаскивание пакетов работ в другие списки соответствующим образом обновляет подпроект.
+ subtasks: >
+ Доска с автоматизированными колонками для подэлементов. Перетаскивание пакетов работ в другие списки соответствующим образом обновляет родительский список.
+ upsale:
+ teaser_text: 'Хотите автоматизировать работу с досками? Обновите платный план до корпоративного.'
+ upgrade: 'Обновить сейчас'
diff --git a/modules/boards/config/locales/crowdin/rw.yml b/modules/boards/config/locales/crowdin/rw.yml
index e99acf5deef..8ae6d3036d1 100644
--- a/modules/boards/config/locales/crowdin/rw.yml
+++ b/modules/boards/config/locales/crowdin/rw.yml
@@ -6,6 +6,8 @@ rw:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ rw:
subproject: Subproject
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/si.yml b/modules/boards/config/locales/crowdin/si.yml
index cc541ddbc8c..8dde53ef9cf 100644
--- a/modules/boards/config/locales/crowdin/si.yml
+++ b/modules/boards/config/locales/crowdin/si.yml
@@ -6,6 +6,8 @@ si:
boards:
label_board: "මණ්ඩලය"
label_boards: "පුවරු"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ si:
subproject: උප ව්යාපෘතිය
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/sk.yml b/modules/boards/config/locales/crowdin/sk.yml
index df61548949d..144bfafb57d 100644
--- a/modules/boards/config/locales/crowdin/sk.yml
+++ b/modules/boards/config/locales/crowdin/sk.yml
@@ -6,6 +6,8 @@ sk:
boards:
label_board: "Nástenka"
label_boards: "Nástenky"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ sk:
subproject: Podprojekt
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/sl.yml b/modules/boards/config/locales/crowdin/sl.yml
index acbcd24f5a4..799c0c1fa02 100644
--- a/modules/boards/config/locales/crowdin/sl.yml
+++ b/modules/boards/config/locales/crowdin/sl.yml
@@ -6,6 +6,8 @@ sl:
boards:
label_board: "Tabla"
label_boards: "Table"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Osnovno
action: "Tabla opravil (%{attribute})"
@@ -16,3 +18,19 @@ sl:
subproject: Podprojekt
subtasks: Starš-otrok
basic: Osnovno
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/sr.yml b/modules/boards/config/locales/crowdin/sr.yml
index b9c780c3b74..5e6a8a7166d 100644
--- a/modules/boards/config/locales/crowdin/sr.yml
+++ b/modules/boards/config/locales/crowdin/sr.yml
@@ -6,6 +6,8 @@ sr:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ sr:
subproject: Subproject
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/sv.yml b/modules/boards/config/locales/crowdin/sv.yml
index 0e8d4e0eed6..7a3f94e8cdf 100644
--- a/modules/boards/config/locales/crowdin/sv.yml
+++ b/modules/boards/config/locales/crowdin/sv.yml
@@ -6,6 +6,8 @@ sv:
boards:
label_board: "Tavla"
label_boards: "Tavlor"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Enkel
action: "Åtgärdstavla (%{attribute})"
@@ -16,3 +18,19 @@ sv:
subproject: Delprojekt
subtasks: Parent-child
basic: Enkel
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/th.yml b/modules/boards/config/locales/crowdin/th.yml
index 1813ba0d8d3..0f1671c40f0 100644
--- a/modules/boards/config/locales/crowdin/th.yml
+++ b/modules/boards/config/locales/crowdin/th.yml
@@ -6,6 +6,8 @@ th:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ th:
subproject: โครงการย่อย
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/tr.yml b/modules/boards/config/locales/crowdin/tr.yml
index 9a1c3dcca76..7f7beeeddec 100644
--- a/modules/boards/config/locales/crowdin/tr.yml
+++ b/modules/boards/config/locales/crowdin/tr.yml
@@ -6,6 +6,8 @@ tr:
boards:
label_board: "Pano"
label_boards: "Panolar"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Temel
action: "Eylem kurulu (%{attribute})"
@@ -16,3 +18,19 @@ tr:
subproject: Alt proje
subtasks: Ana-Yavru
basic: Temel
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/uk.yml b/modules/boards/config/locales/crowdin/uk.yml
index 8c90fdcc000..76da9a1d3a1 100644
--- a/modules/boards/config/locales/crowdin/uk.yml
+++ b/modules/boards/config/locales/crowdin/uk.yml
@@ -6,6 +6,8 @@ uk:
boards:
label_board: "Дошка"
label_boards: "Дошки"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Базова
action: "Дошка дій (%{attribute})"
@@ -16,3 +18,19 @@ uk:
subproject: Підпроєкт
subtasks: Батьківські й дочірні елементи
basic: Базова
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/vi.yml b/modules/boards/config/locales/crowdin/vi.yml
index 152bef95432..adcf67e11f0 100644
--- a/modules/boards/config/locales/crowdin/vi.yml
+++ b/modules/boards/config/locales/crowdin/vi.yml
@@ -6,6 +6,8 @@ vi:
boards:
label_board: "Bảng"
label_boards: "Bảng"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -16,3 +18,19 @@ vi:
subproject: Dự án con
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/zh-TW.yml b/modules/boards/config/locales/crowdin/zh-TW.yml
index 402471b95ca..c55b920ee1a 100644
--- a/modules/boards/config/locales/crowdin/zh-TW.yml
+++ b/modules/boards/config/locales/crowdin/zh-TW.yml
@@ -6,6 +6,8 @@ zh-TW:
boards:
label_board: "面板"
label_boards: "看板"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: 基本
action: "行動項看板 (%{attribute})"
@@ -16,3 +18,19 @@ zh-TW:
subproject: 子專案
subtasks: Parent-child
basic: 基本
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board. Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users. Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute. Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects. Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements. Dragging work packages to other lists updates the parent accordingly.
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/en.yml b/modules/boards/config/locales/en.yml
index 893ca755275..e303e52bf32 100644
--- a/modules/boards/config/locales/en.yml
+++ b/modules/boards/config/locales/en.yml
@@ -7,6 +7,8 @@ en:
boards:
label_board: "Board"
label_boards: "Boards"
+ label_create_new_board: "Create new board"
+ label_board_type: "Board type"
board_types:
free: Basic
action: "Action board (%{attribute})"
@@ -17,3 +19,25 @@ en:
subproject: Subproject
subtasks: Parent-child
basic: Basic
+ board_type_descriptions:
+ basic: >
+ Start from scratch with a blank board.
+ Manually add cards and columns to this board.
+ status: >
+ Basic kanban style board with columns for status such as To Do, In Progress, Done.
+ assignee: >
+ Board with automated columns based on assigned users.
+ Ideal for dispatching work packages.
+ version: >
+ Board with automated columns based on the version attribute.
+ Ideal for planning product development.
+ subproject: >
+ Board with automated columns for subprojects.
+ Dragging work packages to other lists updates the (sub-)project accordingly.
+ subtasks: >
+ Board with automated columns for sub-elements.
+ Dragging work packages to other lists updates the parent accordingly.
+
+ upsale:
+ teaser_text: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
+ upgrade: 'Upgrade now'
diff --git a/modules/boards/config/routes.rb b/modules/boards/config/routes.rb
index 8a930e909ea..89df20b5eff 100644
--- a/modules/boards/config/routes.rb
+++ b/modules/boards/config/routes.rb
@@ -1,11 +1,15 @@
OpenProject::Application.routes.draw do
get '/boards/all', to: 'boards/boards#overview'
- scope '', as: :work_package_boards do
- get '/boards(/*state)', to: 'boards/boards#index'
- end
+ resources :boards,
+ controller: 'boards/boards',
+ only: %i[index show new create destroy],
+ as: :work_package_boards
scope 'projects/:project_id', as: 'project' do
- get '/boards(/*state)', to: 'boards/boards#index', as: :work_package_boards
+ resources :boards,
+ controller: 'boards/boards',
+ only: %i[index show new create],
+ as: :work_package_boards
end
end
diff --git a/modules/boards/lib/open_project/boards/engine.rb b/modules/boards/lib/open_project/boards/engine.rb
index 5db32f429b2..016c0c97aa9 100644
--- a/modules/boards/lib/open_project/boards/engine.rb
+++ b/modules/boards/lib/open_project/boards/engine.rb
@@ -29,11 +29,11 @@ module OpenProject::Boards
name: 'OpenProject Boards' do
project_module :board_view, dependencies: :work_package_tracking, order: 80 do
permission :show_board_views,
- { 'boards/boards': %i[index overview] },
+ { 'boards/boards': %i[index show overview] },
dependencies: :view_work_packages,
contract_actions: { boards: %i[read] }
permission :manage_board_views,
- { 'boards/boards': %i[index] },
+ { 'boards/boards': %i[index show new create destroy] },
dependencies: :manage_public_queries,
contract_actions: { boards: %i[create update destroy] }
end
diff --git a/modules/boards/spec/contracts/boards/create_contract_spec.rb b/modules/boards/spec/contracts/boards/create_contract_spec.rb
new file mode 100644
index 00000000000..33f0014e9ac
--- /dev/null
+++ b/modules/boards/spec/contracts/boards/create_contract_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2023 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write 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 'contracts/shared/model_contract_shared_context'
+
+RSpec.describe Boards::CreateContract do
+ include_context 'ModelContract shared context'
+ let(:project) { build_stubbed(:project) }
+ let(:name) { 'My Board' }
+ let(:user) { build_stubbed(:user) }
+ let(:grid) do
+ build_stubbed(:board_grid, project:, name:)
+ end
+ let(:contract) { described_class.new(grid, user) }
+
+ context 'when :project not provided' do
+ let(:project) { nil }
+
+ it_behaves_like 'contract is invalid', project: :blank
+ end
+
+ context 'when :name not provided' do
+ let(:name) { nil }
+
+ it_behaves_like 'contract is invalid', name: :blank
+ end
+end
diff --git a/modules/boards/spec/features/action_boards/assignee_board_spec.rb b/modules/boards/spec/features/action_boards/assignee_board_spec.rb
index 0ea6828f7e2..439227ed677 100644
--- a/modules/boards/spec/features/action_boards/assignee_board_spec.rb
+++ b/modules/boards/spec/features/action_boards/assignee_board_spec.rb
@@ -92,7 +92,9 @@ RSpec.describe 'Assignee action board',
board_index.visit!
# Create new board
- board_page = board_index.create_board action: :Assignee, expect_empty: true
+ board_page = board_index.create_board title: 'My Assignee Board',
+ action: 'Assignee',
+ expect_empty: true
# Expect no assignees to be present
board_page.expect_empty
@@ -113,7 +115,7 @@ RSpec.describe 'Assignee action board',
board_page.expect_list_option '(none)'
board_page.board(reload: true) do |board|
- expect(board.name).to eq 'Action board (assignee)'
+ expect(board.name).to eq 'My Assignee Board'
queries = board.contained_queries
expect(queries.count).to eq(3)
diff --git a/modules/boards/spec/features/action_boards/custom_field_filters_spec.rb b/modules/boards/spec/features/action_boards/custom_field_filters_spec.rb
index c14bb1ef0c6..1b4e236bce8 100644
--- a/modules/boards/spec/features/action_boards/custom_field_filters_spec.rb
+++ b/modules/boards/spec/features/action_boards/custom_field_filters_spec.rb
@@ -92,7 +92,7 @@ RSpec.describe 'Custom field filter in boards', js: true, with_ee: %i[board_view
board_index.visit!
# Create new board
- board_page = board_index.create_board action: :Status
+ board_page = board_index.create_board action: 'Status'
# expect lists of default status
board_page.expect_list 'Open'
@@ -103,7 +103,7 @@ RSpec.describe 'Custom field filter in boards', js: true, with_ee: %i[board_view
filters.add_filter_by(custom_field.name,
'is (OR)',
- ['A', 'B'],
+ %w[A B],
custom_field.attribute_name(:camel_case))
board_page.expect_changed
diff --git a/modules/boards/spec/features/action_boards/status_board_spec.rb b/modules/boards/spec/features/action_boards/status_board_spec.rb
index 36a6feec7f4..ed018e58abd 100644
--- a/modules/boards/spec/features/action_boards/status_board_spec.rb
+++ b/modules/boards/spec/features/action_boards/status_board_spec.rb
@@ -94,7 +94,7 @@ RSpec.describe 'Status action board', js: true, with_ee: %i[board_view] do
board_index.visit!
# Create new board
- board_page = board_index.create_board action: :Status
+ board_page = board_index.create_board action: 'Status'
# expect lists of default status
board_page.expect_list 'Open'
@@ -111,7 +111,7 @@ RSpec.describe 'Status action board', js: true, with_ee: %i[board_view] do
board_index.visit!
# Create new board
- board_page = board_index.create_board action: :Status
+ board_page = board_index.create_board action: 'Status'
board_page.add_list option: 'Whatever'
board_page.expect_list 'Whatever'
@@ -152,7 +152,8 @@ RSpec.describe 'Status action board', js: true, with_ee: %i[board_view] do
board_index.visit!
# Create new board
- board_page = board_index.create_board action: :Status
+ board_page = board_index.create_board title: 'My Status Board',
+ action: 'Status'
# expect lists of default status
board_page.expect_list 'Open'
@@ -161,7 +162,7 @@ RSpec.describe 'Status action board', js: true, with_ee: %i[board_view] do
board_page.expect_list 'Closed'
board_page.board(reload: true) do |board|
- expect(board.name).to eq 'Action board (status)'
+ expect(board.name).to eq 'My Status Board'
queries = board.contained_queries
expect(queries.count).to eq(2)
@@ -298,14 +299,15 @@ RSpec.describe 'Status action board', js: true, with_ee: %i[board_view] do
board_index.visit!
# Create new board
- board_page = board_index.create_board action: :Status
+ board_page = board_index.create_board action: 'Status'
# expect lists of default status
board_page.expect_list 'Open'
expect(board_page.list_count).to eq(1)
+ board_index.visit!
# Create another status board
- second_board_page = board_index.create_board action: :Status, via_toolbar: true
+ second_board_page = board_index.create_board action: 'Status', via_toolbar: false
# Expect only one list with the default status
second_board_page.expect_list 'Open'
diff --git a/modules/boards/spec/features/action_boards/status_type_moving_board_spec.rb b/modules/boards/spec/features/action_boards/status_type_moving_board_spec.rb
index 239d9552f4e..e6b9fa3d327 100644
--- a/modules/boards/spec/features/action_boards/status_type_moving_board_spec.rb
+++ b/modules/boards/spec/features/action_boards/status_type_moving_board_spec.rb
@@ -111,7 +111,7 @@ RSpec.describe 'Status action board', js: true, with_ee: %i[board_view] do
board_index.visit!
# Create new board
- board_page = board_index.create_board action: :Status
+ board_page = board_index.create_board action: 'Status'
# expect lists of default status
board_page.expect_list 'Open'
diff --git a/modules/boards/spec/features/action_boards/subproject_board_spec.rb b/modules/boards/spec/features/action_boards/subproject_board_spec.rb
index 9afa89bc00f..8b38e17b1b5 100644
--- a/modules/boards/spec/features/action_boards/subproject_board_spec.rb
+++ b/modules/boards/spec/features/action_boards/subproject_board_spec.rb
@@ -82,7 +82,7 @@ RSpec.describe 'Subproject action board', js: true, with_ee: %i[board_view] do
board_index.visit!
# Create new board
- board_page = board_index.create_board action: :Subproject, expect_empty: true
+ board_page = board_index.create_board action: 'Subproject', expect_empty: true
# Expect we can add a child 1
board_page.add_list option: 'Child 1'
@@ -111,7 +111,9 @@ RSpec.describe 'Subproject action board', js: true, with_ee: %i[board_view] do
board_index.visit!
# Create new board
- board_page = board_index.create_board action: :Subproject, expect_empty: true
+ board_page = board_index.create_board title: 'My Subproject Board',
+ action: 'Subproject',
+ expect_empty: true
# Expect we can add a child 1
board_page.add_list option: 'Child 1'
@@ -124,7 +126,7 @@ RSpec.describe 'Subproject action board', js: true, with_ee: %i[board_view] do
board_page.expect_movable 'Child 1', 'Foo', movable: true
board_page.board(reload: true) do |board|
- expect(board.name).to eq 'Action board (subproject)'
+ expect(board.name).to eq 'My Subproject Board'
queries = board.contained_queries
expect(queries.count).to eq(1)
diff --git a/modules/boards/spec/features/action_boards/subtasks_board_spec.rb b/modules/boards/spec/features/action_boards/subtasks_board_spec.rb
index 34296bee7ee..fbc089a682c 100644
--- a/modules/boards/spec/features/action_boards/subtasks_board_spec.rb
+++ b/modules/boards/spec/features/action_boards/subtasks_board_spec.rb
@@ -27,8 +27,8 @@
#++
require 'spec_helper'
-require_relative './../support//board_index_page'
-require_relative './../support/board_page'
+require_relative '../support//board_index_page'
+require_relative '../support/board_page'
RSpec.describe 'Subtasks action board', js: true, with_ee: %i[board_view] do
let(:type) { create(:type_standard) }
@@ -62,7 +62,7 @@ RSpec.describe 'Subtasks action board', js: true, with_ee: %i[board_view] do
board_index.visit!
# Create new board
- board_page = board_index.create_board action: :Parent_child, expect_empty: true
+ board_page = board_index.create_board action: 'Parent-child', expect_empty: true
# Expect we can add a work package column
board_page.add_list option: 'Parent WP'
@@ -86,7 +86,9 @@ RSpec.describe 'Subtasks action board', js: true, with_ee: %i[board_view] do
board_index.visit!
# Create new board
- board_page = board_index.create_board action: :Parent_child, expect_empty: true
+ board_page = board_index.create_board title: 'My Parent-child Board',
+ action: 'Parent-child',
+ expect_empty: true
# Expect we can add a child 1
board_page.add_list option: 'Parent WP'
@@ -99,7 +101,7 @@ RSpec.describe 'Subtasks action board', js: true, with_ee: %i[board_view] do
board_page.expect_movable 'Parent WP', 'Child', movable: true
board_page.board(reload: true) do |board|
- expect(board.name).to eq 'Action board (parent-child)'
+ expect(board.name).to eq 'My Parent-child Board'
queries = board.contained_queries
expect(queries.count).to eq(1)
@@ -157,7 +159,7 @@ RSpec.describe 'Subtasks action board', js: true, with_ee: %i[board_view] do
it 'prevents adding a work package to its own column' do
board_index.visit!
- board_page = board_index.create_board action: :Parent_child, expect_empty: true
+ board_page = board_index.create_board action: 'Parent-child', expect_empty: true
board_page.add_list option: 'Parent WP'
board_page.expect_list 'Parent WP'
board_page.expect_card 'Parent WP', 'Child'
diff --git a/modules/boards/spec/features/action_boards/version_board_spec.rb b/modules/boards/spec/features/action_boards/version_board_spec.rb
index 25d3ac3227b..90186a811c4 100644
--- a/modules/boards/spec/features/action_boards/version_board_spec.rb
+++ b/modules/boards/spec/features/action_boards/version_board_spec.rb
@@ -75,7 +75,8 @@ RSpec.describe 'Version action board', js: true, with_ee: %i[board_view] do
board_index.visit!
# Create new board
- board_page = board_index.create_board action: :Version
+ board_page = board_index.create_board title: 'My Version Board',
+ action: 'Version'
# expect lists of open versions
board_page.expect_list 'Open version'
@@ -96,7 +97,7 @@ RSpec.describe 'Version action board', js: true, with_ee: %i[board_view] do
login_as(user)
end
- it 'allows management of boards' do
+ fit 'allows management of boards' do
board_page = create_new_version_board
board_page.expect_card 'Open version', work_package.subject, present: true
@@ -105,7 +106,7 @@ RSpec.describe 'Version action board', js: true, with_ee: %i[board_view] do
board_page.expect_list_option 'Closed version'
board_page.board(reload: true) do |board|
- expect(board.name).to eq 'Action board (version)'
+ expect(board.name).to eq 'My Version Board'
queries = board.contained_queries
expect(queries.count).to eq(2)
diff --git a/modules/boards/spec/features/board_conflicts_spec.rb b/modules/boards/spec/features/board_conflicts_spec.rb
index e8d1ceed5c0..10eb979f9ba 100644
--- a/modules/boards/spec/features/board_conflicts_spec.rb
+++ b/modules/boards/spec/features/board_conflicts_spec.rb
@@ -61,7 +61,7 @@ RSpec.describe 'Board remote changes resolution', js: true, with_ee: %i[board_vi
board_index.visit!
# Create new board
- board_page = board_index.create_board action: :Status
+ board_page = board_index.create_board action: 'Status'
# expect lists of default status
board_page.expect_list 'Open'
diff --git a/modules/boards/spec/features/board_enterprise_spec.rb b/modules/boards/spec/features/board_enterprise_spec.rb
index 08a816ba008..9a768d09546 100644
--- a/modules/boards/spec/features/board_enterprise_spec.rb
+++ b/modules/boards/spec/features/board_enterprise_spec.rb
@@ -27,8 +27,8 @@
#++
require 'spec_helper'
-require_relative './support/board_index_page'
-require_relative './support/board_page'
+require_relative 'support/board_index_page'
+require_relative 'support/board_page'
RSpec.describe 'Boards enterprise spec', :js, :with_cuprite do
shared_let(:admin) { create(:admin) }
@@ -56,8 +56,8 @@ RSpec.describe 'Boards enterprise spec', :js, :with_cuprite do
it 'disabled all action boards' do
page.find('.toolbar-item a', text: 'Board').click
- expect(page).to have_selector('[data-qa-selector="op-tile-block"]:not([disabled])', text: 'Basic')
- expect(page).to have_selector('[data-qa-selector="op-tile-block"]:disabled', count: 5)
+ expect(page).to have_selector('[data-qa-selector="op-tile-block"]:not(.-disabled)', text: 'Basic')
+ expect(page).to have_selector('[data-qa-selector="op-tile-block"].-disabled', count: 5)
end
it 'shows a banner on the action board' do
@@ -85,7 +85,7 @@ RSpec.describe 'Boards enterprise spec', :js, :with_cuprite do
it 'enables all options' do
page.find('.toolbar-item a', text: 'Board').click
- expect(page).to have_selector('[data-qa-selector="op-tile-block"]:not([disabled])', count: 6)
+ expect(page).to have_selector('[data-qa-selector="op-tile-block"]:not(.-disabled)', count: 6)
end
it 'shows the action board' do
diff --git a/modules/boards/spec/features/board_highlighting_spec.rb b/modules/boards/spec/features/board_highlighting_spec.rb
index 40eaa63b0c5..126cc14b345 100644
--- a/modules/boards/spec/features/board_highlighting_spec.rb
+++ b/modules/boards/spec/features/board_highlighting_spec.rb
@@ -74,7 +74,7 @@ RSpec.describe 'Work Package boards spec', js: true, with_ee: %i[board_view] do
it 'navigates from boards to the WP full view and back' do
board_index.visit!
- board_page = board_index.create_board action: :Status
+ board_page = board_index.create_board action: 'Status'
# See the work packages
board_page.expect_query 'Open', editable: false
diff --git a/modules/boards/spec/features/board_index_spec.rb b/modules/boards/spec/features/board_index_spec.rb
new file mode 100644
index 00000000000..bf9c8490263
--- /dev/null
+++ b/modules/boards/spec/features/board_index_spec.rb
@@ -0,0 +1,142 @@
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2023 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write 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/board_index_page'
+
+RSpec.describe 'Work Package Project Boards Index Page',
+ :js,
+ :with_cuprite,
+ with_ee: %i[board_view],
+ with_flag: { more_global_index_pages: true } do
+ # The identifier is important to test https://community.openproject.com/wp/29754
+ shared_let(:project) { create(:project, identifier: 'boards', enabled_module_names: %i[work_package_tracking board_view]) }
+
+ shared_let(:management_permissions) do
+ %i[show_board_views manage_board_views add_work_packages view_work_packages manage_public_queries]
+ end
+ shared_let(:view_only_permissions) do
+ %i[show_board_views add_work_packages view_work_packages]
+ end
+
+ shared_let(:priority) { create(:default_priority) }
+ shared_let(:status) { create(:default_status) }
+
+ let(:user) do
+ create(:user,
+ member_in_project: project,
+ member_through_role: role)
+ end
+ let(:permissions) { management_permissions }
+ let(:role) { create(:role, permissions:) }
+
+ let(:board_view) { create(:board_grid_with_query, name: 'My board', project:) }
+ let(:other_board_view) { create(:board_grid_with_query, name: 'My other board', project:) }
+
+ let(:board_index) { Pages::BoardIndex.new(project) }
+
+ before do
+ login_as user
+ end
+
+ context 'as a user with board management permissions' do
+ let(:permissions) { management_permissions }
+
+ it 'shows a create button' do
+ board_index.visit!
+
+ board_index.expect_create_button
+ end
+ end
+
+ context 'as a user without board management permissions' do
+ let(:permissions) { view_only_permissions }
+
+ it 'does not show a create button' do
+ board_index.visit!
+
+ board_index.expect_no_create_button
+ end
+ end
+
+ context 'when no boards exist' do
+ it 'displays the empty message' do
+ board_index.visit!
+
+ board_index.expect_no_boards_listed
+ end
+ end
+
+ context 'when boards exist' do
+ before do
+ board_view
+ other_board_view
+ end
+
+ it 'lists the boards' do
+ board_index.visit!
+
+ board_index.expect_boards_listed(board_view, other_board_view)
+ end
+
+ context 'as a user with board management permissions' do
+ let(:permissions) { management_permissions }
+
+ it 'renders delete links for each board' do
+ board_index.visit!
+
+ board_index.expect_delete_button(board_view)
+ board_index.expect_delete_button(other_board_view)
+ end
+ end
+
+ context 'as a user without board management permissions' do
+ let(:permissions) { view_only_permissions }
+
+ it 'does not render delete links' do
+ board_index.visit!
+
+ board_index.expect_no_delete_button(board_view)
+ board_index.expect_no_delete_button(other_board_view)
+ end
+ end
+
+ it 'paginates results', with_settings: { per_page_options: '1' } do
+ # First page displays the historically last meeting
+ board_index.visit!
+ board_index.expect_boards_listed(board_view)
+ board_index.expect_boards_not_listed(other_board_view)
+
+ board_index.expect_to_be_on_page(1)
+
+ board_index.to_page(2)
+ board_index.expect_boards_listed(other_board_view)
+ board_index.expect_boards_not_listed(board_view)
+ end
+ end
+end
diff --git a/modules/boards/spec/features/board_management_spec.rb b/modules/boards/spec/features/board_management_spec.rb
index c32b2d30292..7321e2792bd 100644
--- a/modules/boards/spec/features/board_management_spec.rb
+++ b/modules/boards/spec/features/board_management_spec.rb
@@ -116,7 +116,7 @@ RSpec.describe 'Board management spec', js: true, with_ee: %i[board_view] do
board_index.expect_board board_view.name
# Create new board
- board_page = board_index.create_board action: nil
+ board_page = board_index.create_board
board_page.rename_board 'Board test'
# Rename through toolbar
@@ -203,6 +203,19 @@ RSpec.describe 'Board management spec', js: true, with_ee: %i[board_view] do
board_page.delete_board
board_index.expect_board 'Board foo', present: false
end
+
+ it 'allows creating a new project board form via the sidebar' do
+ board_index.visit!
+
+ board_page = board_index.create_board title: 'My Board created via the sidebar',
+ via_toolbar: false
+ board_page.board(reload: true) do |board|
+ expect(board.name).to eq 'My Board created via the sidebar'
+ queries = board.contained_queries
+ expect(queries.count).to eq(1)
+ expect(queries.first.name).to eq 'Unnamed list'
+ end
+ end
end
context 'with view boards + work package permission' do
diff --git a/modules/boards/spec/features/board_navigation_spec.rb b/modules/boards/spec/features/board_navigation_spec.rb
index 8a3f21e7de9..62cd270e6b4 100644
--- a/modules/boards/spec/features/board_navigation_spec.rb
+++ b/modules/boards/spec/features/board_navigation_spec.rb
@@ -74,7 +74,7 @@ RSpec.describe 'Work Package boards spec', js: true, with_ee: %i[board_view] do
# Click back goes back to the board
find('.work-packages-back-button').click
- expect(page).to have_current_path project_work_package_boards_path(project, board_view.id)
+ expect(page).to have_current_path project_work_package_board_path(project, board_view)
# Open the details page with the info icon
card = board_page.card_for(wp)
@@ -98,14 +98,14 @@ RSpec.describe 'Work Package boards spec', js: true, with_ee: %i[board_view] do
end
it 'navigates correctly the path from overview page to the boards page' do
- visit "/projects/#{project.identifier}"
+ visit project_path(project)
item = page.find('#menu-sidebar li[data-name="board_view"]', wait: 10)
item.find('.toggler').click
subitem = page.find('[data-qa-selector="op-sidemenu--item-action--Myboard"]', wait: 10)
# Ends with boards due to lazy route
- expect(subitem[:href]).to end_with "/projects/#{project.identifier}/boards"
+ expect(subitem[:href]).to end_with project_work_package_boards_path(project)
subitem.click
@@ -128,6 +128,7 @@ RSpec.describe 'Work Package boards spec', js: true, with_ee: %i[board_view] do
# Open the details page with the info icon
card = board_page.card_for(wp)
split_view = card.open_details_view
+ split_view.ensure_page_loaded
split_view.expect_subject
split_view.switch_to_tab tab: 'Relations'
diff --git a/modules/boards/spec/features/board_overview_spec.rb b/modules/boards/spec/features/board_overview_spec.rb
index 60111488f78..41e40409b52 100644
--- a/modules/boards/spec/features/board_overview_spec.rb
+++ b/modules/boards/spec/features/board_overview_spec.rb
@@ -29,27 +29,38 @@
require 'spec_helper'
require_relative './support/board_overview_page'
-RSpec.describe 'Work Package boards overview spec',
- with_cuprite: true,
+RSpec.describe 'Work Package Boards Overview',
+ :with_cuprite,
with_ee: %i[board_view],
with_flag: { more_global_index_pages: true } do
+ # The identifier is important to test https://community.openproject.com/wp/29754
+ shared_let(:project) { create(:project, identifier: 'boards', enabled_module_names: %i[work_package_tracking board_view]) }
+ shared_let(:other_project) { create(:project, enabled_module_names: %i[work_package_tracking board_view]) }
+
+ shared_let(:management_permissions) do
+ %i[show_board_views manage_board_views add_work_packages view_work_packages manage_public_queries]
+ end
+ shared_let(:view_only_permissions) do
+ %i[show_board_views add_work_packages view_work_packages]
+ end
+
+ shared_let(:priority) { create(:default_priority) }
+ shared_let(:status) { create(:default_status) }
+
let(:user) do
create(:user,
member_in_project: project,
member_through_role: role)
end
- # The identifier is important to test https://community.openproject.com/wp/29754
- let(:project) { create(:project, identifier: 'boards', enabled_module_names: %i[work_package_tracking board_view]) }
- let(:other_project) { create(:project, enabled_module_names: %i[work_package_tracking board_view]) }
- let(:permissions) { %i[show_board_views manage_board_views add_work_packages view_work_packages manage_public_queries] }
+ let(:permissions) { management_permissions }
let(:role) { create(:role, permissions:) }
- let!(:priority) { create(:default_priority) }
- let!(:status) { create(:default_status) }
- let(:board_overview) { Pages::BoardOverview.new }
+
let(:board_view) { create(:board_grid_with_query, name: 'My board', project:) }
let(:other_board_view) { create(:board_grid_with_query, name: 'My other board', project:) }
let(:other_project_board_view) { create(:board_grid_with_query, name: 'Unseeable Board', project: other_project) }
+ let(:board_overview) { Pages::BoardOverview.new }
+
before do
login_as user
end
@@ -60,6 +71,26 @@ RSpec.describe 'Work Package boards overview spec',
board_overview.expect_global_menu_item_selected
end
+ context 'as a user with board management permissions' do
+ let(:permissions) { management_permissions }
+
+ it 'shows a create button' do
+ board_overview.visit!
+
+ board_overview.expect_create_button
+ end
+ end
+
+ context 'as a user without board management permissions' do
+ let(:permissions) { view_only_permissions }
+
+ it 'does not show a create button' do
+ board_overview.visit!
+
+ board_overview.expect_no_create_button
+ end
+ end
+
context 'when no boards exist' do
it 'displays the empty message' do
board_overview.visit!
@@ -80,7 +111,7 @@ RSpec.describe 'Work Package boards overview spec',
end
end
- context 'when boards exists' do
+ context 'when boards exist' do
before do
board_view
other_board_view
@@ -94,6 +125,14 @@ RSpec.describe 'Work Package boards overview spec',
board_overview.expect_boards_not_listed(other_project_board_view)
end
+ it 'does not render delete links' do
+ board_overview.visit!
+
+ board_overview.expect_no_delete_button(board_view)
+ board_overview.expect_no_delete_button(other_board_view)
+ board_overview.expect_no_delete_button(other_project_board_view)
+ end
+
it 'paginates results', with_settings: { per_page_options: '1' } do
# First page displays the historically last meeting
board_overview.visit!
diff --git a/modules/boards/spec/features/board_reference_work_package_spec.rb b/modules/boards/spec/features/board_reference_work_package_spec.rb
index ecb427a7695..415d873f6f1 100644
--- a/modules/boards/spec/features/board_reference_work_package_spec.rb
+++ b/modules/boards/spec/features/board_reference_work_package_spec.rb
@@ -70,7 +70,7 @@ RSpec.describe 'Board reference work package spec', js: true, with_ee: %i[board_
board_index.visit!
# Create new board
- board_page = board_index.create_board action: nil
+ board_page = board_index.create_board
board_page.rename_list 'Unnamed list', 'First'
# Filter for Version
@@ -108,7 +108,7 @@ RSpec.describe 'Board reference work package spec', js: true, with_ee: %i[board_
board_index.visit!
# Create new board
- board_page = board_index.create_board action: nil
+ board_page = board_index.create_board
board_page.rename_list 'Unnamed list', 'First'
# Reference an existing work package
diff --git a/modules/boards/spec/features/boards_global_create_spec.rb b/modules/boards/spec/features/boards_global_create_spec.rb
new file mode 100644
index 00000000000..3fc270b9462
--- /dev/null
+++ b/modules/boards/spec/features/boards_global_create_spec.rb
@@ -0,0 +1,218 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_relative 'support/board_new_page'
+
+RSpec.describe 'Boards',
+ 'Creating a view from a Global Context',
+ :js,
+ :with_cuprite,
+ with_ee: %i[board_view] do
+ shared_let(:project) { create(:project, enabled_module_names: %i[work_package_tracking board_view]) }
+ shared_let(:other_project) { create(:project, enabled_module_names: %i[work_package_tracking board_view]) }
+ shared_let(:admin) { create(:admin) }
+
+ shared_let(:status) { create(:default_status) }
+ shared_let(:versions) { create_list(:version, 3, project:) }
+ shared_let(:excluded_versions) do
+ [
+ create(:version, project:, status: 'closed'),
+ create(:version, project: other_project, sharing: 'system')
+ ]
+ end
+
+ shared_let(:new_board_page) { Pages::NewBoard.new }
+
+ before do
+ login_as admin
+ end
+
+ context 'within the global index page' do
+ before do
+ visit boards_all_path
+ end
+
+ context 'when clicking on the create button' do
+ before do
+ new_board_page.navigate_by_create_button
+ end
+
+ it 'navigates to the global create form' do
+ expect(page).to have_current_path new_work_package_board_path
+ expect(page).to have_content I18n.t('boards.label_create_new_board')
+ end
+ end
+ end
+
+ context 'within the global create page' do
+ before do
+ new_board_page.visit!
+ end
+
+ context 'with a Community Edition', with_ee: %i[] do
+ it 'renders an enterprise banner and disables all restriced board types', :aggregate_failures do
+ expect(page).to have_selector('op-enterprise-banner')
+ expect(page).to have_selector(:radio_button, 'Basic')
+
+ %w[Status Assignee Version Subproject Parent-child].each do |restricted_board_type|
+ expect(page).to have_selector(:radio_button, restricted_board_type, disabled: true)
+ end
+ end
+ end
+
+ context 'with an Enterprise Edition' do
+ context 'with all fields set' do
+ before do
+ wait_for_reload # Halt until the project autocompleter is ready
+
+ new_board_page.set_title "Gotham Renewal Board"
+ new_board_page.set_project project
+ end
+
+ context 'when creating a "Basic" board' do
+ before do
+ new_board_page.set_board_type 'Basic'
+ new_board_page.click_on_submit
+
+ wait_for_reload
+ end
+
+ it 'creates the board and redirects me to it' do
+ expect(page).to have_text(I18n.t(:notice_successful_create))
+ expect(page).to have_current_path("/projects/#{project.identifier}/boards/#{Boards::Grid.last.id}")
+ expect(page).to have_text "Gotham Renewal Board"
+ end
+ end
+
+ context 'when creating a "Status" board' do
+ before do
+ new_board_page.set_board_type 'Status'
+ new_board_page.click_on_submit
+
+ wait_for_reload
+ end
+
+ it 'creates the board and redirects me to it' do
+ expect(page).to have_text(I18n.t(:notice_successful_create))
+ expect(page).to have_current_path("/projects/#{project.identifier}/boards/#{Boards::Grid.last.id}")
+ expect(page).to have_text "Gotham Renewal Board"
+ expect(page).to have_selector("[data-query-name='#{status.name}']")
+ end
+ end
+
+ context 'when creating an "Assignee" board' do
+ before do
+ new_board_page.set_board_type 'Assignee'
+ new_board_page.click_on_submit
+
+ wait_for_reload
+ end
+
+ it 'creates the board and redirects me to it' do
+ expect(page).to have_text(I18n.t(:notice_successful_create))
+ expect(page).to have_current_path("/projects/#{project.identifier}/boards/#{Boards::Grid.last.id}")
+ expect(page).to have_text "Gotham Renewal Board"
+ end
+ end
+
+ context 'when creating a "Version" board' do
+ before do
+ new_board_page.set_board_type 'Version'
+ new_board_page.click_on_submit
+
+ wait_for_reload
+ end
+
+ it 'creates the board and redirects me to it', :aggregate_failures do
+ expect(page).to have_text(I18n.t(:notice_successful_create))
+ expect(page).to have_current_path("/projects/#{project.identifier}/boards/#{Boards::Grid.last.id}")
+ expect(page).to have_text "Gotham Renewal Board"
+ versions.each do |version|
+ expect(page).to have_selector("[data-query-name='#{version.name}'")
+ end
+ excluded_versions.each do |version|
+ expect(page).not_to have_selector("[data-query-name='#{version.name}'")
+ end
+ end
+ end
+
+ context 'when creating a "Subproject" board' do
+ before do
+ new_board_page.set_board_type 'Subproject'
+ new_board_page.click_on_submit
+
+ wait_for_reload
+ end
+
+ it 'creates the board and redirects me to it' do
+ expect(page).to have_text(I18n.t(:notice_successful_create))
+ expect(page).to have_current_path("/projects/#{project.identifier}/boards/#{Boards::Grid.last.id}")
+ expect(page).to have_text "Gotham Renewal Board"
+ end
+ end
+
+ context 'when creating a "Parent-child" board' do
+ before do
+ new_board_page.set_board_type 'Parent-child'
+ new_board_page.click_on_submit
+
+ wait_for_reload
+ end
+
+ it 'creates the board and redirects me to it' do
+ expect(page).to have_text(I18n.t(:notice_successful_create))
+ expect(page).to have_current_path("/projects/#{project.identifier}/boards/#{Boards::Grid.last.id}")
+ expect(page).to have_text "Gotham Renewal Board"
+ end
+ end
+ end
+
+ context 'when missing a required field' do
+ describe 'title' do
+ before do
+ wait_for_reload # Halt until the project autocompleter is ready
+
+ new_board_page.set_project(project)
+ new_board_page.click_on_submit
+ end
+
+ it 'renders a required attribute validation error' do
+ expect(Boards::Grid.all).to be_empty
+
+ # Required HTML attribute just warns
+ expect(page).to have_current_path(new_work_package_board_path)
+ end
+ end
+
+ describe 'project_id' do
+ before do
+ new_board_page.set_title("Batman's Itinerary")
+ new_board_page.click_on_submit
+
+ wait_for_reload
+ end
+
+ it 'renders a required attribute validation error' do
+ expect(Boards::Grid.all).to be_empty
+
+ new_board_page.expect_toast message: "Project #{I18n.t('activerecord.errors.messages.blank')}",
+ type: :error
+ new_board_page.expect_project_dropdown
+ end
+ end
+ end
+ end
+
+ describe 'cancel button' do
+ context "when it's clicked" do
+ before do
+ new_board_page.click_on_cancel_button
+ end
+
+ it 'navigates back to the global index page' do
+ expect(page).to have_current_path(boards_all_path)
+ end
+ end
+ end
+ end
+end
diff --git a/modules/boards/spec/features/boards_sorting_spec.rb b/modules/boards/spec/features/boards_sorting_spec.rb
index 13b0f2d39be..74416b989c2 100644
--- a/modules/boards/spec/features/boards_sorting_spec.rb
+++ b/modules/boards/spec/features/boards_sorting_spec.rb
@@ -27,8 +27,8 @@
#++
require 'spec_helper'
-require_relative './support/board_index_page'
-require_relative './support/board_page'
+require_relative 'support/board_index_page'
+require_relative 'support/board_page'
RSpec.describe 'Work Package boards sorting spec', js: true, with_ee: %i[board_view] do
let(:admin) { create(:admin) }
@@ -47,38 +47,27 @@ RSpec.describe 'Work Package boards sorting spec', js: true, with_ee: %i[board_v
# By adding each board the sort of table will change
# The currently added board should be at the top
it 'sorts the boards grid and menu based on their names' do
- board_page = board_index.create_board action: nil
+ board_page = board_index.create_board title: 'My Basic Board'
- retry_block do
- board_page.back_to_index
- find('[data-qa-selector="boards-table-column--name"]', text: 'Unnamed board')
- end
+ board_page.back_to_index
+ board_index.expect_boards_listed 'My Basic Board'
+ query_menu.expect_menu_entry 'My Basic Board'
- expect(page.all('[data-qa-selector="boards-table-column--name"]').map(&:text))
- .to eq ['Unnamed board']
- query_menu.expect_menu_entry 'Unnamed board'
+ board_page = board_index.create_board title: 'My Action Board',
+ action: 'Version',
+ expect_empty: true
+ board_page.back_to_index
+ board_index.expect_boards_listed 'My Action Board',
+ 'My Basic Board'
+ query_menu.expect_menu_entry 'My Action Board'
- board_page = board_index.create_board action: :Version, expect_empty: true
- retry_block do
- board_page.back_to_index
- find('[data-qa-selector="boards-table-column--name"]', text: 'Action board (version)')
- end
-
- expect(page.all('[data-qa-selector="boards-table-column--name"]').map(&:text))
- .to eq ['Action board (version)', 'Unnamed board']
- query_menu.expect_menu_entry 'Action board (version)'
-
- board_page = board_index.create_board action: :Status
+ board_page = board_index.create_board title: 'My Status Board',
+ action: 'Status'
board_page.back_to_index
- retry_block do
- board_page.back_to_index
- find('[data-qa-selector="boards-table-column--name"]', text: 'Action board (status)')
- end
-
- expect(page.all('[data-qa-selector="boards-table-column--name"]').map(&:text))
- .to eq ['Action board (status)', 'Action board (version)', 'Unnamed board']
-
- query_menu.expect_menu_entry 'Action board (status)'
+ board_index.expect_boards_listed 'My Status Board',
+ 'My Action Board',
+ 'My Basic Board'
+ query_menu.expect_menu_entry 'My Status Board'
end
end
diff --git a/modules/boards/spec/features/support/board_index_page.rb b/modules/boards/spec/features/support/board_index_page.rb
index cb26f4b5377..d962ab27fb7 100644
--- a/modules/boards/spec/features/support/board_index_page.rb
+++ b/modules/boards/spec/features/support/board_index_page.rb
@@ -27,10 +27,11 @@
#++
require 'support/pages/page'
-require_relative './board_page'
+require_relative 'board_list_page'
+require_relative 'board_new_page'
module Pages
- class BoardIndex < Page
+ class BoardIndex < BoardListPage
attr_reader :project
def initialize(project = nil)
@@ -56,15 +57,20 @@ module Pages
expect(page).to have_conditional_selector(present, 'td.name', text: name)
end
- def create_board(action: nil, expect_empty: false, via_toolbar: false)
+ def create_board(action: 'Basic', title: "#{action} Board", expect_empty: false, via_toolbar: true)
if via_toolbar
- page.find('[data-qa-selector="sidebar--create-board-button"]').click
+ within '.toolbar-items' do
+ click_link 'Board'
+ end
else
- page.find('.toolbar-item a', text: 'Board').click
+ find('[data-qa-selector="sidebar--create-board-button"]').click
end
- text = action == nil ? 'Basic' : action.to_s[0..5]
- find('[data-qa-selector="op-tile-block-title"]', text:).click
+ new_board_page = NewBoard.new
+
+ new_board_page.set_title title
+ new_board_page.set_board_type action
+ new_board_page.click_on_submit
if expect_empty
expect(page).to have_selector('.boards-list--add-item-text', wait: 10)
diff --git a/modules/boards/spec/features/support/board_list_page.rb b/modules/boards/spec/features/support/board_list_page.rb
new file mode 100644
index 00000000000..70267f6526f
--- /dev/null
+++ b/modules/boards/spec/features/support/board_list_page.rb
@@ -0,0 +1,105 @@
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2023 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write 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 'support/pages/page'
+
+module Pages
+ class BoardListPage < Page
+ def visit!
+ raise 'Define how to visit me'
+ end
+
+ def expect_create_button
+ within '.toolbar-items' do
+ expect(page).to have_link 'Board'
+ end
+ end
+
+ def expect_no_create_button
+ within '.toolbar-items' do
+ expect(page).not_to have_link 'Board'
+ end
+ end
+
+ def expect_delete_button(board)
+ within '#content-wrapper' do
+ expect(page).to have_selector "[data-qa-selector='board-remove-#{board.id}']"
+ end
+ end
+
+ def expect_no_delete_button(board)
+ within '#content-wrapper' do
+ expect(page).not_to have_selector "[data-qa-selector='board-remove-#{board.id}']"
+ end
+ end
+
+ def expect_boards_listed(*boards)
+ board_names = if boards.all? { |board| board.to_s == board }
+ boards
+ else
+ boards.map(&:name)
+ end
+
+ within '#content-wrapper' do
+ board_names.each do |board_name|
+ expect(page).to have_selector("td.name", text: board_name)
+ end
+ end
+ end
+
+ def expect_boards_not_listed(*boards)
+ board_names = if boards.all? { |board| board.to_s == board }
+ boards
+ else
+ boards.map(&:name)
+ end
+
+ within '#content-wrapper' do
+ board_names.each do |board_name|
+ expect(page).not_to have_selector("td.title", text: board_name)
+ end
+ end
+ end
+
+ def expect_no_boards_listed
+ within '#content-wrapper' do
+ expect(page).to have_content I18n.t(:no_results_title_text)
+ end
+ end
+
+ def expect_to_be_on_page(number)
+ expect(page).to have_selector('.op-pagination--item_current', text: number)
+ end
+
+ def to_page(number)
+ within '.op-pagination--pages' do
+ click_link number.to_s
+ end
+ end
+ end
+end
diff --git a/modules/boards/spec/features/support/board_new_page.rb b/modules/boards/spec/features/support/board_new_page.rb
new file mode 100644
index 00000000000..6ad950171a4
--- /dev/null
+++ b/modules/boards/spec/features/support/board_new_page.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'support/pages/page'
+
+module Pages
+ class NewBoard < Page
+ include ::Components::Autocompleter::NgSelectAutocompleteHelpers
+
+ def visit!
+ visit new_work_package_board_path
+ end
+
+ def navigate_by_create_button
+ visit boards_all_path unless page.current_path == boards_all_path
+
+ within '.toolbar-items' do
+ click_link 'Board'
+ end
+ end
+
+ def set_title(title)
+ fill_in I18n.t(:label_title), with: title
+ end
+
+ def expect_project_dropdown
+ find "[data-qa-selector='project_id']"
+ end
+
+ def set_project(project)
+ select_autocomplete(find('[data-qa-selector="project_id"]'),
+ query: project,
+ results_selector: 'body',
+ wait_for_fetched_options: false)
+ end
+
+ def set_board_type(board_type)
+ choose board_type, match: :first
+ end
+
+ def click_on_submit
+ click_on I18n.t(:button_create)
+ end
+
+ def click_on_cancel_button
+ click_on 'Cancel'
+ end
+ end
+end
diff --git a/modules/boards/spec/features/support/board_overview_page.rb b/modules/boards/spec/features/support/board_overview_page.rb
index 793c115d545..cac8cfa9c0a 100644
--- a/modules/boards/spec/features/support/board_overview_page.rb
+++ b/modules/boards/spec/features/support/board_overview_page.rb
@@ -27,10 +27,10 @@
#++
require 'support/pages/page'
-require_relative './board_page'
+require_relative 'board_list_page'
module Pages
- class BoardOverview < Page
+ class BoardOverview < BoardListPage
def visit!
navigate_to_modules_menu_item("Boards")
end
@@ -40,37 +40,5 @@ module Pages
expect(page).to have_selector('.selected', text: 'Boards')
end
end
-
- def expect_no_boards_listed
- within '#content-wrapper' do
- expect(page).to have_content I18n.t(:no_results_title_text)
- end
- end
-
- def expect_boards_listed(*boards)
- within '#content-wrapper' do
- boards.each do |board|
- expect(page).to have_selector("td.name", text: board.name)
- end
- end
- end
-
- def expect_to_be_on_page(number)
- expect(page).to have_selector('.op-pagination--item_current', text: number)
- end
-
- def to_page(number)
- within '.op-pagination--pages' do
- click_link number.to_s
- end
- end
-
- def expect_boards_not_listed(*boards)
- within '#content-wrapper' do
- boards.each do |board|
- expect(page).not_to have_selector("td.title", text: board.name)
- end
- end
- end
end
end
diff --git a/modules/boards/spec/features/support/board_page.rb b/modules/boards/spec/features/support/board_page.rb
index 4f5ae76875e..ee0a261a08e 100644
--- a/modules/boards/spec/features/support/board_page.rb
+++ b/modules/boards/spec/features/support/board_page.rb
@@ -264,9 +264,9 @@ module Pages
def visit!
if board.project
- visit project_work_package_boards_path(project_id: board.project.id, state: board.id)
+ visit project_work_package_board_path(board.project, board)
else
- visit work_package_boards_path(state: board.id)
+ visit work_package_board_path(board)
end
end
diff --git a/modules/boards/spec/routing/boards_routing_spec.rb b/modules/boards/spec/routing/boards_routing_spec.rb
index 9a938ad8c7d..f4954bbbcf0 100644
--- a/modules/boards/spec/routing/boards_routing_spec.rb
+++ b/modules/boards/spec/routing/boards_routing_spec.rb
@@ -29,15 +29,51 @@
require 'spec_helper'
RSpec.describe 'Boards routing' do
- it {
+ it do
expect(subject)
- .to route(:get, '/projects/foobar/boards/state')
- .to(controller: 'boards/boards', action: 'index', project_id: 'foobar', state: 'state')
- }
+ .to route(:get, '/boards/all')
+ .to(controller: 'boards/boards', action: 'overview')
+ end
- it {
+ it do
expect(subject)
- .to route(:get, '/boards/state')
- .to(controller: 'boards/boards', action: 'index', state: 'state')
- }
+ .to route(:get, '/boards')
+ .to(controller: 'boards/boards', action: 'index')
+ end
+
+ it do
+ expect(subject)
+ .to route(:get, '/boards/1')
+ .to(controller: 'boards/boards', action: 'show', id: 1)
+ end
+
+ it do
+ expect(subject)
+ .to route(:get, '/projects/foobar/boards/1')
+ .to(controller: 'boards/boards', action: 'show', project_id: 'foobar', id: 1)
+ end
+
+ it do
+ expect(subject)
+ .to route(:get, '/boards/new')
+ .to(controller: 'boards/boards', action: 'new')
+ end
+
+ it do
+ expect(subject)
+ .to route(:get, '/projects/foobar/boards/new')
+ .to(controller: 'boards/boards', action: 'new', project_id: 'foobar')
+ end
+
+ it do
+ expect(subject)
+ .to route(:post, '/projects/foobar/boards')
+ .to(controller: 'boards/boards', action: 'create', project_id: 'foobar')
+ end
+
+ it do
+ expect(subject)
+ .to route(:post, '/boards')
+ .to(controller: 'boards/boards', action: 'create')
+ end
end
diff --git a/modules/storages/spec/services/storages/project_storages/shared_synchronization_trigger_examples.rb b/modules/boards/spec/services/assignee_board_create_service_spec.rb
similarity index 60%
rename from modules/storages/spec/services/storages/project_storages/shared_synchronization_trigger_examples.rb
rename to modules/boards/spec/services/assignee_board_create_service_spec.rb
index 21ec1b43956..0b9d6ef2503 100644
--- a/modules/storages/spec/services/storages/project_storages/shared_synchronization_trigger_examples.rb
+++ b/modules/boards/spec/services/assignee_board_create_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
@@ -26,29 +28,36 @@
# See COPYRIGHT and LICENSE files for more details.
#++
-RSpec.shared_examples 'a nextcloud synchronization trigger' do
- context 'when project_folder mode is automatic' do
- it 'schedules appropriate background job' do
- model_instance.project_folder_mode = 'automatic'
- subject
- expect(enqueued_jobs.count).to eq(1)
- expect(enqueued_jobs[0][:job]).to eq(Storages::ManageNextcloudIntegrationEventsJob)
- end
- end
+require 'spec_helper'
- context 'when project_folder mode is manual' do
- it 'does not schedule a background job' do
- model_instance.project_folder_mode = 'manual'
- subject
- expect(enqueued_jobs.count).to eq(0)
- end
- end
+RSpec.describe Boards::AssigneeBoardCreateService do
+ shared_let(:project) { create(:project) }
+ shared_let(:user) { build_stubbed(:admin) }
+ shared_let(:instance) { described_class.new(user:) }
- context 'when project_folder mode is inactive' do
- it 'does not schedule a background job' do
- model_instance.project_folder_mode = 'inactive'
- subject
- expect(enqueued_jobs.count).to eq(0)
+ subject { instance.call(params) }
+
+ context 'with all valid params' do
+ let(:params) do
+ {
+ name: "Gotham Renewal Board",
+ project:,
+ attribute: 'assignee'
+ }
+ end
+
+ it 'is successful' do
+ expect(subject).to be_success
+ end
+
+ it 'creates an "Assignee" board with no widgets attached', :aggregate_failures do
+ board = subject.result
+
+ expect(board.name).to eq("Gotham Renewal Board")
+ expect(board.options[:attribute]).to eq('assignee')
+ expect(board.options[:type]).to eq('action')
+
+ expect(board.widgets).to be_empty
end
end
end
diff --git a/modules/meeting/lib/open_project/meeting/patches/textile_converter_patch.rb b/modules/boards/spec/services/base_create_service_shared_examples.rb
similarity index 78%
rename from modules/meeting/lib/open_project/meeting/patches/textile_converter_patch.rb
rename to modules/boards/spec/services/base_create_service_shared_examples.rb
index 2b68eac8a6a..6e9b91e57af 100644
--- a/modules/meeting/lib/open_project/meeting/patches/textile_converter_patch.rb
+++ b/modules/boards/spec/services/base_create_service_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
@@ -26,21 +28,14 @@
# See COPYRIGHT and LICENSE files for more details.
#++
-module OpenProject::Meeting::Patches
- module TextileConverterPatch
- extend ActiveSupport::Concern
+require 'spec_helper'
- included do
- prepend(Patch)
- end
+RSpec.shared_examples 'sets the appropriate sort_criteria on each query' do
+ it '', :aggregate_failures do
+ subject
- module Patch
- def models_to_convert
- super.merge(
- ::MeetingContent => [:text],
- ::Journal::MeetingContentJournal => [:text]
- )
- end
- end
+ queries_sort_criteria = queries.map(&:sort_criteria)
+
+ expect(queries_sort_criteria).to all eq([%w[manual_sorting asc], %w[id asc]])
end
end
diff --git a/modules/boards/spec/services/basic_board_create_service_spec.rb b/modules/boards/spec/services/basic_board_create_service_spec.rb
new file mode 100644
index 00000000000..5666e0a6f45
--- /dev/null
+++ b/modules/boards/spec/services/basic_board_create_service_spec.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2023 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write 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 'base_create_service_shared_examples'
+
+RSpec.describe Boards::BasicBoardCreateService do
+ shared_let(:project) { create(:project) }
+ shared_let(:user) { build_stubbed(:admin) }
+ shared_let(:instance) { described_class.new(user:) }
+
+ subject { instance.call(params) }
+
+ context 'with all valid params' do
+ let(:params) do
+ {
+ name: "Gotham Renewal Board",
+ project:,
+ attribute: 'basic'
+ }
+ end
+
+ it 'is successful' do
+ expect(subject).to be_success
+ end
+
+ it 'creates a "Basic" board', :aggregate_failures do
+ board = subject.result
+
+ expect(board.name).to eq("Gotham Renewal Board")
+ expect(board.options[:attribute]).to be_nil
+ expect(board.options[:type]).to eq 'free'
+ end
+
+ describe 'widgets and queries' do
+ let(:board) { subject.result }
+ let(:widgets) { board.widgets }
+ let(:queries) { Query.all }
+
+ it 'creates one of each', :aggregate_failures do
+ subject
+
+ expect(widgets.count).to eq 1
+ expect(queries.count).to eq 1
+ end
+
+ it 'sets the manual sorting filter on each' do
+ subject
+
+ query_filter = queries.flat_map(&:filters).map(&:to_hash).first
+ widget_filter = widgets.flat_map { _1.options['filters'] }.first
+
+ expect(query_filter).to have_key :manual_sort
+ expect(query_filter).to eq widget_filter
+ end
+
+ it_behaves_like 'sets the appropriate sort_criteria on each query'
+ end
+ end
+end
diff --git a/modules/boards/spec/services/status_board_create_service_spec.rb b/modules/boards/spec/services/status_board_create_service_spec.rb
new file mode 100644
index 00000000000..b585eb6dca9
--- /dev/null
+++ b/modules/boards/spec/services/status_board_create_service_spec.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2023 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write 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 'base_create_service_shared_examples'
+
+RSpec.describe Boards::StatusBoardCreateService do
+ shared_let(:project) { create(:project) }
+ shared_let(:status) { create(:default_status) }
+ shared_let(:user) { build_stubbed(:admin) }
+ shared_let(:instance) { described_class.new(user:) }
+
+ subject { instance.call(params) }
+
+ context 'with all valid params' do
+ let(:params) do
+ {
+ name: "Gotham Renewal Board",
+ project:,
+ attribute: 'status'
+ }
+ end
+
+ it 'is successful' do
+ expect(subject).to be_success
+ end
+
+ it 'creates a "Status" board', :aggregate_failures do
+ board = subject.result
+
+ expect(board.name).to eq("Gotham Renewal Board")
+ expect(board.options[:attribute]).to eq('status')
+ expect(board.options[:type]).to eq('action')
+ end
+
+ describe 'widgets and queries' do
+ let(:board) { subject.result }
+ let(:widgets) { board.widgets }
+ let(:queries) { Query.all }
+
+ it 'creates one of each for the current default status', :aggregate_failures do
+ subject
+
+ expect(widgets.count).to eq 1
+ expect(queries.count).to eq 1
+ end
+
+ it 'sets the filters on each' do
+ subject
+
+ query_filter = queries.flat_map(&:filters).map(&:to_hash).first
+ widget_filter = widgets.flat_map { _1.options['filters'] }.first
+
+ expect(query_filter).to match_array(widget_filter)
+ end
+
+ it_behaves_like 'sets the appropriate sort_criteria on each query'
+ end
+ end
+end
diff --git a/modules/boards/spec/services/subproject_board_create_service_spec.rb b/modules/boards/spec/services/subproject_board_create_service_spec.rb
new file mode 100644
index 00000000000..0c9837d83f7
--- /dev/null
+++ b/modules/boards/spec/services/subproject_board_create_service_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2023 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write 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 Boards::SubprojectBoardCreateService do
+ shared_let(:project) { create(:project) }
+ shared_let(:user) { build_stubbed(:admin) }
+ shared_let(:instance) { described_class.new(user:) }
+
+ subject { instance.call(params) }
+
+ context 'with all valid params' do
+ let(:params) do
+ {
+ name: "Gotham Renewal Board",
+ project:,
+ attribute: 'subproject'
+ }
+ end
+
+ it 'is successful' do
+ expect(subject).to be_success
+ end
+
+ it 'creates a "Subproject" board with no widgets attached', :aggregate_failures do
+ board = subject.result
+
+ expect(board.name).to eq("Gotham Renewal Board")
+ expect(board.options[:attribute]).to eq('subproject')
+ expect(board.options[:type]).to eq('action')
+
+ expect(board.widgets).to be_empty
+ end
+ end
+end
diff --git a/modules/boards/spec/services/subtasks_board_create_service_spec.rb b/modules/boards/spec/services/subtasks_board_create_service_spec.rb
new file mode 100644
index 00000000000..0dedb208138
--- /dev/null
+++ b/modules/boards/spec/services/subtasks_board_create_service_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2023 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write 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 Boards::SubtasksBoardCreateService do
+ shared_let(:project) { create(:project) }
+ shared_let(:user) { build_stubbed(:admin) }
+ shared_let(:instance) { described_class.new(user:) }
+
+ subject { instance.call(params) }
+
+ context 'with all valid params' do
+ let(:params) do
+ {
+ name: "Gotham Renewal Board",
+ project:,
+ attribute: 'subtasks'
+ }
+ end
+
+ it 'is successful' do
+ expect(subject).to be_success
+ end
+
+ it 'creates a "Parent-child" board with no widgets attached', :aggregate_failures do
+ board = subject.result
+
+ expect(board.name).to eq("Gotham Renewal Board")
+ expect(board.options[:attribute]).to eq('subtasks')
+ expect(board.options[:type]).to eq('action')
+
+ expect(board.widgets).to be_empty
+ end
+ end
+end
diff --git a/modules/boards/spec/services/version_board_create_service_spec.rb b/modules/boards/spec/services/version_board_create_service_spec.rb
new file mode 100644
index 00000000000..7579668e288
--- /dev/null
+++ b/modules/boards/spec/services/version_board_create_service_spec.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2023 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write 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 'base_create_service_shared_examples'
+
+RSpec.describe Boards::VersionBoardCreateService do
+ shared_let(:project) { create(:project) }
+ shared_let(:other_project) { create(:project) }
+ shared_let(:versions) { create_list(:version, 3, project:) }
+ shared_let(:excluded_versions) do
+ [
+ create(:version, project:, status: 'closed'),
+ create(:version, project: other_project, sharing: 'system')
+ ]
+ end
+
+ shared_let(:user) { build_stubbed(:admin) }
+ shared_let(:instance) { described_class.new(user:) }
+
+ subject { instance.call(params) }
+
+ context 'with all valid params' do
+ let(:params) do
+ {
+ name: "Gotham Renewal Board",
+ project:,
+ attribute: 'version'
+ }
+ end
+
+ it 'is successful' do
+ expect(subject).to be_success
+ end
+
+ it 'creates a "Version" board', :aggregate_failures do
+ board = subject.result
+
+ expect(board.name).to eq("Gotham Renewal Board")
+ expect(board.options[:attribute]).to eq('version')
+ expect(board.options[:type]).to eq('action')
+ end
+
+ describe 'widgets and queries' do
+ let(:board) { subject.result }
+ let(:widgets) { board.widgets }
+ let(:queries) { Query.all }
+
+ it 'creates one of each per expected version', :aggregate_failures do
+ subject
+
+ expect(widgets.count).to eq(versions.count)
+ expect(queries.count).to eq(versions.count)
+
+ expect(queries.map(&:name)).to match_array(versions.map(&:name))
+ end
+
+ it 'sets the filters on each' do
+ subject
+
+ queries_filters = queries.flat_map(&:filters).map(&:to_hash)
+ widgets_filters = widgets.flat_map { _1.options['filters'] }
+
+ expect(queries_filters).to match_array(widgets_filters)
+ end
+
+ it_behaves_like 'sets the appropriate sort_criteria on each query'
+ end
+ end
+end
diff --git a/modules/calendar/app/services/calendar/create_ical_service.rb b/modules/calendar/app/services/calendar/create_ical_service.rb
index 5d542ed1690..8fef8864a54 100644
--- a/modules/calendar/app/services/calendar/create_ical_service.rb
+++ b/modules/calendar/app/services/calendar/create_ical_service.rb
@@ -166,7 +166,7 @@ module Calendar
replace_newlines: false
)
- "\n#{WorkPackage.human_attribute_name(:description)}:\n #{stripped_text}"
+ "\n#{WorkPackage.human_attribute_name(:description)}:\n#{stripped_text}"
end
end
end
diff --git a/modules/calendar/config/locales/crowdin/de.yml b/modules/calendar/config/locales/crowdin/de.yml
index e7bde975812..a2fd48cda00 100644
--- a/modules/calendar/config/locales/crowdin/de.yml
+++ b/modules/calendar/config/locales/crowdin/de.yml
@@ -2,7 +2,7 @@
de:
label_calendar: "Kalender"
label_calendar_plural: "Kalender"
- label_new_calendar: "New calendar"
+ label_new_calendar: "Neuer Kalender"
permission_view_calendar: "Kalender ansehen"
permission_manage_calendars: "Kalender verwalten"
permission_share_calendars: "iCalendar abonnieren"
diff --git a/modules/calendar/config/locales/crowdin/nl.yml b/modules/calendar/config/locales/crowdin/nl.yml
index c68d24bf574..a65d6f78579 100644
--- a/modules/calendar/config/locales/crowdin/nl.yml
+++ b/modules/calendar/config/locales/crowdin/nl.yml
@@ -2,8 +2,8 @@
nl:
label_calendar: "Kalender"
label_calendar_plural: "Agenda's"
- label_new_calendar: "New calendar"
+ label_new_calendar: "Nieuwe kalender"
permission_view_calendar: "Bekijk agenda's"
permission_manage_calendars: "Agenda's beheren"
- permission_share_calendars: "Subscribe to iCalendars"
+ permission_share_calendars: "Abonneren op iCalendars"
project_module_calendar_view: "Agenda's"
diff --git a/modules/calendar/spec/features/calendar_sharing_spec.rb b/modules/calendar/spec/features/calendar_sharing_spec.rb
index 70e4816511c..4afc37d68ac 100644
--- a/modules/calendar/spec/features/calendar_sharing_spec.rb
+++ b/modules/calendar/spec/features/calendar_sharing_spec.rb
@@ -27,7 +27,7 @@
#++
require 'spec_helper'
-require_relative './shared_context'
+require_relative 'shared_context'
RSpec.describe 'Calendar sharing via ical', js: true do
include_context 'with calendar full access'
@@ -45,6 +45,12 @@ RSpec.describe 'Calendar sharing via ical', js: true do
share_calendars
])
end
+ let(:saved_query) do
+ create(:query_with_view_work_packages_calendar,
+ user: user_with_sharing_permission,
+ project:,
+ public: false)
+ end
let(:user_without_sharing_permission) do
# missing share_calendars permission
@@ -66,13 +72,6 @@ RSpec.describe 'Calendar sharing via ical', js: true do
member_in_project: project)
end
- let(:saved_query) do
- create(:query_with_view_work_packages_calendar,
- user: user_with_sharing_permission,
- project:,
- public: false)
- end
-
context 'without sufficient permissions and the ical_enabled setting enabled', with_settings: { ical_enabled: true } do
let(:saved_query) do
create(:query_with_view_work_packages_calendar,
@@ -108,12 +107,12 @@ RSpec.describe 'Calendar sharing via ical', js: true do
# expect disabled sharing menu item
within "#settingsDropdown" do
- # expect(page).to have_button("Subscribe to iCalendar", disabled: true) # disabled selector not working
- expect(page).to have_selector(".menu-item.inactive", text: "Subscribe to iCalendar")
- page.click_button("Subscribe to iCalendar")
+ # expect(page).to have_button("Subscribe to calendar", disabled: true) # disabled selector not working
+ expect(page).to have_selector(".menu-item.inactive", text: "Subscribe to calendar")
+ page.click_button("Subscribe to calendar")
# modal should not be shown
- expect(page).not_to have_selector('.spot-modal--header', text: "Subscribe to iCalendar")
+ expect(page).not_to have_selector('.spot-modal--header', text: "Subscribe to calendar")
end
end
end
@@ -155,12 +154,12 @@ RSpec.describe 'Calendar sharing via ical', js: true do
# expect disabled sharing menu item
within "#settingsDropdown" do
- # expect(page).to have_button("Subscribe to iCalendar", disabled: true) # disabled selector not working
- expect(page).to have_selector(".menu-item.inactive", text: "Subscribe to iCalendar")
- page.click_button("Subscribe to iCalendar")
+ # expect(page).to have_button("Subscribe to calendar", disabled: true) # disabled selector not working
+ expect(page).to have_selector(".menu-item.inactive", text: "Subscribe to calendar")
+ page.click_button("Subscribe to calendar")
# modal should not be shown
- expect(page).not_to have_selector('.spot-modal--header', text: "Subscribe to iCalendar")
+ expect(page).not_to have_selector('.spot-modal--header', text: "Subscribe to calendar")
end
end
end
@@ -187,35 +186,35 @@ RSpec.describe 'Calendar sharing via ical', js: true do
# expect active sharing menu item
within "#settingsDropdown" do
- expect(page).to have_selector(".menu-item", text: "Subscribe to iCalendar")
+ expect(page).to have_selector(".menu-item", text: "Subscribe to calendar")
end
end
it 'shows a sharing modal' do
open_sharing_modal
- expect(page).to have_selector('.spot-modal--header', text: "Subscribe to iCalendar")
+ expect(page).to have_selector('.spot-modal--header', text: "Subscribe to calendar")
end
it 'closes the sharing modal when closed by user by clicking the close button' do
open_sharing_modal
- expect(page).to have_selector('.spot-modal--header', text: "Subscribe to iCalendar")
+ expect(page).to have_selector('.spot-modal--header', text: "Subscribe to calendar")
click_button "Cancel"
- expect(page).not_to have_selector('.spot-modal--header', text: "Subscribe to iCalendar")
+ expect(page).not_to have_selector('.spot-modal--header', text: "Subscribe to calendar")
end
it 'successfully requests a new tokenized iCalendar URL when a unique name is provided' do
open_sharing_modal
- fill_in "Token name", with: "A token name"
+ fill_in "Where will you be using this?", with: "A token name"
click_button "Copy URL"
# implicitly testing for success -> modal is closed and fallback message is shown
- expect(page).not_to have_selector('.spot-modal--header', text: "Subscribe to iCalendar")
+ expect(page).not_to have_selector('.spot-modal--header', text: "Subscribe to calendar")
expect(page).to have_content("/projects/#{saved_query.project.id}/calendars/#{saved_query.id}/ical?ical_token=")
# explictly testing for success message is not working in test env, probably
@@ -236,30 +235,30 @@ RSpec.describe 'Calendar sharing via ical', js: true do
click_button "Copy URL"
# modal is still shown and error message is shown
- expect(page).to have_selector('.spot-modal--header', text: "Subscribe to iCalendar")
+ expect(page).to have_selector('.spot-modal--header', text: "Subscribe to calendar")
expect(page).to have_content("Name is mandatory")
end
it 'validates the uniqueness of a name' do
open_sharing_modal
- fill_in "Token name", with: "A token name"
+ fill_in "Where will you be using this?", with: "A token name"
click_button "Copy URL"
- expect(page).not_to have_selector('.spot-modal--header', text: "Subscribe to iCalendar")
+ expect(page).not_to have_selector('.spot-modal--header', text: "Subscribe to calendar")
expect(page).to have_content("/projects/#{saved_query.project.id}/calendars/#{saved_query.id}/ical?ical_token=")
# do the same thing again, now expect validation error
open_sharing_modal
- fill_in "Token name", with: "A token name" # same name for same user and same query -> not allowed
+ fill_in "Where will you be using this?", with: "A token name" # same name for same user and same query -> not allowed
click_button "Copy URL"
# modal is still shown and error message is shown
- expect(page).to have_selector('.spot-modal--header', text: "Subscribe to iCalendar")
+ expect(page).to have_selector('.spot-modal--header', text: "Subscribe to calendar")
expect(page).to have_content("Name is already in use")
end
end
@@ -275,7 +274,7 @@ RSpec.describe 'Calendar sharing via ical', js: true do
end
expect(page).to have_selector(".title-container", text: "Working days")
- click_link 'iCalendar'
+ click_link I18n.t(:label_calendar_subscriptions)
expect(page)
.to have_field('Enable iCalendar subscriptions', checked: true)
@@ -309,12 +308,12 @@ RSpec.describe 'Calendar sharing via ical', js: true do
# expect disabled sharing menu item
within "#settingsDropdown" do
- # expect(page).to have_button("Subscribe to iCalendar", disabled: true) # disabled selector not working
- expect(page).to have_selector(".menu-item.inactive", text: "Subscribe to iCalendar")
- page.click_button("Subscribe to iCalendar")
+ # expect(page).to have_button("Subscribe to calendar", disabled: true) # disabled selector not working
+ expect(page).to have_selector(".menu-item.inactive", text: "Subscribe to calendar")
+ page.click_button("Subscribe to calendar")
# modal should not be shown
- expect(page).not_to have_selector('.spot-modal--header', text: "Subscribe to iCalendar")
+ expect(page).not_to have_selector('.spot-modal--header', text: "Subscribe to calendar")
end
end
end
@@ -330,8 +329,8 @@ RSpec.describe 'Calendar sharing via ical', js: true do
# expect disabled sharing menu item
within "#settingsDropdown" do
- expect(page).to have_selector(".menu-item", text: "Subscribe to iCalendar")
- page.click_button("Subscribe to iCalendar")
+ expect(page).to have_selector(".menu-item", text: "Subscribe to calendar")
+ page.click_button("Subscribe to calendar")
end
end
end
diff --git a/modules/calendar/spec/services/create_ical_service_spec.rb b/modules/calendar/spec/services/create_ical_service_spec.rb
index 0ecc957677b..c8f3e714ce1 100644
--- a/modules/calendar/spec/services/create_ical_service_spec.rb
+++ b/modules/calendar/spec/services/create_ical_service_spec.rb
@@ -61,14 +61,14 @@ RSpec.describe Calendar::CreateICalService, type: :model do
described_class.new
end
- let(:freezed_date_time) { DateTime.now }
+ let(:frozen_date_time) { DateTime.now }
let(:formatted_result) do
subject.result.gsub("\r\n ", "").gsub("\r", "").gsub("\\n", "\n")
end
before do
- Timecop.freeze(freezed_date_time)
+ Timecop.freeze(frozen_date_time)
end
subject do
@@ -95,7 +95,7 @@ RSpec.describe Calendar::CreateICalService, type: :model do
UID:#{work_package_with_due_date.id}@localhost:3000
DTSTART;VALUE=DATE:#{work_package_with_due_date.due_date.strftime('%Y%m%d')}
DTEND;VALUE=DATE:#{(work_package_with_due_date.due_date + 1.day).strftime('%Y%m%d')}
- DESCRIPTION:Project: #{project.name}\nType: None\nStatus: #{work_package_with_due_date.status.name}\nAssignee: \nPriority: #{work_package_with_due_date.priority.name}\n\nDescription:\n #{work_package_with_due_date.description}
+ DESCRIPTION:Project: #{project.name}\nType: None\nStatus: #{work_package_with_due_date.status.name}\nAssignee: \nPriority: #{work_package_with_due_date.priority.name}\n\nDescription:\n#{work_package_with_due_date.description}
LOCATION:http://localhost:3000/work_packages/#{work_package_with_due_date.id}
ORGANIZER;CN=#{work_package_with_due_date.author.name}:mailto:#{work_package_with_due_date.author.mail}
SUMMARY:#{work_package_with_due_date.name}
@@ -105,7 +105,7 @@ RSpec.describe Calendar::CreateICalService, type: :model do
UID:#{work_package_with_start_date.id}@localhost:3000
DTSTART;VALUE=DATE:#{work_package_with_start_date.start_date.strftime('%Y%m%d')}
DTEND;VALUE=DATE:#{(work_package_with_start_date.start_date + 1.day).strftime('%Y%m%d')}
- DESCRIPTION:Project: #{project.name}\nType: None\nStatus: #{work_package_with_start_date.status.name}\nAssignee: \nPriority: #{work_package_with_start_date.priority.name}\n\nDescription:\n #{work_package_with_start_date.description}
+ DESCRIPTION:Project: #{project.name}\nType: None\nStatus: #{work_package_with_start_date.status.name}\nAssignee: \nPriority: #{work_package_with_start_date.priority.name}\n\nDescription:\n#{work_package_with_start_date.description}
LOCATION:http://localhost:3000/work_packages/#{work_package_with_start_date.id}
ORGANIZER;CN=#{work_package_with_start_date.author.name}:mailto:#{work_package_with_start_date.author.mail}
SUMMARY:#{work_package_with_start_date.name}
@@ -115,7 +115,7 @@ RSpec.describe Calendar::CreateICalService, type: :model do
UID:#{work_package_with_start_and_due_date.id}@localhost:3000
DTSTART;VALUE=DATE:#{work_package_with_start_and_due_date.start_date.strftime('%Y%m%d')}
DTEND;VALUE=DATE:#{(work_package_with_start_and_due_date.due_date + 1.day).strftime('%Y%m%d')}
- DESCRIPTION:Project: #{project.name}\nType: None\nStatus: #{work_package_with_start_and_due_date.status.name}\nAssignee: \nPriority: #{work_package_with_start_and_due_date.priority.name}\n\nDescription:\n #{work_package_with_start_and_due_date.description}
+ DESCRIPTION:Project: #{project.name}\nType: None\nStatus: #{work_package_with_start_and_due_date.status.name}\nAssignee: \nPriority: #{work_package_with_start_and_due_date.priority.name}\n\nDescription:\n#{work_package_with_start_and_due_date.description}
LOCATION:http://localhost:3000/work_packages/#{work_package_with_start_and_due_date.id}
ORGANIZER;CN=#{work_package_with_start_and_due_date.author.name}:mailto:#{work_package_with_start_and_due_date.author.mail}
SUMMARY:#{work_package_with_start_and_due_date.name}
@@ -125,7 +125,7 @@ RSpec.describe Calendar::CreateICalService, type: :model do
UID:#{work_package_with_due_date_and_assignee.id}@localhost:3000
DTSTART;VALUE=DATE:#{work_package_with_due_date_and_assignee.due_date.strftime('%Y%m%d')}
DTEND;VALUE=DATE:#{(work_package_with_due_date_and_assignee.due_date + 1.day).strftime('%Y%m%d')}
- DESCRIPTION:Project: #{project.name}\nType: None\nStatus: #{work_package_with_due_date_and_assignee.status.name}\nAssignee: #{work_package_with_due_date_and_assignee.assigned_to.name}\nPriority: #{work_package_with_due_date_and_assignee.priority.name}\n\nDescription:\n #{work_package_with_due_date_and_assignee.description}
+ DESCRIPTION:Project: #{project.name}\nType: None\nStatus: #{work_package_with_due_date_and_assignee.status.name}\nAssignee: #{work_package_with_due_date_and_assignee.assigned_to.name}\nPriority: #{work_package_with_due_date_and_assignee.priority.name}\n\nDescription:\n#{work_package_with_due_date_and_assignee.description}
LOCATION:http://localhost:3000/work_packages/#{work_package_with_due_date_and_assignee.id}
ORGANIZER;CN=#{work_package_with_due_date_and_assignee.author.name}:mailto:#{work_package_with_due_date_and_assignee.author.mail}
SUMMARY:#{work_package_with_due_date_and_assignee.name}
diff --git a/modules/costs/config/locales/crowdin/ne.yml b/modules/costs/config/locales/crowdin/ne.yml
index a592f7cea2a..82a25daaf10 100644
--- a/modules/costs/config/locales/crowdin/ne.yml
+++ b/modules/costs/config/locales/crowdin/ne.yml
@@ -80,7 +80,7 @@ ne:
label_costlog: "Logged unit costs"
label_cost_plural: "Costs"
label_cost_type_plural: "Cost types"
- label_cost_type_specific: "Cost type #%{id}: %{name}" #%{id}: %{name}"
+ label_cost_type_specific: "Cost type #%{id}: %{name}"
label_costs_per_page: "Costs per page"
label_currency: "Currency"
label_currency_format: "Format of currency"
diff --git a/modules/documents/lib/open_project/documents/engine.rb b/modules/documents/lib/open_project/documents/engine.rb
index 2c68809a421..05fb8956ffd 100644
--- a/modules/documents/lib/open_project/documents/engine.rb
+++ b/modules/documents/lib/open_project/documents/engine.rb
@@ -74,7 +74,5 @@ module OpenProject::Documents
# Add documents to allowed search params
additional_permitted_attributes search: %i(documents)
-
- patch_with_namespace :OpenProject, :TextFormatting, :Formats, :Markdown, :TextileConverter
end
end
diff --git a/modules/documents/lib/open_project/documents/patches/textile_converter_patch.rb b/modules/documents/lib/open_project/documents/patches/textile_converter_patch.rb
deleted file mode 100644
index 97bf04aa866..00000000000
--- a/modules/documents/lib/open_project/documents/patches/textile_converter_patch.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-#-- copyright
-# OpenProject Documents Plugin
-#
-# Former OpenProject Core functionality extracted into a plugin.
-#
-# Copyright (C) 2009-2014 the OpenProject Foundation (OPF)
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write 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 OpenProject::Documents::Patches
- module TextileConverterPatch
- def models_to_convert
- super.merge(::Document => [:description])
- end
- end
-end
-
-OpenProject::TextFormatting::Formats::Markdown::TextileConverter.prepend(
- OpenProject::Documents::Patches::TextileConverterPatch
-)
diff --git a/modules/github_integration/config/locales/js-en.yml b/modules/github_integration/config/locales/js-en.yml
index 47236883571..247437c2e54 100644
--- a/modules/github_integration/config/locales/js-en.yml
+++ b/modules/github_integration/config/locales/js-en.yml
@@ -48,8 +48,8 @@ en:
github_actions: Actions
pull_requests:
- message: "Pull request #%{pr_number} %{pr_link} for %{repository_link} has been %{pr_state} by %{github_user_link}."
- referenced_message: "%{github_user_link} referenced this work package in pull request #%{pr_number} %{pr_link} on %{repository_link}."
+ message: "Pull request #%{pr_number} %{pr_link} for %{repository_link} authored by %{github_user_link} has been %{pr_state}."
+ referenced_message: "Pull request #%{pr_number} %{pr_link} for %{repository_link} authored by %{github_user_link} referenced this work package."
states:
opened: 'opened'
closed: 'closed'
diff --git a/modules/github_integration/frontend/module/pull-request/pull-request-macro.component.ts b/modules/github_integration/frontend/module/pull-request/pull-request-macro.component.ts
index 712d83588fd..72c995616a3 100644
--- a/modules/github_integration/frontend/module/pull-request/pull-request-macro.component.ts
+++ b/modules/github_integration/frontend/module/pull-request/pull-request-macro.component.ts
@@ -36,7 +36,7 @@ import {
} from '@angular/core';
import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service';
import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs';
-import { IGithubPullRequest } from '../state/github-pull-request.model';
+import { IGithubPullRequest, IGithubUserResource } from '../state/github-pull-request.model';
import { GithubPullRequestResourceService } from '../state/github-pull-request.service';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@@ -85,7 +85,8 @@ export class PullRequestMacroComponent implements OnInit {
}
private buildText(pr:IGithubPullRequest):string {
- const githubUserLink = this.htmlLink(pr._embedded.githubUser.htmlUrl, pr._embedded.githubUser.login);
+ const actor = this.deriveActor(pr) as IGithubUserResource;
+ const actorLink = this.htmlLink(actor.htmlUrl, actor.login);
const repositoryLink = this.htmlLink(pr.repositoryHtmlUrl, pr.repository);
const prLink = this.htmlLink(pr.htmlUrl, pr.title);
@@ -100,11 +101,20 @@ export class PullRequestMacroComponent implements OnInit {
`js.github_integration.pull_requests.states.${this.pullRequestState}`,
{ defaultValue: this.pullRequestState || '(unknown state)' },
),
- github_user_link: githubUserLink,
+ github_user_link: actorLink,
},
);
}
+
+ private deriveActor(pr:IGithubPullRequest) {
+ if (this.pullRequestState === 'merged') {
+ return pr._embedded.mergedBy;
+ } else {
+ return pr._embedded.githubUser;
+ }
+ }
+
private htmlLink(href:string, title:string):string {
const link = document.createElement('a');
link.href = href;
diff --git a/modules/github_integration/spec/features/work_package_activity_spec.rb b/modules/github_integration/spec/features/work_package_activity_spec.rb
new file mode 100644
index 00000000000..27b3c98eeff
--- /dev/null
+++ b/modules/github_integration/spec/features/work_package_activity_spec.rb
@@ -0,0 +1,189 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Work Package Activity Tab',
+ 'Comments by Github',
+ :js,
+ :with_cuprite do
+ shared_let(:github_system_user) { create(:admin, firstname: 'Github', lastname: 'System User') }
+ shared_let(:admin) { create(:admin) }
+
+ shared_let(:project) { create(:project, enabled_module_names: Setting.default_projects_modules + %w[activity]) }
+ shared_let(:work_package) { create(:work_package, project:) }
+
+ shared_let(:pull_request_author) { create(:github_user, github_login: 'i_am_the_author') }
+ shared_let(:pull_request_merging_user) { create(:github_user, github_login: 'i_merged') }
+ shared_let(:pull_request) do
+ create(:github_pull_request,
+ github_user: pull_request_author)
+ end
+
+ def trigger_pull_request_action
+ OpenProject::GithubIntegration::NotificationHandler::PullRequest.new
+ .process(payload)
+
+ pull_request.reload
+ end
+
+ let(:payload) do
+ {
+ 'action' => action,
+ 'open_project_user_id' => github_system_user.id,
+ 'pull_request' => pull_request_hash,
+ 'sender' => sender_section_hash
+ }
+ end
+
+ let(:pull_request_hash) do
+ {
+ 'id' => pull_request.id,
+ 'number' => pull_request.number,
+ 'body' => "Mentioning OP##{work_package.id}",
+ 'title' => pull_request.title,
+ 'html_url' => pull_request.github_html_url,
+ 'updated_at' => Time.current.iso8601,
+ 'state' => state,
+ 'draft' => false,
+ 'merged' => merged,
+ 'merged_by' => merged_by_section_hash,
+ 'merged_at' => merged_at,
+ 'comments' => pull_request.comments_count + 1,
+ 'review_comments' => pull_request.review_comments_count,
+ 'additions' => pull_request.additions_count,
+ 'deletions' => pull_request.deletions_count,
+ 'changed_files' => pull_request.changed_files_count,
+ 'labels' => pull_request.labels,
+ 'user' => {
+ 'id' => pull_request_author.github_id,
+ 'login' => pull_request_author.github_login,
+ 'html_url' => pull_request_author.github_html_url,
+ 'avatar_url' => pull_request_author.github_avatar_url
+ },
+ 'base' => {
+ 'repo' => {
+ 'full_name' => 'author_user/repo',
+ 'html_url' => 'github.com/author_user/repo'
+ }
+ }
+ }
+ end
+
+ let(:sender_section_hash) do
+ {
+ 'login' => 'test_user',
+ 'html_url' => 'github.com/test_user'
+ }
+ end
+
+ let(:work_package_page) { Pages::SplitWorkPackage.new(work_package, project) }
+
+ context 'when the pull request is merged' do
+ let(:action) { 'closed' }
+
+ before do
+ trigger_pull_request_action
+ login_as admin
+ end
+
+ context "and I visit the work package's activity tab" do
+ let(:merged) { true }
+ let(:merged_by_section_hash) do
+ {
+ 'id' => pull_request_merging_user.github_id,
+ 'login' => pull_request_merging_user.github_login,
+ 'html_url' => pull_request_merging_user.github_html_url,
+ 'avatar_url' => pull_request_merging_user.github_avatar_url
+ }
+ end
+ let(:merged_at) { Time.current.iso8601 }
+ let(:state) { 'closed' }
+
+ before do
+ work_package_page.visit_tab! 'activity'
+ work_package_page.ensure_page_loaded
+ end
+
+ it 'renders a comment stating the Pull Request was merged by the merge actor' do
+ expected_merge_comment = <<~GITHUB_MERGE_COMMENT.squish
+ Merged#{I18n.t('js.github_integration.pull_requests.message',
+ pr_number: pull_request.number,
+ pr_link: pull_request.title,
+ repository_link: pull_request.repository,
+ pr_state: 'merged',
+ github_user_link: pull_request_merging_user.github_login)}
+ GITHUB_MERGE_COMMENT
+
+ expect(page).to have_selector('.user-comment > .message', text: expected_merge_comment)
+ end
+ end
+ end
+
+ context 'when the Work Package is referenced in a Pull Request' do
+ let(:action) { 'referenced' }
+
+ let(:merged) { false }
+ let(:merged_by_section_hash) { {} }
+ let(:merged_at) { nil }
+ let(:state) { 'open' }
+
+ before do
+ trigger_pull_request_action
+ login_as admin
+ end
+
+ context "and I visit the work package's activity tab" do
+ before do
+ work_package_page.visit_tab! 'activity'
+ work_package_page.ensure_page_loaded
+ end
+
+ it 'renders a comment stating the Work Package was referenced in the Pull Request' do
+ expected_referenced_comment = <<~GITHUB_REFERENCED_COMMENT.squish
+ Referenced#{I18n.t('js.github_integration.pull_requests.referenced_message',
+ pr_number: pull_request.number,
+ pr_link: pull_request.title,
+ repository_link: pull_request.repository,
+ pr_state: 'referenced',
+ github_user_link: pull_request_author.github_login)}
+ GITHUB_REFERENCED_COMMENT
+
+ expect(page).to have_selector('.user-comment > .message', text: expected_referenced_comment)
+ end
+ end
+ end
+
+ context 'when any non-edge-case action is performed on a pull request' do
+ let(:action) { 'ready_for_review' }
+
+ let(:merged) { false }
+ let(:merged_by_section_hash) { {} }
+ let(:merged_at) { nil }
+ let(:state) { 'open' }
+
+ before do
+ trigger_pull_request_action
+ login_as admin
+ end
+
+ context "and I visit the work package's activity tab" do
+ before do
+ work_package_page.visit_tab! 'activity'
+ work_package_page.ensure_page_loaded
+ end
+
+ it 'renders a comment stating that said action was performed on the Pull Request' do
+ expected_action_comment = <<~GITHUB_READY_FOR_REVIEW_COMMENT.squish
+ Marked Ready For Review#{I18n.t('js.github_integration.pull_requests.message',
+ pr_number: pull_request.number,
+ pr_link: pull_request.title,
+ repository_link: pull_request.repository,
+ pr_state: 'marked ready for review',
+ github_user_link: pull_request_author.github_login)}
+ GITHUB_READY_FOR_REVIEW_COMMENT
+
+ expect(page).to have_selector('.user-comment > .message', text: expected_action_comment)
+ end
+ end
+ end
+end
diff --git a/modules/ldap_groups/app/components/ldap_groups/synchronized_filters/row_component.rb b/modules/ldap_groups/app/components/ldap_groups/synchronized_filters/row_component.rb
index 5e1cde0fb7d..9d2346a543f 100644
--- a/modules/ldap_groups/app/components/ldap_groups/synchronized_filters/row_component.rb
+++ b/modules/ldap_groups/app/components/ldap_groups/synchronized_filters/row_component.rb
@@ -57,10 +57,12 @@ module LdapGroups
[
edit_link,
delete_link
- ]
+ ].compact
end
def edit_link
+ return if model.seeded_from_env?
+
link_to I18n.t(:button_edit),
{ controller: table.target_controller, ldap_filter_id: model.id, action: :edit },
class: 'icon icon-edit',
@@ -68,6 +70,8 @@ module LdapGroups
end
def delete_link
+ return if model.seeded_from_env?
+
link_to I18n.t(:button_delete),
{ controller: table.target_controller, ldap_filter_id: model.id, action: :destroy_info },
class: 'icon icon-delete',
diff --git a/modules/ldap_groups/app/models/ldap_groups/synchronized_filter.rb b/modules/ldap_groups/app/models/ldap_groups/synchronized_filter.rb
index b0e5564be45..43baa15f7ce 100644
--- a/modules/ldap_groups/app/models/ldap_groups/synchronized_filter.rb
+++ b/modules/ldap_groups/app/models/ldap_groups/synchronized_filter.rb
@@ -10,6 +10,7 @@ module LdapGroups
foreign_key: 'filter_id',
dependent: :destroy
+ validates_presence_of :name
validates_presence_of :filter_string
validates_presence_of :ldap_auth_source
validate :validate_filter_syntax
@@ -23,6 +24,13 @@ module LdapGroups
base_dn.presence || ldap_auth_source.base_dn
end
+ def seeded_from_env?
+ return false if ldap_auth_source.nil?
+
+ ldap_auth_source&.seeded_from_env? &&
+ Setting.seed_ldap.dig(ldap_auth_source.name, 'groupfilter', name)
+ end
+
private
def validate_filter_syntax
diff --git a/modules/ldap_groups/app/views/ldap_groups/synchronized_filters/show.html.erb b/modules/ldap_groups/app/views/ldap_groups/synchronized_filters/show.html.erb
index 21f6f7c89b5..e43b446b714 100644
--- a/modules/ldap_groups/app/views/ldap_groups/synchronized_filters/show.html.erb
+++ b/modules/ldap_groups/app/views/ldap_groups/synchronized_filters/show.html.erb
@@ -1,24 +1,36 @@
<% html_title(t(:label_administration), t('ldap_groups.synchronized_filters.plural'), h(@filter.name)) -%>
<%= error_messages_for @filter %>
+<% blocked = @filter.seeded_from_env? %>
<%= breadcrumb_toolbar(h(@filter.name)) do %>
-
- <%= link_to({ action: :edit },
- class: 'button') do %>
- <%= op_icon('button--icon icon-edit') %>
- <%= t(:button_edit) %>
- <% end %>
-
-
- <%= link_to({ action: :destroy_info },
- class: 'button -danger') do %>
- <%= op_icon('button--icon icon-delete') %>
- <%= t(:button_delete) %>
- <% end %>
-
+ <% unless blocked %>
+
+ <%= link_to({ action: :edit },
+ class: 'button') do %>
+ <%= op_icon('button--icon icon-edit') %>
+ <%= t(:button_edit) %>
+ <% end %>
+
+
+ <%= link_to({ action: :destroy_info },
+ class: 'button -danger') do %>
+ <%= op_icon('button--icon icon-delete') %>
+ <%= t(:button_delete) %>
+ <% end %>
+
+ <% end %>
<% end %>
+<% if blocked %>
+
+
+ <%= t(:label_seeded_from_env_warning) %>
+
+
+<% end %>
+
+
<%= render(AttributeGroups::AttributeGroupComponent.new) do |component|
component.with_attribute(key: @filter.class.human_attribute_name(:name),
@@ -29,10 +41,10 @@
value: @filter.used_base_dn)
component.with_attribute(key: @filter.class.human_attribute_name(:sync_users),
value: if @filter.sync_users
- checked_image @filter.sync_users
- else
- t(:general_text_no)
- end)
+ checked_image @filter.sync_users
+ else
+ t(:general_text_no)
+ end)
component.with_attribute(key: @filter.class.human_attribute_name(:filter_string),
value: @filter.filter_string)
component.with_attribute(key: t('ldap_groups.synchronized_groups.plural'),
@@ -41,14 +53,14 @@
-<%= toolbar(title: t('ldap_groups.synchronized_groups.plural')) do %>
-
- <%= link_to({ action: :synchronize },
- class: 'button') do %>
- <%= t('ldap_groups.label_synchronize') %>
- <% end %>
-
-<% end %>
+ <%= toolbar(title: t('ldap_groups.synchronized_groups.plural')) do %>
+
+ <%= link_to({ action: :synchronize },
+ class: 'button') do %>
+ <%= t('ldap_groups.label_synchronize') %>
+ <% end %>
+
+ <% end %>
<%= render ::LdapGroups::SynchronizedGroups::TableComponent.new(rows: @filter.groups, show_inline_create: false, deletable: false) %>
diff --git a/modules/ldap_groups/config/locales/crowdin/af.yml b/modules/ldap_groups/config/locales/crowdin/af.yml
index 716d99c1f26..e9a789b3674 100644
--- a/modules/ldap_groups/config/locales/crowdin/af.yml
+++ b/modules/ldap_groups/config/locales/crowdin/af.yml
@@ -3,11 +3,11 @@ af:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/ar.yml b/modules/ldap_groups/config/locales/crowdin/ar.yml
index 012c5c3ba03..84e90860ba2 100644
--- a/modules/ldap_groups/config/locales/crowdin/ar.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ar.yml
@@ -3,11 +3,11 @@ ar:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'اتصال LDAP'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'مزامنة المستخدمين'
ldap_groups/synchronized_filter:
filter_string: 'مرشّح LDAP'
- auth_source: 'اتصال LDAP'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'مزامنة المستخدمين'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/az.yml b/modules/ldap_groups/config/locales/crowdin/az.yml
index ebbd28b71a1..7dba2d0e033 100644
--- a/modules/ldap_groups/config/locales/crowdin/az.yml
+++ b/modules/ldap_groups/config/locales/crowdin/az.yml
@@ -3,11 +3,11 @@ az:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/be.yml b/modules/ldap_groups/config/locales/crowdin/be.yml
index 0f769cc30be..907b53a7acf 100644
--- a/modules/ldap_groups/config/locales/crowdin/be.yml
+++ b/modules/ldap_groups/config/locales/crowdin/be.yml
@@ -3,11 +3,11 @@ be:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/bg.yml b/modules/ldap_groups/config/locales/crowdin/bg.yml
index bb57fb040b4..182fd68d4f5 100644
--- a/modules/ldap_groups/config/locales/crowdin/bg.yml
+++ b/modules/ldap_groups/config/locales/crowdin/bg.yml
@@ -3,11 +3,11 @@ bg:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/ca.yml b/modules/ldap_groups/config/locales/crowdin/ca.yml
index 31d78fe6fe2..a32ccf1d9cc 100644
--- a/modules/ldap_groups/config/locales/crowdin/ca.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ca.yml
@@ -3,11 +3,11 @@ ca:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'Connexió LDAP'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Usuaris sincronitzats'
ldap_groups/synchronized_filter:
filter_string: 'Filtre LDAP'
- auth_source: 'Connexió LDAP'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Atribut de nom de grup"
sync_users: 'Usuaris sincronitzats'
base_dn: "Base de cerca DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/ckb-IR.yml b/modules/ldap_groups/config/locales/crowdin/ckb-IR.yml
index 86da64a7c21..2338e8fa9df 100644
--- a/modules/ldap_groups/config/locales/crowdin/ckb-IR.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ckb-IR.yml
@@ -3,11 +3,11 @@ ckb-IR:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/cs.yml b/modules/ldap_groups/config/locales/crowdin/cs.yml
index e01041239e3..7708cbfc38c 100644
--- a/modules/ldap_groups/config/locales/crowdin/cs.yml
+++ b/modules/ldap_groups/config/locales/crowdin/cs.yml
@@ -3,11 +3,11 @@ cs:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'Připojení LDAP'
+ ldap_auth_source: 'Připojení LDAP'
sync_users: 'Synchronizovat uživatele'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filtr'
- auth_source: 'Připojení LDAP'
+ ldap_auth_source: 'Připojení LDAP'
group_name_attribute: "Atribut názvu skupiny"
sync_users: 'Synchronizovat uživatele'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/da.yml b/modules/ldap_groups/config/locales/crowdin/da.yml
index 787900a906e..4de06cd6148 100644
--- a/modules/ldap_groups/config/locales/crowdin/da.yml
+++ b/modules/ldap_groups/config/locales/crowdin/da.yml
@@ -3,11 +3,11 @@ da:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Synkroniser brugere'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Synkroniser brugere'
base_dn: "Søg i base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/de.yml b/modules/ldap_groups/config/locales/crowdin/de.yml
index 0dac9d97352..c6222173bc1 100644
--- a/modules/ldap_groups/config/locales/crowdin/de.yml
+++ b/modules/ldap_groups/config/locales/crowdin/de.yml
@@ -3,11 +3,11 @@ de:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP-Verbindung'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Benutzer synchronisieren'
ldap_groups/synchronized_filter:
filter_string: 'LDAP-Filter'
- auth_source: 'LDAP-Verbindung'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Attribut für Gruppenname"
sync_users: 'Benutzer synchronisieren'
base_dn: "Suchbasis DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/el.yml b/modules/ldap_groups/config/locales/crowdin/el.yml
index b2793a4649d..2c098a81bf8 100644
--- a/modules/ldap_groups/config/locales/crowdin/el.yml
+++ b/modules/ldap_groups/config/locales/crowdin/el.yml
@@ -3,11 +3,11 @@ el:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'Σύνδεση LDAP'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Συγχρονισμός χρηστών'
ldap_groups/synchronized_filter:
filter_string: 'LDAP Φίλτρο'
- auth_source: 'Σύνδεση LDAP'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Χαρακτηριστικό ονόματος ομάδας"
sync_users: 'Συγχρονισμός χρηστών'
base_dn: "Αναζήτηση base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/eo.yml b/modules/ldap_groups/config/locales/crowdin/eo.yml
index c41eda25a0c..16a6f5528d2 100644
--- a/modules/ldap_groups/config/locales/crowdin/eo.yml
+++ b/modules/ldap_groups/config/locales/crowdin/eo.yml
@@ -3,11 +3,11 @@ eo:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/es.yml b/modules/ldap_groups/config/locales/crowdin/es.yml
index 5c2b1f6b2a3..fcf0bb892b3 100644
--- a/modules/ldap_groups/config/locales/crowdin/es.yml
+++ b/modules/ldap_groups/config/locales/crowdin/es.yml
@@ -3,11 +3,11 @@ es:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'Conexión LDAP'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sincronizar usuarios'
ldap_groups/synchronized_filter:
filter_string: 'Filtro LDAP'
- auth_source: 'Conexión LDAP'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Atributo de nombre de grupo"
sync_users: 'Sincronizar usuarios'
base_dn: "DN base de búsqueda"
diff --git a/modules/ldap_groups/config/locales/crowdin/et.yml b/modules/ldap_groups/config/locales/crowdin/et.yml
index a4bff7dc568..33a33ce9ef0 100644
--- a/modules/ldap_groups/config/locales/crowdin/et.yml
+++ b/modules/ldap_groups/config/locales/crowdin/et.yml
@@ -3,11 +3,11 @@ et:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/eu.yml b/modules/ldap_groups/config/locales/crowdin/eu.yml
index bcf7eb41500..c918ae55f1b 100644
--- a/modules/ldap_groups/config/locales/crowdin/eu.yml
+++ b/modules/ldap_groups/config/locales/crowdin/eu.yml
@@ -3,11 +3,11 @@ eu:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/fa.yml b/modules/ldap_groups/config/locales/crowdin/fa.yml
index f1b2231668c..6fc374fe231 100644
--- a/modules/ldap_groups/config/locales/crowdin/fa.yml
+++ b/modules/ldap_groups/config/locales/crowdin/fa.yml
@@ -3,11 +3,11 @@ fa:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'همسان سازی کاربران'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'همسان سازی کاربران'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/fi.yml b/modules/ldap_groups/config/locales/crowdin/fi.yml
index dc4fe1c07f7..11179a4e0fa 100644
--- a/modules/ldap_groups/config/locales/crowdin/fi.yml
+++ b/modules/ldap_groups/config/locales/crowdin/fi.yml
@@ -3,11 +3,11 @@ fi:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/fil.yml b/modules/ldap_groups/config/locales/crowdin/fil.yml
index 67d6d6e0818..d42f2162b71 100644
--- a/modules/ldap_groups/config/locales/crowdin/fil.yml
+++ b/modules/ldap_groups/config/locales/crowdin/fil.yml
@@ -3,11 +3,11 @@ fil:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/fr.yml b/modules/ldap_groups/config/locales/crowdin/fr.yml
index 377375d67ce..df62488c4c8 100644
--- a/modules/ldap_groups/config/locales/crowdin/fr.yml
+++ b/modules/ldap_groups/config/locales/crowdin/fr.yml
@@ -3,11 +3,11 @@ fr:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'Connexion LDAP'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Synchroniser les utilisateurs'
ldap_groups/synchronized_filter:
filter_string: 'Filtre LDAP'
- auth_source: 'Connexion LDAP'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Attribut nom de groupe"
sync_users: 'Synchroniser les utilisateurs'
base_dn: "Rechercher dans la base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/he.yml b/modules/ldap_groups/config/locales/crowdin/he.yml
index 5f6887d6b34..99982f1c621 100644
--- a/modules/ldap_groups/config/locales/crowdin/he.yml
+++ b/modules/ldap_groups/config/locales/crowdin/he.yml
@@ -3,11 +3,11 @@ he:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/hi.yml b/modules/ldap_groups/config/locales/crowdin/hi.yml
index ddc8a266240..657d4888a3c 100644
--- a/modules/ldap_groups/config/locales/crowdin/hi.yml
+++ b/modules/ldap_groups/config/locales/crowdin/hi.yml
@@ -3,11 +3,11 @@ hi:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/hr.yml b/modules/ldap_groups/config/locales/crowdin/hr.yml
index e06f78849b0..e98de2e6db6 100644
--- a/modules/ldap_groups/config/locales/crowdin/hr.yml
+++ b/modules/ldap_groups/config/locales/crowdin/hr.yml
@@ -3,11 +3,11 @@ hr:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/hu.yml b/modules/ldap_groups/config/locales/crowdin/hu.yml
index aea207c6c9f..dc61517e630 100644
--- a/modules/ldap_groups/config/locales/crowdin/hu.yml
+++ b/modules/ldap_groups/config/locales/crowdin/hu.yml
@@ -3,11 +3,11 @@ hu:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP csatlakozás'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Felhasználók szinkronizálása'
ldap_groups/synchronized_filter:
filter_string: 'LDAP szűrő'
- auth_source: 'LDAP csatlakozás'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Csoport név attribútum"
sync_users: 'Felhasználók szinkronizálása'
base_dn: "Keresési alap DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/id.yml b/modules/ldap_groups/config/locales/crowdin/id.yml
index d6f817e0c36..732901f0e2d 100644
--- a/modules/ldap_groups/config/locales/crowdin/id.yml
+++ b/modules/ldap_groups/config/locales/crowdin/id.yml
@@ -3,11 +3,11 @@ id:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'koneksi LDAP'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sinkronkan pengguna'
ldap_groups/synchronized_filter:
filter_string: 'penyaring LDAP'
- auth_source: 'koneksi LDAP'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Atribut nama grup"
sync_users: 'Sinkronkan pengguna'
base_dn: "Cari basis DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/it.yml b/modules/ldap_groups/config/locales/crowdin/it.yml
index bf1c2bb7d60..62dccdcd168 100644
--- a/modules/ldap_groups/config/locales/crowdin/it.yml
+++ b/modules/ldap_groups/config/locales/crowdin/it.yml
@@ -3,11 +3,11 @@ it:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'Connessione LDAP'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sincronizza utenti'
ldap_groups/synchronized_filter:
filter_string: 'Filtro LDAP'
- auth_source: 'Connessione LDAP'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Attributo nome gruppo"
sync_users: 'Sincronizza utenti'
base_dn: "Ricerca DN base"
diff --git a/modules/ldap_groups/config/locales/crowdin/ja.yml b/modules/ldap_groups/config/locales/crowdin/ja.yml
index 6e87f689c31..5423a7de934 100644
--- a/modules/ldap_groups/config/locales/crowdin/ja.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ja.yml
@@ -3,11 +3,11 @@ ja:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP 接続'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'ユーザーを同期'
ldap_groups/synchronized_filter:
filter_string: 'LDAPフィルタ'
- auth_source: 'LDAP 接続'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "グループ名属性"
sync_users: 'ユーザーを同期'
base_dn: "ベース DN を検索"
diff --git a/modules/ldap_groups/config/locales/crowdin/ka.yml b/modules/ldap_groups/config/locales/crowdin/ka.yml
index efab8240ffa..4bf7e238182 100644
--- a/modules/ldap_groups/config/locales/crowdin/ka.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ka.yml
@@ -3,11 +3,11 @@ ka:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/ko.yml b/modules/ldap_groups/config/locales/crowdin/ko.yml
index 4848bf49ee0..4691b07ca81 100644
--- a/modules/ldap_groups/config/locales/crowdin/ko.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ko.yml
@@ -3,11 +3,11 @@ ko:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP 연결'
+ ldap_auth_source: 'LDAP connection'
sync_users: '사용자 동기화'
ldap_groups/synchronized_filter:
filter_string: 'LDAP 필터'
- auth_source: 'LDAP 연결'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "그룹 이름 특성"
sync_users: '사용자 동기화'
base_dn: "기본 DN 검색"
diff --git a/modules/ldap_groups/config/locales/crowdin/lt.yml b/modules/ldap_groups/config/locales/crowdin/lt.yml
index 484fee5c556..c1b3776a1df 100644
--- a/modules/ldap_groups/config/locales/crowdin/lt.yml
+++ b/modules/ldap_groups/config/locales/crowdin/lt.yml
@@ -3,11 +3,11 @@ lt:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP jungtis'
+ ldap_auth_source: 'LDAP jungtis'
sync_users: 'Sinchronizuoti naudotojus'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filtras'
- auth_source: 'LDAP jungtis'
+ ldap_auth_source: 'LDAP jungtis'
group_name_attribute: "Grupės pavadinimo atributas"
sync_users: 'Sinchronizuoti naudotojus'
base_dn: "Paieškos pagrindo DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/lv.yml b/modules/ldap_groups/config/locales/crowdin/lv.yml
index 1c707ffe16b..4382d67d73e 100644
--- a/modules/ldap_groups/config/locales/crowdin/lv.yml
+++ b/modules/ldap_groups/config/locales/crowdin/lv.yml
@@ -3,11 +3,11 @@ lv:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/mn.yml b/modules/ldap_groups/config/locales/crowdin/mn.yml
index 1ec02387139..0ecea5f25a0 100644
--- a/modules/ldap_groups/config/locales/crowdin/mn.yml
+++ b/modules/ldap_groups/config/locales/crowdin/mn.yml
@@ -3,11 +3,11 @@ mn:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/ne.yml b/modules/ldap_groups/config/locales/crowdin/ne.yml
index 85f7ad7e6d4..a4146a9d926 100644
--- a/modules/ldap_groups/config/locales/crowdin/ne.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ne.yml
@@ -3,11 +3,11 @@ ne:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/nl.yml b/modules/ldap_groups/config/locales/crowdin/nl.yml
index 6204607289b..aa2c8e4310b 100644
--- a/modules/ldap_groups/config/locales/crowdin/nl.yml
+++ b/modules/ldap_groups/config/locales/crowdin/nl.yml
@@ -3,11 +3,11 @@ nl:
attributes:
ldap_groups/synchronized_group:
dn: 'AFW'
- auth_source: 'LDAP verbinding'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Gebruikers synchroniseren'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP verbinding'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Attribuut groepsnaam"
sync_users: 'Gebruikers synchroniseren'
base_dn: "Zoek basis DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/no.yml b/modules/ldap_groups/config/locales/crowdin/no.yml
index 6376b3cf5d5..1da1bd4e68e 100644
--- a/modules/ldap_groups/config/locales/crowdin/no.yml
+++ b/modules/ldap_groups/config/locales/crowdin/no.yml
@@ -3,11 +3,11 @@
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/pl.yml b/modules/ldap_groups/config/locales/crowdin/pl.yml
index 7e2d965c96b..84717049796 100644
--- a/modules/ldap_groups/config/locales/crowdin/pl.yml
+++ b/modules/ldap_groups/config/locales/crowdin/pl.yml
@@ -3,11 +3,11 @@ pl:
attributes:
ldap_groups/synchronized_group:
dn: 'Nazwa wyróżniająca'
- auth_source: 'Połączenie LDAP'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Synchronizuj użytkowników'
ldap_groups/synchronized_filter:
filter_string: 'Filtr LDAP'
- auth_source: 'Połączenie LDAP'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Atrybut nazwy grupy"
sync_users: 'Synchronizuj użytkowników'
base_dn: "Wyszukaj bazową nazwę wyróżniającą"
diff --git a/modules/ldap_groups/config/locales/crowdin/pt.yml b/modules/ldap_groups/config/locales/crowdin/pt.yml
index 6d2b3d7bebe..9e32a30ad3e 100644
--- a/modules/ldap_groups/config/locales/crowdin/pt.yml
+++ b/modules/ldap_groups/config/locales/crowdin/pt.yml
@@ -3,11 +3,11 @@ pt:
attributes:
ldap_groups/synchronized_group:
dn: 'ND'
- auth_source: 'Conexão LDAP'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sincronizar usuários'
ldap_groups/synchronized_filter:
filter_string: 'Filtro LDAP'
- auth_source: 'Conexão LDAP'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Atributo de nome de grupo"
sync_users: 'Sincronizar usuários'
base_dn: "Procurar DN base"
diff --git a/modules/ldap_groups/config/locales/crowdin/ro.yml b/modules/ldap_groups/config/locales/crowdin/ro.yml
index 36eec601971..b27191c5f04 100644
--- a/modules/ldap_groups/config/locales/crowdin/ro.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ro.yml
@@ -3,11 +3,11 @@ ro:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'Conexiune LDAP'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sincronizați utilizatorii'
ldap_groups/synchronized_filter:
filter_string: 'Filtru LDAP'
- auth_source: 'Conexiune LDAP'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Atributul nume de grup"
sync_users: 'Sincronizați utilizatorii'
base_dn: "Baza de căutare DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/ru.yml b/modules/ldap_groups/config/locales/crowdin/ru.yml
index e6f6ce46b42..078f6f91934 100644
--- a/modules/ldap_groups/config/locales/crowdin/ru.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ru.yml
@@ -3,11 +3,11 @@ ru:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP-подключение'
+ ldap_auth_source: 'Подключение к LDAP'
sync_users: 'Синхронизация пользователей'
ldap_groups/synchronized_filter:
filter_string: 'Фильтр LDAP'
- auth_source: 'LDAP-подключение'
+ ldap_auth_source: 'Подключение к LDAP'
group_name_attribute: "Атрибут имени группы"
sync_users: 'Синхронизация пользователей'
base_dn: "Поиск базы DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/rw.yml b/modules/ldap_groups/config/locales/crowdin/rw.yml
index 0eac7faf613..440707b6096 100644
--- a/modules/ldap_groups/config/locales/crowdin/rw.yml
+++ b/modules/ldap_groups/config/locales/crowdin/rw.yml
@@ -3,11 +3,11 @@ rw:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/si.yml b/modules/ldap_groups/config/locales/crowdin/si.yml
index f9e83f48965..26004200f05 100644
--- a/modules/ldap_groups/config/locales/crowdin/si.yml
+++ b/modules/ldap_groups/config/locales/crowdin/si.yml
@@ -3,11 +3,11 @@ si:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/sk.yml b/modules/ldap_groups/config/locales/crowdin/sk.yml
index b38062dd0ae..e0d4a49f148 100644
--- a/modules/ldap_groups/config/locales/crowdin/sk.yml
+++ b/modules/ldap_groups/config/locales/crowdin/sk.yml
@@ -3,11 +3,11 @@ sk:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'Filter LDAP'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/sl.yml b/modules/ldap_groups/config/locales/crowdin/sl.yml
index 241b0d9e2ae..b88c14fb9db 100644
--- a/modules/ldap_groups/config/locales/crowdin/sl.yml
+++ b/modules/ldap_groups/config/locales/crowdin/sl.yml
@@ -3,11 +3,11 @@ sl:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP povezava'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sinhroniziraj uporabnike'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP povezava'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Atribut imena skupine\n"
sync_users: 'Sinhroniziraj uporabnike'
base_dn: "Išči po bazi DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/sr.yml b/modules/ldap_groups/config/locales/crowdin/sr.yml
index fe0cb4a9360..6681a8942e0 100644
--- a/modules/ldap_groups/config/locales/crowdin/sr.yml
+++ b/modules/ldap_groups/config/locales/crowdin/sr.yml
@@ -3,11 +3,11 @@ sr:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/sv.yml b/modules/ldap_groups/config/locales/crowdin/sv.yml
index 71b5a009477..6e7ead354c0 100644
--- a/modules/ldap_groups/config/locales/crowdin/sv.yml
+++ b/modules/ldap_groups/config/locales/crowdin/sv.yml
@@ -3,11 +3,11 @@ sv:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP-anslutning'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Synkronisera användare'
ldap_groups/synchronized_filter:
filter_string: 'LDAP-filter'
- auth_source: 'LDAP-anslutning'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Attribut för gruppnamn"
sync_users: 'Synkronisera användare'
base_dn: "Sök bas DN:"
diff --git a/modules/ldap_groups/config/locales/crowdin/th.yml b/modules/ldap_groups/config/locales/crowdin/th.yml
index 39616f1c714..5311c386a90 100644
--- a/modules/ldap_groups/config/locales/crowdin/th.yml
+++ b/modules/ldap_groups/config/locales/crowdin/th.yml
@@ -3,11 +3,11 @@ th:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/tr.yml b/modules/ldap_groups/config/locales/crowdin/tr.yml
index 72a947f0252..87e8023cc94 100644
--- a/modules/ldap_groups/config/locales/crowdin/tr.yml
+++ b/modules/ldap_groups/config/locales/crowdin/tr.yml
@@ -3,11 +3,11 @@ tr:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP bağlantısı'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Kullanıcıları senkronize et'
ldap_groups/synchronized_filter:
filter_string: 'LDAP süzgeçi'
- auth_source: 'LDAP bağlantısı'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Grup adı özelliği"
sync_users: 'Kullanıcıları senkronize et'
base_dn: "Arama tabanı DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/uk.yml b/modules/ldap_groups/config/locales/crowdin/uk.yml
index 1d244db32c0..d7e71ad0ef4 100644
--- a/modules/ldap_groups/config/locales/crowdin/uk.yml
+++ b/modules/ldap_groups/config/locales/crowdin/uk.yml
@@ -3,11 +3,11 @@ uk:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP-підключення'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Синхронізація користувачів'
ldap_groups/synchronized_filter:
filter_string: 'Фільтр LDAP'
- auth_source: 'LDAP-підключення'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Атрибут назви групи"
sync_users: 'Синхронізація користувачів'
base_dn: "База пошуку унікальних імен"
diff --git a/modules/ldap_groups/config/locales/crowdin/vi.yml b/modules/ldap_groups/config/locales/crowdin/vi.yml
index 337f5194836..49c834fe455 100644
--- a/modules/ldap_groups/config/locales/crowdin/vi.yml
+++ b/modules/ldap_groups/config/locales/crowdin/vi.yml
@@ -3,11 +3,11 @@ vi:
attributes:
ldap_groups/synchronized_group:
dn: 'DN'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
sync_users: 'Sync users'
ldap_groups/synchronized_filter:
filter_string: 'LDAP filter'
- auth_source: 'LDAP connection'
+ ldap_auth_source: 'LDAP connection'
group_name_attribute: "Group name attribute"
sync_users: 'Sync users'
base_dn: "Search base DN"
diff --git a/modules/ldap_groups/config/locales/crowdin/zh-TW.yml b/modules/ldap_groups/config/locales/crowdin/zh-TW.yml
index 6147e9f7b34..c94b111f4e0 100644
--- a/modules/ldap_groups/config/locales/crowdin/zh-TW.yml
+++ b/modules/ldap_groups/config/locales/crowdin/zh-TW.yml
@@ -3,11 +3,11 @@ zh-TW:
attributes:
ldap_groups/synchronized_group:
dn: '獨特名'
- auth_source: 'LDAP 連線'
+ ldap_auth_source: 'LDAP 連線'
sync_users: '同步使用者'
ldap_groups/synchronized_filter:
filter_string: '簡約登入目錄制約(LDAP)篩選'
- auth_source: 'LDAP 連線'
+ ldap_auth_source: 'LDAP 連線'
group_name_attribute: "群組名字屬性"
sync_users: '同步使用者'
base_dn: "搜尋基礎 DN"
@@ -49,7 +49,7 @@ zh-TW:
title: '移除同步的群組 %{name}'
confirmation: "如繼續,將移除同步的群組 %{name} 和所有透過該群組同步的全部 %{users_count} 個用戶。"
info: "Note: The OpenProject group itself and members added outside this LDAP synchronization will not be removed."
- verification: "Enter the group's name %{name} to verify the deletion."
+ verification: "輸入專案的名稱 %{name} 來確認刪除"
help_text_html: |
This module allows you to set up a synchronization between LDAP and OpenProject groups.
It depends on LDAP groups need to use the groupOfNames / memberOf attribute set to be working with OpenProject.
diff --git a/modules/ldap_groups/lib/open_project/ldap_groups/patches/ldap_auth_source_patch.rb b/modules/ldap_groups/lib/open_project/ldap_groups/patches/ldap_auth_source_patch.rb
index c8effc02dd5..b5137b1e17a 100644
--- a/modules/ldap_groups/lib/open_project/ldap_groups/patches/ldap_auth_source_patch.rb
+++ b/modules/ldap_groups/lib/open_project/ldap_groups/patches/ldap_auth_source_patch.rb
@@ -6,6 +6,10 @@ module OpenProject::LdapGroups
has_many :ldap_groups_synchronized_groups,
class_name: '::LdapGroups::SynchronizedGroup',
dependent: :destroy
+
+ has_many :ldap_groups_synchronized_filters,
+ class_name: '::LdapGroups::SynchronizedFilter',
+ dependent: :destroy
end
end
end
diff --git a/modules/ldap_groups/spec/features/filter_administration_spec.rb b/modules/ldap_groups/spec/features/filter_administration_spec.rb
new file mode 100644
index 00000000000..1f5beada2e6
--- /dev/null
+++ b/modules/ldap_groups/spec/features/filter_administration_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../spec_helper'
+
+RSpec.describe 'LDAP group filter administration spec', js: true do
+ let(:admin) { create(:admin) }
+
+ before do
+ login_as admin
+ end
+
+ context 'with EE', with_ee: %i[ldap_groups] do
+ context 'when providing seed variables',
+ :settings_reset,
+ with_env: {
+ OPENPROJECT_SEED_LDAP_FOO_HOST: "localhost",
+ OPENPROJECT_SEED_LDAP_FOO_PORT: "12389",
+ OPENPROJECT_SEED_LDAP_FOO_SECURITY: "plain_ldap",
+ OPENPROJECT_SEED_LDAP_FOO_TLS__VERIFY: "false",
+ OPENPROJECT_SEED_LDAP_FOO_BINDUSER: "uid=admin,ou=system",
+ OPENPROJECT_SEED_LDAP_FOO_BINDPASSWORD: "secret",
+ OPENPROJECT_SEED_LDAP_FOO_BASEDN: "dc=example,dc=com",
+ OPENPROJECT_SEED_LDAP_FOO_FILTER: "(uid=*)",
+ OPENPROJECT_SEED_LDAP_FOO_SYNC__USERS: "true",
+ OPENPROJECT_SEED_LDAP_FOO_LOGIN__MAPPING: "uid",
+ OPENPROJECT_SEED_LDAP_FOO_FIRSTNAME__MAPPING: "givenName",
+ OPENPROJECT_SEED_LDAP_FOO_LASTNAME__MAPPING: "sn",
+ OPENPROJECT_SEED_LDAP_FOO_MAIL__MAPPING: "mail",
+ OPENPROJECT_SEED_LDAP_FOO_ADMIN__MAPPING: "",
+ OPENPROJECT_SEED_LDAP_FOO_GROUPFILTER_BAR_BASE: "ou=groups,dc=example,dc=com",
+ OPENPROJECT_SEED_LDAP_FOO_GROUPFILTER_BAR_FILTER: "(cn=*)",
+ OPENPROJECT_SEED_LDAP_FOO_GROUPFILTER_BAR_SYNC__USERS: "true",
+ OPENPROJECT_SEED_LDAP_FOO_GROUPFILTER_BAR_GROUP__ATTRIBUTE: "dn"
+ } do
+ it 'blocks editing of the filter' do
+ reset(:seed_ldap)
+ allow(LdapGroups::SynchronizationJob).to receive(:perform_now)
+ EnvData::LdapSeeder.new({}).seed_data!
+
+ visit ldap_groups_synchronized_groups_path
+ expect(page).to have_text 'bar'
+ page.find('td.name a', text: 'bar').click
+
+ expect(page).to have_text I18n.t(:label_seeded_from_env_warning)
+ expect(page).not_to have_link 'Edit'
+ expect(page).not_to have_link 'Delete'
+ end
+ end
+ end
+end
diff --git a/modules/meeting/app/controllers/meetings_controller.rb b/modules/meeting/app/controllers/meetings_controller.rb
index 57c6481959c..754dc5ce55e 100644
--- a/modules/meeting/app/controllers/meetings_controller.rb
+++ b/modules/meeting/app/controllers/meetings_controller.rb
@@ -32,8 +32,8 @@ class MeetingsController < ApplicationController
before_action :build_meeting, only: %i[new create]
before_action :find_meeting, except: %i[index new create]
before_action :convert_params, only: %i[create update]
- before_action :authorize, except: %i[index new]
- before_action :authorize_global, only: %i[index new]
+ before_action :authorize, except: %i[index new create]
+ before_action :authorize_global, only: %i[index new create]
helper :watchers
helper :meeting_contents
diff --git a/modules/meeting/app/helpers/meetings_helper.rb b/modules/meeting/app/helpers/meetings_helper.rb
index acd2a3b5e1a..b1e123d093a 100644
--- a/modules/meeting/app/helpers/meetings_helper.rb
+++ b/modules/meeting/app/helpers/meetings_helper.rb
@@ -171,7 +171,15 @@ module MeetingsHelper
content_tag('div', "#{header}#{details}".html_safe, id: "change-#{journal.id}", class: 'journal')
end
- def global_create_context?
+ def global_meeting_create_context?
+ global_new_meeting_action? || global_create_meeting_action?
+ end
+
+ def global_new_meeting_action?
request.path == new_meeting_path
end
+
+ def global_create_meeting_action?
+ request.path == meetings_path && @project.nil?
+ end
end
diff --git a/modules/meeting/app/views/meetings/_form.html.erb b/modules/meeting/app/views/meetings/_form.html.erb
index 11738e292f3..14902e91bca 100644
--- a/modules/meeting/app/views/meetings/_form.html.erb
+++ b/modules/meeting/app/views/meetings/_form.html.erb
@@ -35,7 +35,7 @@ See COPYRIGHT and LICENSE files for more details.
<%= f.text_field :title, :required => true, :size => 60, container_class: '-wide' %>