- <%= angular_component_tag "opce-enterprise-banner",
- inputs: {
- collapsible: false,
- opReferrer: "boards",
- linkMessage: t("boards.upsale.upgrade"),
- textMessage: t("boards.upsale.teaser_text"),
- moreInfoLink: OpenProject::Static::Links.links[:enterprise_docs][:boards][:href]
- } %>
-
- <% end %>
+ <%=
+ render(EnterpriseEdition::BannerComponent.new(:board_view))
+ %>
<% board_types.each_with_index do |board_type, tile_number| %>
diff --git a/modules/boards/config/locales/crowdin/af.yml b/modules/boards/config/locales/crowdin/af.yml
index 009d5cc62b5..a4ac4dc63f3 100644
--- a/modules/boards/config/locales/crowdin/af.yml
+++ b/modules/boards/config/locales/crowdin/af.yml
@@ -6,6 +6,10 @@ af:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ af:
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 21ed45cdd90..0f629c46bef 100644
--- a/modules/boards/config/locales/crowdin/ar.yml
+++ b/modules/boards/config/locales/crowdin/ar.yml
@@ -6,6 +6,10 @@ ar:
permission_show_board_views: "شاهد لوحات المهمات"
permission_manage_board_views: "إدارة اللوحات"
project_module_board_view: "اللوحات"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "لوحة"
label_boards: "لوحات"
@@ -34,6 +38,3 @@ ar:
لوحة بأعمدة تلقائية للمشاريع الفرعية. يؤدي سحب حزم العمل إلى قوائم أخرى إلى تحديث المشروع (أو المشروع الفرعي) وفقًا لذلك.
subtasks: >
لوحة بأعمدة تلقائية للعناصر الفرعية. يؤدي سحب حزم العمل إلى قوائم أخرى إلى تحديث العنصر الرئيسي وفقًا لذلك.
- upsale:
- teaser_text: 'هل ترغب في أتمتة سير العمل الخاص بك باستخدام اللوحات؟ اللوحات المتقدمة هي إضافة خاصة بالمؤسسات. يرجى الترقية إلى خطة مدفوعة.'
- upgrade: 'الترقية الآن'
diff --git a/modules/boards/config/locales/crowdin/az.yml b/modules/boards/config/locales/crowdin/az.yml
index 4ab2095e152..696da9b7ca2 100644
--- a/modules/boards/config/locales/crowdin/az.yml
+++ b/modules/boards/config/locales/crowdin/az.yml
@@ -6,6 +6,10 @@ az:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ az:
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 695f85a1801..a03fe1f1a4b 100644
--- a/modules/boards/config/locales/crowdin/be.yml
+++ b/modules/boards/config/locales/crowdin/be.yml
@@ -6,6 +6,10 @@ be:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Дошка"
label_boards: "Boards"
@@ -34,6 +38,3 @@ be:
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 6f964111752..f2a0e9fd45c 100644
--- a/modules/boards/config/locales/crowdin/bg.yml
+++ b/modules/boards/config/locales/crowdin/bg.yml
@@ -6,6 +6,10 @@ bg:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Табла"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Табла"
@@ -34,6 +38,3 @@ bg:
Табло с автоматизирани колони за подпроекти. Плъзгането на работни пакети към други списъци актуализира (под) проекта съответно.
subtasks: >
Табло с автоматизирани колони за поделементи. Плъзгането на работни пакети към други списъци актуализира родителя съответно.
- upsale:
- teaser_text: 'Искате ли да автоматизирате работните си процеси с помощта на Табла? Усъвършенстваните табла са добавка само за Enterprise. Моля, преминете към платен план.'
- upgrade: 'Актуализирайте сега'
diff --git a/modules/boards/config/locales/crowdin/ca.yml b/modules/boards/config/locales/crowdin/ca.yml
index 9f7aff04b73..805f2c2588b 100644
--- a/modules/boards/config/locales/crowdin/ca.yml
+++ b/modules/boards/config/locales/crowdin/ca.yml
@@ -6,6 +6,10 @@ ca:
permission_show_board_views: "Veure els taulells"
permission_manage_board_views: "Administra els taulells"
project_module_board_view: "Taulells"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Taulell"
label_boards: "Taulells"
@@ -34,6 +38,3 @@ ca:
Taulell amb columnes automàtiques per subprojectes. Movent els paquets de treball a altres llistes actualitzarà el (sub-)projecte.
subtasks: >
Taulell amb columnes automàtiques per subelements. Movent els paquets de treball a altres llistes actualitzarà el paquet pare.
- upsale:
- teaser_text: 'T''agradaria automatitzar els teus processos de treball amb taulells? Els taulells avançats són una extensió de l''edició Enterprise. Si us plau, actualitza a un pla de pagament.'
- upgrade: 'Actualitza ara'
diff --git a/modules/boards/config/locales/crowdin/ckb-IR.yml b/modules/boards/config/locales/crowdin/ckb-IR.yml
index 8b3c3d772ec..1ee5e746778 100644
--- a/modules/boards/config/locales/crowdin/ckb-IR.yml
+++ b/modules/boards/config/locales/crowdin/ckb-IR.yml
@@ -6,6 +6,10 @@ ckb-IR:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ ckb-IR:
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 279bc2a047f..3cb08346760 100644
--- a/modules/boards/config/locales/crowdin/cs.yml
+++ b/modules/boards/config/locales/crowdin/cs.yml
@@ -6,6 +6,10 @@ cs:
permission_show_board_views: "Zobrazit tabule"
permission_manage_board_views: "Správa tabulí"
project_module_board_view: "Tabule"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Tabule"
label_boards: "Tabule"
@@ -34,6 +38,3 @@ cs:
Tabule s automatickými sloupce pro podprojekty. Přesunutí pracovních balíčků do jiných seznamů odpovídajícím způsobem aktualizuje podprojekt.
subtasks: >
Tabule s automatickými sloupce pro podprojekty. Přesunutí pracovních balíčků do jiných seznamů odpovídajícím způsobem
- upsale:
- teaser_text: 'Chcete automatizovat své workflows s Tabulemi? Pokročilé Tabule jsou doplňkem Enterprise. Proveďte upgrade na placený plán.'
- upgrade: 'Upgradovat nyní'
diff --git a/modules/boards/config/locales/crowdin/da.yml b/modules/boards/config/locales/crowdin/da.yml
index b3a5c23227a..4dbda67fe93 100644
--- a/modules/boards/config/locales/crowdin/da.yml
+++ b/modules/boards/config/locales/crowdin/da.yml
@@ -6,6 +6,10 @@ da:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ da:
Board med automatiserede kolonner til underprojekter. Træk arbejdspakker til andre lister opdaterer (under)projektet i overensstemmelse hermed.
subtasks: >
Board med automatiserede kolonner til underelementer. Træk arbejdspakker til andre lister, opdaterer den overordnede i overensstemmelse hermed.
- 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 2ad3e10bcd5..b5d5860f95c 100644
--- a/modules/boards/config/locales/crowdin/de.yml
+++ b/modules/boards/config/locales/crowdin/de.yml
@@ -6,6 +6,10 @@ de:
permission_show_board_views: "Boards anzeigen"
permission_manage_board_views: "Boards verwalten"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ de:
Board mit dynamischen Spalten für Unterprojekte. Durch Verschieben von Arbeitspaketen kann das (Unter-)Projekt geändert werden.
subtasks: >
Board mit dynamischen Spalten für Unterelemente. Verschieben von Arbeitspaketen auf andere Listen aktualisiert das Elternelement.
- upsale:
- teaser_text: 'Möchten Sie Ihre Workflows mit Boards automatisieren? Erweiterte Boards sind ein Enterprise Add-on. Bitte upgraden Sie auf ein kostenpflichtiges Abonnement.'
- upgrade: 'Jetzt Upgrade durchführen'
diff --git a/modules/boards/config/locales/crowdin/el.yml b/modules/boards/config/locales/crowdin/el.yml
index 8cf608a5f64..4faaaa56da0 100644
--- a/modules/boards/config/locales/crowdin/el.yml
+++ b/modules/boards/config/locales/crowdin/el.yml
@@ -6,6 +6,10 @@ el:
permission_show_board_views: "Προβολή πινάκων"
permission_manage_board_views: "Διαχείριση πινάκων"
project_module_board_view: "Πίνακες"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Πίνακας"
label_boards: "Πίνακες"
@@ -34,6 +38,3 @@ el:
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: 'Αναβάθμιση τώρα'
diff --git a/modules/boards/config/locales/crowdin/eo.yml b/modules/boards/config/locales/crowdin/eo.yml
index 4096a8c5399..2597f1265fe 100644
--- a/modules/boards/config/locales/crowdin/eo.yml
+++ b/modules/boards/config/locales/crowdin/eo.yml
@@ -6,6 +6,10 @@ eo:
permission_show_board_views: "Vidi panelojn"
permission_manage_board_views: "Administri panelojn"
project_module_board_view: "Tabuloj"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Tabulo"
label_boards: "Tabuloj"
@@ -34,6 +38,3 @@ eo:
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: 'Ppromocii nun'
diff --git a/modules/boards/config/locales/crowdin/es.yml b/modules/boards/config/locales/crowdin/es.yml
index 142154d48e2..c2bae03dd33 100644
--- a/modules/boards/config/locales/crowdin/es.yml
+++ b/modules/boards/config/locales/crowdin/es.yml
@@ -6,6 +6,10 @@ es:
permission_show_board_views: "Ver tableros"
permission_manage_board_views: "Administrar tableros"
project_module_board_view: "Tableros"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Tablero"
label_boards: "Tableros"
@@ -34,6 +38,3 @@ es:
Tablero con columnas automáticas para subproyectos. Al arrastrar paquetes de trabajo a otras listas, se actualizan los (sub)proyectos en consecuencia.
subtasks: >
Tablero con columnas automáticas para subelementos. Al arrastrar paquetes de trabajo a otras listas, se actualizan los elementos principales en consecuencia.
- upsale:
- teaser_text: '¿Quieres automatizar tus flujos de trabajo con tableros? Los tableros avanzados son una extensión de Enterprise. Mejores su plan a uno de pago.'
- upgrade: 'Mejorar ahora'
diff --git a/modules/boards/config/locales/crowdin/et.yml b/modules/boards/config/locales/crowdin/et.yml
index 553075b7d8d..9f528312588 100644
--- a/modules/boards/config/locales/crowdin/et.yml
+++ b/modules/boards/config/locales/crowdin/et.yml
@@ -6,6 +6,10 @@ et:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ et:
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 f8e083e0f4f..8c5e526d9cb 100644
--- a/modules/boards/config/locales/crowdin/eu.yml
+++ b/modules/boards/config/locales/crowdin/eu.yml
@@ -6,6 +6,10 @@ eu:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ eu:
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 c5fd4aed7f4..9520720336a 100644
--- a/modules/boards/config/locales/crowdin/fa.yml
+++ b/modules/boards/config/locales/crowdin/fa.yml
@@ -6,6 +6,10 @@ fa:
permission_show_board_views: "مشاهده تابلو ها"
permission_manage_board_views: "مدیریت تابلو ها"
project_module_board_view: "انجمن ها"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "تابلو"
label_boards: "تابلو"
@@ -34,6 +38,3 @@ fa:
تابلویی با ستون های خودکار برای پروژه های فرعی. کشیدن بسته های کاری به لیست های دیگر، پروژه(فرعی) را بر این اساس به روز می کند.
subtasks: >
تابلویی با ستون های خودکار برای عناصر فرعی. کشیدن بستههای کاری به فهرستهای دیگر، والد را متناسب با آن بهروزرسانی میکند.
- upsale:
- teaser_text: 'آیا می خواهید گردش کار خود را با تابلوها خودکار کنید؟ بردهای پیشرفته یک افزونه Enterprise هستند. لطفاً به یک طرح پولی ارتقا دهید.'
- upgrade: 'هم اکنون ارتقا داده شود'
diff --git a/modules/boards/config/locales/crowdin/fi.yml b/modules/boards/config/locales/crowdin/fi.yml
index d39439f92f7..a30b650667b 100644
--- a/modules/boards/config/locales/crowdin/fi.yml
+++ b/modules/boards/config/locales/crowdin/fi.yml
@@ -6,6 +6,10 @@ fi:
permission_show_board_views: "Näytä taulut"
permission_manage_board_views: "Hallitse tauluja"
project_module_board_view: "Taulut"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Taulu"
label_boards: "Taulut"
@@ -34,6 +38,3 @@ fi:
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: 'Päivitä nyt'
diff --git a/modules/boards/config/locales/crowdin/fil.yml b/modules/boards/config/locales/crowdin/fil.yml
index 1055e01d840..c1ce831bb45 100644
--- a/modules/boards/config/locales/crowdin/fil.yml
+++ b/modules/boards/config/locales/crowdin/fil.yml
@@ -6,6 +6,10 @@ fil:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ fil:
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 fb0850c0dce..29b0e087aa9 100644
--- a/modules/boards/config/locales/crowdin/fr.yml
+++ b/modules/boards/config/locales/crowdin/fr.yml
@@ -6,6 +6,10 @@ fr:
permission_show_board_views: "Afficher les tableaux"
permission_manage_board_views: "Gérer les tableaux"
project_module_board_view: "Tableaux"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Tableau"
label_boards: "Tableaux"
@@ -34,6 +38,3 @@ fr:
Tableau avec colonnes automatiques pour les sous-projets. Le déplacement des lots de travaux vers d'autres listes met à jour le (sous-)projet en conséquence.
subtasks: >
Tableau avec colonnes automatiques pour les sous-éléments. Le déplacement des lots de travaux vers d'autres listes met à jour le parent en conséquence.
- upsale:
- teaser_text: 'Voulez-vous automatiser vos flux de travail avec des tableaux ? Les tableaux avancés sont un module de la version Enterprise. Veuillez passer à un abonnement payant.'
- upgrade: 'Mettre à niveau maintenant'
diff --git a/modules/boards/config/locales/crowdin/he.yml b/modules/boards/config/locales/crowdin/he.yml
index 561ad19cef0..cd3da7518f4 100644
--- a/modules/boards/config/locales/crowdin/he.yml
+++ b/modules/boards/config/locales/crowdin/he.yml
@@ -6,6 +6,10 @@ he:
permission_show_board_views: "תצוגת לוחות"
permission_manage_board_views: "לוח ניהול"
project_module_board_view: "לוחות"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "לוח"
label_boards: "לוחות"
@@ -34,6 +38,3 @@ he:
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: 'שדרג עכשיו'
diff --git a/modules/boards/config/locales/crowdin/hi.yml b/modules/boards/config/locales/crowdin/hi.yml
index eed7b83e1b3..6a70e090727 100644
--- a/modules/boards/config/locales/crowdin/hi.yml
+++ b/modules/boards/config/locales/crowdin/hi.yml
@@ -6,6 +6,10 @@ hi:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ hi:
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 07edebe60aa..04f030f93b6 100644
--- a/modules/boards/config/locales/crowdin/hr.yml
+++ b/modules/boards/config/locales/crowdin/hr.yml
@@ -6,6 +6,10 @@ hr:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ hr:
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: 'Ažuriraj sada'
diff --git a/modules/boards/config/locales/crowdin/hu.yml b/modules/boards/config/locales/crowdin/hu.yml
index be41e173afd..cdb30974d27 100644
--- a/modules/boards/config/locales/crowdin/hu.yml
+++ b/modules/boards/config/locales/crowdin/hu.yml
@@ -6,6 +6,10 @@ hu:
permission_show_board_views: "Táblák megtekintése"
permission_manage_board_views: "Táblák kezelése"
project_module_board_view: "Táblák"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Tábla"
label_boards: "Táblák"
diff --git a/modules/boards/config/locales/crowdin/id.yml b/modules/boards/config/locales/crowdin/id.yml
index cb81c95a827..4a58ec8b0e6 100644
--- a/modules/boards/config/locales/crowdin/id.yml
+++ b/modules/boards/config/locales/crowdin/id.yml
@@ -6,6 +6,10 @@ id:
permission_show_board_views: "Lihat papan"
permission_manage_board_views: "Atur papan"
project_module_board_view: "Papan"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Papan"
label_boards: "Papan"
@@ -34,6 +38,3 @@ id:
Papan dengan kolom otomatis untuk subproyek. Menyeret paket pekerjaan ke daftar lain akan memperbarui (sub) proyek yang sesuai.
subtasks: >
Papan dengan kolom otomatis untuk sub-elemen. Menyeret paket pekerjaan ke daftar lain akan memperbarui induknya.
- upsale:
- teaser_text: 'Apakah Anda ingin mengotomatiskan alur kerja Anda dengan Papan? Papan tingkat lanjut adalah add-on Enterprise. Tingkatkan ke paket berbayar.'
- upgrade: 'Tingkatkan sekarang'
diff --git a/modules/boards/config/locales/crowdin/it.yml b/modules/boards/config/locales/crowdin/it.yml
index d34881e3400..b00f6938276 100644
--- a/modules/boards/config/locales/crowdin/it.yml
+++ b/modules/boards/config/locales/crowdin/it.yml
@@ -6,6 +6,10 @@ it:
permission_show_board_views: "Mostra schede"
permission_manage_board_views: "Gestisci schede"
project_module_board_view: "Tabelloni"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Tabellone"
label_boards: "Tabelloni"
@@ -34,6 +38,3 @@ it:
Bacheca con colonne automatizzate per sotto-progetti. Trascinando le macro-attività su altre liste aggiornerà di conseguenza il (sotto)progetto.
subtasks: >
Bacheca con colonne automatizzate per sotto-elementi. Trascinando le macro-attività su altre liste aggiornerà di conseguenza il genitore.
- upsale:
- teaser_text: 'Vorresti automatizzare i tuoi flussi di lavoro con le bacheche? Le schede avanzate sono un''aggiunta di Enterprise. Passa a un piano a pagamento.'
- upgrade: 'Effettua l''upgrade ora'
diff --git a/modules/boards/config/locales/crowdin/ja.yml b/modules/boards/config/locales/crowdin/ja.yml
index 64a8048f513..52e2684c9a2 100644
--- a/modules/boards/config/locales/crowdin/ja.yml
+++ b/modules/boards/config/locales/crowdin/ja.yml
@@ -6,6 +6,10 @@ ja:
permission_show_board_views: "ボードの表示"
permission_manage_board_views: "ボードの管理"
project_module_board_view: "ボード"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "ボード"
label_boards: "ボード"
@@ -34,6 +38,3 @@ ja:
サブプロジェクトの列が自動化されたボード。ワークパッケージを他のリストにドラッグすると、それに応じて(サブ)プロジェクトが更新されます。
subtasks: >
子要素の列が自動化されたボード。ワークパッケージを他のリストにドラッグすると、それに応じて親要素が更新されます。
- upsale:
- teaser_text: 'ボードでワークフローを自動化しませんか?アドバンスボードはエンタープライズアドオンです。有料プランにアップグレードしてください。'
- upgrade: '今すぐアップグレード'
diff --git a/modules/boards/config/locales/crowdin/js-af.yml b/modules/boards/config/locales/crowdin/js-af.yml
index a2f81d4dc4a..60147f45f96 100644
--- a/modules/boards/config/locales/crowdin/js-af.yml
+++ b/modules/boards/config/locales/crowdin/js-af.yml
@@ -1,14 +1,15 @@
#English strings go here
af:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-ar.yml b/modules/boards/config/locales/crowdin/js-ar.yml
index de4782f4a43..1af3a2266d0 100644
--- a/modules/boards/config/locales/crowdin/js-ar.yml
+++ b/modules/boards/config/locales/crowdin/js-ar.yml
@@ -1,14 +1,15 @@
#English strings go here
ar:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'لوحة غير مسماة'
label_unnamed_list: 'قائمة غير مسماة'
label_board_type: 'نوع اللوحة'
- upsale:
- teaser_text: 'هل ترغب في أتمتة سير العمل الخاص بك باستخدام اللوحات؟ اللوحات المتقدمة هي إضافة خاصة بالمؤسسات. يرجى الترقية إلى خطة مدفوعة.'
- upgrade: 'الترقية الآن'
lists:
delete: 'حذف القائمة'
version:
diff --git a/modules/boards/config/locales/crowdin/js-az.yml b/modules/boards/config/locales/crowdin/js-az.yml
index 029a5840016..2078ca03375 100644
--- a/modules/boards/config/locales/crowdin/js-az.yml
+++ b/modules/boards/config/locales/crowdin/js-az.yml
@@ -1,14 +1,15 @@
#English strings go here
az:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-be.yml b/modules/boards/config/locales/crowdin/js-be.yml
index 91c9b342a45..760b89ae4b6 100644
--- a/modules/boards/config/locales/crowdin/js-be.yml
+++ b/modules/boards/config/locales/crowdin/js-be.yml
@@ -1,14 +1,15 @@
#English strings go here
be:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-bg.yml b/modules/boards/config/locales/crowdin/js-bg.yml
index f1cc3cd5786..8005d96c7bd 100644
--- a/modules/boards/config/locales/crowdin/js-bg.yml
+++ b/modules/boards/config/locales/crowdin/js-bg.yml
@@ -1,14 +1,15 @@
#English strings go here
bg:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Създайте ново табло'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Тип на таблото'
- upsale:
- teaser_text: 'Искате ли да автоматизирате работните си процеси с помощта на Табла? Усъвършенстваните табла са добавка само за Enterprise. Моля, преминете към платен план.'
- upgrade: 'Актуализирайте сега'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-ca.yml b/modules/boards/config/locales/crowdin/js-ca.yml
index d8e6bdf35c0..4c005a38739 100644
--- a/modules/boards/config/locales/crowdin/js-ca.yml
+++ b/modules/boards/config/locales/crowdin/js-ca.yml
@@ -1,14 +1,15 @@
#English strings go here
ca:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Crea un tauler nou'
label_unnamed_board: 'Taulell sense nom'
label_unnamed_list: 'Llista sense nom'
label_board_type: 'Tipus de taulell'
- upsale:
- teaser_text: 'T''agradaria automatitzar els teus processos de treball amb taulells? Els taulells avançats són una extensió de l''edició Enterprise. Si us plau, actualitza a un pla de pagament.'
- upgrade: 'Actualitzeu ara'
lists:
delete: 'Suprimeix la llista'
version:
diff --git a/modules/boards/config/locales/crowdin/js-ckb-IR.yml b/modules/boards/config/locales/crowdin/js-ckb-IR.yml
index f6dff202dff..f3c448bf00f 100644
--- a/modules/boards/config/locales/crowdin/js-ckb-IR.yml
+++ b/modules/boards/config/locales/crowdin/js-ckb-IR.yml
@@ -1,14 +1,15 @@
#English strings go here
ckb-IR:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-cs.yml b/modules/boards/config/locales/crowdin/js-cs.yml
index c067a0b2a01..98b43379189 100644
--- a/modules/boards/config/locales/crowdin/js-cs.yml
+++ b/modules/boards/config/locales/crowdin/js-cs.yml
@@ -1,14 +1,15 @@
#English strings go here
cs:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Vytvořit tabuli'
label_unnamed_board: 'Nepojmenovaná tabule'
label_unnamed_list: 'Nepojmenovaný seznam'
label_board_type: 'Typ tabule'
- upsale:
- teaser_text: 'Chcete automatizovat své workflows s Tabulemi? Pokročilé Tabule jsou doplňkem Enterprise. Proveďte upgrade na placený plán.'
- upgrade: 'Upgradovat nyní'
lists:
delete: 'Odstranit seznam'
version:
diff --git a/modules/boards/config/locales/crowdin/js-da.yml b/modules/boards/config/locales/crowdin/js-da.yml
index 20f2f8f1f16..50a86850922 100644
--- a/modules/boards/config/locales/crowdin/js-da.yml
+++ b/modules/boards/config/locales/crowdin/js-da.yml
@@ -1,14 +1,15 @@
#English strings go here
da:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-de.yml b/modules/boards/config/locales/crowdin/js-de.yml
index 9c9174127c5..ea9bbfe188d 100644
--- a/modules/boards/config/locales/crowdin/js-de.yml
+++ b/modules/boards/config/locales/crowdin/js-de.yml
@@ -1,14 +1,15 @@
#English strings go here
de:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Neues Board erstellen'
label_unnamed_board: 'Unbenanntes Board'
label_unnamed_list: 'Unbenannte Liste'
label_board_type: 'Board-Typ'
- upsale:
- teaser_text: 'Möchten Sie Ihre Workflows mit Boards automatisieren? Erweiterte Boards sind ein Enterprise Add-on. Bitte upgraden Sie auf ein kostenpflichtiges Abonnement.'
- upgrade: 'Jetzt Upgrade durchführen'
lists:
delete: 'Liste löschen'
version:
diff --git a/modules/boards/config/locales/crowdin/js-el.yml b/modules/boards/config/locales/crowdin/js-el.yml
index 854c3c6e54e..5af73b3c268 100644
--- a/modules/boards/config/locales/crowdin/js-el.yml
+++ b/modules/boards/config/locales/crowdin/js-el.yml
@@ -1,14 +1,15 @@
#English strings go here
el:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Δημιουργία νέου πίνακα'
label_unnamed_board: 'Ανώνυμος πίνακας'
label_unnamed_list: 'Ανώνυμη λίστα'
label_board_type: 'Τύπος πίνακα'
- 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: 'Αναβαθμίστε τώρα'
lists:
delete: 'Διαγραφή λίστας'
version:
diff --git a/modules/boards/config/locales/crowdin/js-eo.yml b/modules/boards/config/locales/crowdin/js-eo.yml
index 4ef519904d2..0fbc72233f4 100644
--- a/modules/boards/config/locales/crowdin/js-eo.yml
+++ b/modules/boards/config/locales/crowdin/js-eo.yml
@@ -1,14 +1,15 @@
#English strings go here
eo:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Nenomita panelo'
label_unnamed_list: 'Nenomita listo'
label_board_type: 'Speco de panelo'
- 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: 'Ppromocii nun'
lists:
delete: 'Forigi liston'
version:
diff --git a/modules/boards/config/locales/crowdin/js-es.yml b/modules/boards/config/locales/crowdin/js-es.yml
index bbd62c1ec5e..95cb8b92afb 100644
--- a/modules/boards/config/locales/crowdin/js-es.yml
+++ b/modules/boards/config/locales/crowdin/js-es.yml
@@ -1,14 +1,15 @@
#English strings go here
es:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Crear tablero'
label_unnamed_board: 'Tablero sin nombre'
label_unnamed_list: 'Lista sin nombre'
label_board_type: 'Tipo de tablero'
- upsale:
- teaser_text: '¿Quieres automatizar tus flujos de trabajo con tableros? Los tableros avanzados son una extensión de Enterprise. Por favor, actualice a un plan de pago.'
- upgrade: 'Actualizar ahora'
lists:
delete: 'Eliminar lista'
version:
diff --git a/modules/boards/config/locales/crowdin/js-et.yml b/modules/boards/config/locales/crowdin/js-et.yml
index e373cc04e62..dffeaf1aca5 100644
--- a/modules/boards/config/locales/crowdin/js-et.yml
+++ b/modules/boards/config/locales/crowdin/js-et.yml
@@ -1,14 +1,15 @@
#English strings go here
et:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-eu.yml b/modules/boards/config/locales/crowdin/js-eu.yml
index 9559967940f..7596d17c5f2 100644
--- a/modules/boards/config/locales/crowdin/js-eu.yml
+++ b/modules/boards/config/locales/crowdin/js-eu.yml
@@ -1,14 +1,15 @@
#English strings go here
eu:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-fa.yml b/modules/boards/config/locales/crowdin/js-fa.yml
index 3a0a36aa0fe..659f639a3da 100644
--- a/modules/boards/config/locales/crowdin/js-fa.yml
+++ b/modules/boards/config/locales/crowdin/js-fa.yml
@@ -1,14 +1,15 @@
#English strings go here
fa:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'ایجاد تابلوی اعلانات'
label_unnamed_board: 'انجمن بدون نام'
label_unnamed_list: 'Unnamed list'
label_board_type: 'نوع انجمن'
- upsale:
- teaser_text: 'آیا می خواهید گردش کار خود را با تابلوها خودکار کنید؟ بردهای پیشرفته یک افزونه Enterprise هستند. لطفاً به یک طرح پولی ارتقا دهید.'
- upgrade: 'هم اکنون ارتقا داده شود'
lists:
delete: 'حذف لیست'
version:
diff --git a/modules/boards/config/locales/crowdin/js-fi.yml b/modules/boards/config/locales/crowdin/js-fi.yml
index f38e221035c..1ac1c9d8f7d 100644
--- a/modules/boards/config/locales/crowdin/js-fi.yml
+++ b/modules/boards/config/locales/crowdin/js-fi.yml
@@ -1,14 +1,15 @@
#English strings go here
fi:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Luo uusi taulu'
label_unnamed_board: 'Nimeämätön taulu'
label_unnamed_list: 'Nimeämätön lista'
label_board_type: 'Taulun tyyppi'
- 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: 'Päivitä nyt'
lists:
delete: 'Poista lista'
version:
diff --git a/modules/boards/config/locales/crowdin/js-fil.yml b/modules/boards/config/locales/crowdin/js-fil.yml
index feba7692a4d..221ef4125b6 100644
--- a/modules/boards/config/locales/crowdin/js-fil.yml
+++ b/modules/boards/config/locales/crowdin/js-fil.yml
@@ -1,14 +1,15 @@
#English strings go here
fil:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-fr.yml b/modules/boards/config/locales/crowdin/js-fr.yml
index 96fdc3b404d..13880ecbb02 100644
--- a/modules/boards/config/locales/crowdin/js-fr.yml
+++ b/modules/boards/config/locales/crowdin/js-fr.yml
@@ -1,14 +1,15 @@
#English strings go here
fr:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Créer un nouveau tableau'
label_unnamed_board: 'Tableau sans nom'
label_unnamed_list: 'Liste sans nom'
label_board_type: 'Type de tableau'
- upsale:
- teaser_text: 'Voulez-vous automatiser vos flux de travail avec des tableaux ? Les tableaux avancés sont un add-on Entreprise. Veuillez passer à une offre payante.'
- upgrade: 'Passer au plan supérieur'
lists:
delete: 'Supprimer la liste'
version:
diff --git a/modules/boards/config/locales/crowdin/js-he.yml b/modules/boards/config/locales/crowdin/js-he.yml
index d568c5241c9..8c46ae75980 100644
--- a/modules/boards/config/locales/crowdin/js-he.yml
+++ b/modules/boards/config/locales/crowdin/js-he.yml
@@ -1,14 +1,15 @@
#English strings go here
he:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'ליצור לוח חדש'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'סוג לוח'
- 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: 'שדרג עכשיו'
lists:
delete: 'מחיקת רשימה'
version:
diff --git a/modules/boards/config/locales/crowdin/js-hi.yml b/modules/boards/config/locales/crowdin/js-hi.yml
index 026d9e2dee1..49f0331166f 100644
--- a/modules/boards/config/locales/crowdin/js-hi.yml
+++ b/modules/boards/config/locales/crowdin/js-hi.yml
@@ -1,14 +1,15 @@
#English strings go here
hi:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-hr.yml b/modules/boards/config/locales/crowdin/js-hr.yml
index 957f6663a06..294a567f36b 100644
--- a/modules/boards/config/locales/crowdin/js-hr.yml
+++ b/modules/boards/config/locales/crowdin/js-hr.yml
@@ -1,14 +1,15 @@
#English strings go here
hr:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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: 'Ažuriraj sada'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-hu.yml b/modules/boards/config/locales/crowdin/js-hu.yml
index 8ba83a42f4d..d3e643e39fa 100644
--- a/modules/boards/config/locales/crowdin/js-hu.yml
+++ b/modules/boards/config/locales/crowdin/js-hu.yml
@@ -1,14 +1,15 @@
#English strings go here
hu:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Új tábla létrehozása'
label_unnamed_board: 'Névtelen tábla'
label_unnamed_list: 'Névtelen lista'
label_board_type: 'Tábla típus'
- upsale:
- teaser_text: 'Szeretné automatizálni a munkafolyamatait a Boards-szal? A fejlett táblák Enterprise funkció. Kérjük, frissítsen fizetős csomagra.'
- upgrade: 'Frissítés most'
lists:
delete: 'Törölt lista'
version:
diff --git a/modules/boards/config/locales/crowdin/js-id.yml b/modules/boards/config/locales/crowdin/js-id.yml
index 9a4a7fe9f80..41d56a28382 100644
--- a/modules/boards/config/locales/crowdin/js-id.yml
+++ b/modules/boards/config/locales/crowdin/js-id.yml
@@ -1,14 +1,15 @@
#English strings go here
id:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Buat papan baru'
label_unnamed_board: 'Papan tanpa nama'
label_unnamed_list: 'Daftar tanpa nama'
label_board_type: 'Tipe papan'
- upsale:
- teaser_text: 'Apakah Anda ingin mengotomatiskan alur kerja Anda dengan Papan? Papan tingkat lanjut adalah add-on Enterprise. Tingkatkan ke paket berbayar.'
- upgrade: 'Upgrade sekarang'
lists:
delete: 'Hapus daftar'
version:
diff --git a/modules/boards/config/locales/crowdin/js-it.yml b/modules/boards/config/locales/crowdin/js-it.yml
index 30fdae00c45..02cb9f6770f 100644
--- a/modules/boards/config/locales/crowdin/js-it.yml
+++ b/modules/boards/config/locales/crowdin/js-it.yml
@@ -1,14 +1,15 @@
#English strings go here
it:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Crea una nuova scheda'
label_unnamed_board: 'Scheda senza nome'
label_unnamed_list: 'Lista senza nome'
label_board_type: 'Tipo scheda'
- upsale:
- teaser_text: 'Vorresti automatizzare i tuoi flussi di lavoro con Boards? Le schede avanzate sono un addon di Imprese. Sei pregato di passare a un piano a pagamento.'
- upgrade: 'Aggiorna ora'
lists:
delete: 'Elimina elenco'
version:
diff --git a/modules/boards/config/locales/crowdin/js-ja.yml b/modules/boards/config/locales/crowdin/js-ja.yml
index b64547bc2ec..09a35e70329 100644
--- a/modules/boards/config/locales/crowdin/js-ja.yml
+++ b/modules/boards/config/locales/crowdin/js-ja.yml
@@ -1,14 +1,15 @@
#English strings go here
ja:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: '新しい掲示板を作成'
label_unnamed_board: '無題のボード'
label_unnamed_list: '無題のリスト'
label_board_type: 'ボードの形式'
- upsale:
- teaser_text: 'ボードでワークフローを自動化しませんか?アドバンスボードはエンタープライズアドオンです。有料プランにアップグレードしてください。'
- upgrade: '今すぐアップグレード'
lists:
delete: 'リストを削除'
version:
diff --git a/modules/boards/config/locales/crowdin/js-ka.yml b/modules/boards/config/locales/crowdin/js-ka.yml
index 6498ef6244b..77c58faf396 100644
--- a/modules/boards/config/locales/crowdin/js-ka.yml
+++ b/modules/boards/config/locales/crowdin/js-ka.yml
@@ -1,14 +1,15 @@
#English strings go here
ka:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'უსახელო დაფა'
label_unnamed_list: 'უსახელო სია'
label_board_type: 'დაფის ტიპი'
- upsale:
- teaser_text: 'გნებავთ თქვენი საწარმოო პროცესების ავტომატიზაცია Boards-ით? დამატებითი დაფები საწარმოო ვერსიის მოდულია. გადადით ფასიან ვერსიაზე.'
- upgrade: 'ახლავე განაახლე'
lists:
delete: 'სიის წაშლა'
version:
diff --git a/modules/boards/config/locales/crowdin/js-kk.yml b/modules/boards/config/locales/crowdin/js-kk.yml
index f5b6aafa59f..9fcae91d19e 100644
--- a/modules/boards/config/locales/crowdin/js-kk.yml
+++ b/modules/boards/config/locales/crowdin/js-kk.yml
@@ -1,14 +1,15 @@
#English strings go here
kk:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- upsale:
- teaser_text: 'Өзіңіздегі жұмыс ағымын автоматтандырғыңыз келеді ме? Жетілдірілген тақталар Enterprise қосымшасы болып келеді. Ақылы жоспарға өтуіңізді сұраймыз.'
- upgrade: 'Upgrade now'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-ko.yml b/modules/boards/config/locales/crowdin/js-ko.yml
index e688be2cc96..c6ece2ea0e8 100644
--- a/modules/boards/config/locales/crowdin/js-ko.yml
+++ b/modules/boards/config/locales/crowdin/js-ko.yml
@@ -1,14 +1,15 @@
#English strings go here
ko:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: '새 보드 만들기'
label_unnamed_board: '명명되지 않은 보드'
label_unnamed_list: '명명되지 않은 목록'
label_board_type: '보드 유형'
- upsale:
- teaser_text: '보드에서 작업 흐름을 자동화하길 원하시나요? 고급 보드는 기업용 기능입니다. 유료 플랜으로 업그레이드하세요.'
- upgrade: '지금 업그레이드'
lists:
delete: '목록 삭제'
version:
diff --git a/modules/boards/config/locales/crowdin/js-lt.yml b/modules/boards/config/locales/crowdin/js-lt.yml
index ab6ecc367a1..31f208f04e4 100644
--- a/modules/boards/config/locales/crowdin/js-lt.yml
+++ b/modules/boards/config/locales/crowdin/js-lt.yml
@@ -1,14 +1,15 @@
#English strings go here
lt:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Kurti naują lentą'
label_unnamed_board: 'Bevardė lenta'
label_unnamed_list: 'Bevardis sąrašas'
label_board_type: 'Lentos tipas'
- 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'
lists:
delete: 'Pašalinti sąrašą'
version:
diff --git a/modules/boards/config/locales/crowdin/js-lv.yml b/modules/boards/config/locales/crowdin/js-lv.yml
index 894b228afad..94b7ea9e16d 100644
--- a/modules/boards/config/locales/crowdin/js-lv.yml
+++ b/modules/boards/config/locales/crowdin/js-lv.yml
@@ -1,14 +1,15 @@
#English strings go here
lv:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-mn.yml b/modules/boards/config/locales/crowdin/js-mn.yml
index bc9e1ec407b..8e24292bf27 100644
--- a/modules/boards/config/locales/crowdin/js-mn.yml
+++ b/modules/boards/config/locales/crowdin/js-mn.yml
@@ -1,14 +1,15 @@
#English strings go here
mn:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Шинэ самбар үүсгэх'
label_unnamed_board: 'Нэрлэгдээгүй самбар'
label_unnamed_list: 'Нэрлэгдээгүй жагсаалт'
label_board_type: 'Самбарын төрөл'
- upsale:
- teaser_text: 'Та самбарын тусламжтайгаар ажлын урсгалаа автоматжуулахыг хүсч байна уу? Нарийвчилсан самбарууд нь Enterprise нэмэлт юм. Төлбөртэй багц болгон шинэчилнэ үү.'
- upgrade: 'Яг одоо шинэчлэх'
lists:
delete: 'Устгасан жагсаалт'
version:
diff --git a/modules/boards/config/locales/crowdin/js-ms.yml b/modules/boards/config/locales/crowdin/js-ms.yml
index aab49afba5f..5881e6422d7 100644
--- a/modules/boards/config/locales/crowdin/js-ms.yml
+++ b/modules/boards/config/locales/crowdin/js-ms.yml
@@ -1,14 +1,15 @@
#English strings go here
ms:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Cipta board baru'
label_unnamed_board: 'Board tanpa nama'
label_unnamed_list: 'Senarai tanpa nama'
label_board_type: "Jenis board\n"
- upsale:
- teaser_text: 'Adakah anda ingin mengautomasikan aliran kerja dengan Board? Board lanjutan ada di dalam tambahan Enterprise. Sila naik taraf ke pelan berbayar.'
- upgrade: 'Naik taraf sekarang'
lists:
delete: 'Padam senarai'
version:
diff --git a/modules/boards/config/locales/crowdin/js-ne.yml b/modules/boards/config/locales/crowdin/js-ne.yml
index 03b1b75e375..b6b077a24f0 100644
--- a/modules/boards/config/locales/crowdin/js-ne.yml
+++ b/modules/boards/config/locales/crowdin/js-ne.yml
@@ -1,14 +1,15 @@
#English strings go here
ne:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-nl.yml b/modules/boards/config/locales/crowdin/js-nl.yml
index 848af3f3fdf..64b390bb48c 100644
--- a/modules/boards/config/locales/crowdin/js-nl.yml
+++ b/modules/boards/config/locales/crowdin/js-nl.yml
@@ -1,14 +1,15 @@
#English strings go here
nl:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Maak een nieuw bord'
label_unnamed_board: 'Naamloos bord'
label_unnamed_list: 'Naamloze lijst'
label_board_type: 'Type bord'
- upsale:
- teaser_text: 'Wilt u uw workflows automatiseren met Borden? Geavanceerde borden zijn een Enterprise add-on. Upgrade naar een betaald plan.'
- upgrade: 'Upgrade nu'
lists:
delete: 'Lijst verwijderen'
version:
diff --git a/modules/boards/config/locales/crowdin/js-no.yml b/modules/boards/config/locales/crowdin/js-no.yml
index 49e89ed3180..a83ab5e7528 100644
--- a/modules/boards/config/locales/crowdin/js-no.yml
+++ b/modules/boards/config/locales/crowdin/js-no.yml
@@ -1,14 +1,15 @@
#English strings go here
"no":
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Opprett ny tavle'
label_unnamed_board: 'Navnløs tavle'
label_unnamed_list: 'Navnløs liste'
label_board_type: 'Tavle type'
- upsale:
- teaser_text: 'Ønsker du å automatisere dine arbeidsflyter med Tavler? Avanserte tavler er et Enterprise add-on. Vennligst oppgrader til et betalt abonnement.'
- upgrade: 'Oppgrader nå'
lists:
delete: 'Slett liste'
version:
diff --git a/modules/boards/config/locales/crowdin/js-pl.yml b/modules/boards/config/locales/crowdin/js-pl.yml
index 095b038a443..d923086a63c 100644
--- a/modules/boards/config/locales/crowdin/js-pl.yml
+++ b/modules/boards/config/locales/crowdin/js-pl.yml
@@ -1,14 +1,15 @@
#English strings go here
pl:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Utwórz nową tablicę'
label_unnamed_board: 'Tablica bez nazwy'
label_unnamed_list: 'Lista bez nazwy'
label_board_type: 'Typ tablicy'
- upsale:
- teaser_text: 'Czy chcesz zautomatyzować swoje przepływy pracy za pomocą tablic? Zaawansowane tablice to funkcja dodatku Enterprise. Przejdź na płatny plan.'
- upgrade: 'Zmień plan teraz'
lists:
delete: 'Usuń listę'
version:
diff --git a/modules/boards/config/locales/crowdin/js-pt-BR.yml b/modules/boards/config/locales/crowdin/js-pt-BR.yml
index aac3c5d7370..19dcf090599 100644
--- a/modules/boards/config/locales/crowdin/js-pt-BR.yml
+++ b/modules/boards/config/locales/crowdin/js-pt-BR.yml
@@ -1,14 +1,15 @@
#English strings go here
pt-BR:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Criar novo quadro'
label_unnamed_board: 'Quadro sem nome'
label_unnamed_list: 'Lista sem nome'
label_board_type: 'Tipo de quadro'
- upsale:
- teaser_text: 'Gostaria de automatizar seu fluxo de trabalho com Painéis? Painéis avançados são um complemento Enterprise. Atualize para um plano pago.'
- upgrade: 'Atualizar agora'
lists:
delete: 'Excluir lista'
version:
diff --git a/modules/boards/config/locales/crowdin/js-pt-PT.yml b/modules/boards/config/locales/crowdin/js-pt-PT.yml
index a86dbc06923..92b0cc49807 100644
--- a/modules/boards/config/locales/crowdin/js-pt-PT.yml
+++ b/modules/boards/config/locales/crowdin/js-pt-PT.yml
@@ -1,14 +1,15 @@
#English strings go here
pt-PT:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Criar novo quadro'
label_unnamed_board: 'Quadro sem nome'
label_unnamed_list: 'Lista sem nome'
label_board_type: 'Tipo de quadro'
- upsale:
- teaser_text: 'Gostaria de automatizar seus fluxos de trabalho com Quadros? Quadros avançados são um complemento de Enterprise. Faça upgrade para um plano pago.'
- upgrade: 'Atualizar agora'
lists:
delete: 'Apagar lista'
version:
diff --git a/modules/boards/config/locales/crowdin/js-ro.yml b/modules/boards/config/locales/crowdin/js-ro.yml
index bcc598a1e86..f87bdc9f57c 100644
--- a/modules/boards/config/locales/crowdin/js-ro.yml
+++ b/modules/boards/config/locales/crowdin/js-ro.yml
@@ -1,14 +1,15 @@
#English strings go here
ro:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Creează un nou panou'
label_unnamed_board: 'Consiliul de administrație fără nume'
label_unnamed_list: 'Listă fără nume'
label_board_type: 'Tipul de placă'
- upsale:
- teaser_text: 'Doriți să vă automatizați fluxurile de lucru cu Boards? Tablourile avansate sunt un add-on pentru Enterprise. Vă rugăm să treceți la un plan plătit.'
- upgrade: 'Actualizează acum'
lists:
delete: 'Ștergeți lista'
version:
diff --git a/modules/boards/config/locales/crowdin/js-ru.yml b/modules/boards/config/locales/crowdin/js-ru.yml
index e47a8fa39c5..918d4b0bad5 100644
--- a/modules/boards/config/locales/crowdin/js-ru.yml
+++ b/modules/boards/config/locales/crowdin/js-ru.yml
@@ -1,14 +1,15 @@
#English strings go here
ru:
+ ee:
+ upsale:
+ board_view:
+ description: 'Хотите автоматизировать работу с досками? Обновите платный план до корпоративного.'
js:
boards:
create_new: 'Создать новую доску'
label_unnamed_board: 'Безымянная доска'
label_unnamed_list: 'Список досок без имени'
label_board_type: 'Тип доски'
- upsale:
- teaser_text: 'Вы хотите автоматизировать свои рабочие процессы с досками? Расширенные доски являются дополнением предприятия. Пожалуйста, обновите платный план.'
- upgrade: 'Обновить сейчас'
lists:
delete: 'Удалить список'
version:
diff --git a/modules/boards/config/locales/crowdin/js-rw.yml b/modules/boards/config/locales/crowdin/js-rw.yml
index 013c1e00607..7c49742fe96 100644
--- a/modules/boards/config/locales/crowdin/js-rw.yml
+++ b/modules/boards/config/locales/crowdin/js-rw.yml
@@ -1,14 +1,15 @@
#English strings go here
rw:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-si.yml b/modules/boards/config/locales/crowdin/js-si.yml
index 38dfb80f3fc..bbd42fc3f1e 100644
--- a/modules/boards/config/locales/crowdin/js-si.yml
+++ b/modules/boards/config/locales/crowdin/js-si.yml
@@ -1,14 +1,15 @@
#English strings go here
si:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-sk.yml b/modules/boards/config/locales/crowdin/js-sk.yml
index 6ce12a623e4..0ec8bbe6940 100644
--- a/modules/boards/config/locales/crowdin/js-sk.yml
+++ b/modules/boards/config/locales/crowdin/js-sk.yml
@@ -1,14 +1,15 @@
#English strings go here
sk:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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: 'Prejdite na vyššiu verziu'
lists:
delete: 'Vymazať zoznam'
version:
diff --git a/modules/boards/config/locales/crowdin/js-sl.yml b/modules/boards/config/locales/crowdin/js-sl.yml
index ffb9dc3f946..7cd20eafb51 100644
--- a/modules/boards/config/locales/crowdin/js-sl.yml
+++ b/modules/boards/config/locales/crowdin/js-sl.yml
@@ -1,14 +1,15 @@
#English strings go here
sl:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Ustvari novo tablo'
label_unnamed_board: 'Neimenovana tabla'
label_unnamed_list: 'Neimenovani seznam'
label_board_type: 'Vrsta table'
- 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: 'Nadgradi zdaj'
lists:
delete: 'Izbriši seznam'
version:
diff --git a/modules/boards/config/locales/crowdin/js-sr.yml b/modules/boards/config/locales/crowdin/js-sr.yml
index 16744793f0b..4abbb3d4c44 100644
--- a/modules/boards/config/locales/crowdin/js-sr.yml
+++ b/modules/boards/config/locales/crowdin/js-sr.yml
@@ -1,14 +1,15 @@
#English strings go here
sr:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Nova tabla'
label_unnamed_board: 'Neimenovana tabla'
label_unnamed_list: 'Neimenovana lista'
label_board_type: 'Tip table'
- 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: 'Nadogradite'
lists:
delete: 'Obriši listu'
version:
diff --git a/modules/boards/config/locales/crowdin/js-sv.yml b/modules/boards/config/locales/crowdin/js-sv.yml
index 86dc494bdcf..433e8fd6e5b 100644
--- a/modules/boards/config/locales/crowdin/js-sv.yml
+++ b/modules/boards/config/locales/crowdin/js-sv.yml
@@ -1,14 +1,15 @@
#English strings go here
sv:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Skapa ny tavla'
label_unnamed_board: 'Namnlös tavla'
label_unnamed_list: 'Namnlös lista'
label_board_type: 'Typ av tavla'
- 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: 'Uppgradera nu'
lists:
delete: 'Ta bort lista'
version:
diff --git a/modules/boards/config/locales/crowdin/js-th.yml b/modules/boards/config/locales/crowdin/js-th.yml
index dd2d946c559..75e27a1dcc9 100644
--- a/modules/boards/config/locales/crowdin/js-th.yml
+++ b/modules/boards/config/locales/crowdin/js-th.yml
@@ -1,14 +1,15 @@
#English strings go here
th:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-tr.yml b/modules/boards/config/locales/crowdin/js-tr.yml
index 3907d03e983..719bfd907c7 100644
--- a/modules/boards/config/locales/crowdin/js-tr.yml
+++ b/modules/boards/config/locales/crowdin/js-tr.yml
@@ -1,14 +1,15 @@
#English strings go here
tr:
+ ee:
+ upsale:
+ board_view:
+ description: 'İş akışlarınızı Panolar ile otomatikleştirmek ister misiniz? Gelişmiş panolar Enterprise eklentidir. Lütfen ücretli bir plana yükseltin.'
js:
boards:
create_new: 'Yeni pano oluştur'
label_unnamed_board: 'Adsız tahta'
label_unnamed_list: 'Adsız liste'
label_board_type: 'Yazı tahtası tipi'
- upsale:
- teaser_text: 'Panolar ile iş akışlarınızı otomatikleştirmek ister misiniz? Gelişmiş kartlar bir Enterprise eklentidir. Lütfen ücretli bir plana yükseltin.'
- upgrade: 'Şimdi Yükselt'
lists:
delete: 'Listeyi silin'
version:
diff --git a/modules/boards/config/locales/crowdin/js-uk.yml b/modules/boards/config/locales/crowdin/js-uk.yml
index dbb6c2cf6de..fbd740a74c5 100644
--- a/modules/boards/config/locales/crowdin/js-uk.yml
+++ b/modules/boards/config/locales/crowdin/js-uk.yml
@@ -1,14 +1,15 @@
#English strings go here
uk:
+ ee:
+ upsale:
+ board_view:
+ description: 'Хочете автоматизувати робочі процеси за допомогою Дощок? Поліпшені дошки – це доповнення версії Enterprise. Перейдіть на платний план.'
js:
boards:
create_new: 'Створити нову дошку'
label_unnamed_board: 'Дошка без назви'
label_unnamed_list: 'Список без назви'
label_board_type: 'Тип дошки'
- upsale:
- teaser_text: 'Хочете автоматизувати робочі процеси за допомогою дощок? Поліпшені дошки – це доповнення версії Enterprise. Перейдіть на платний план.'
- upgrade: 'Купити зараз'
lists:
delete: 'Вилучити список'
version:
diff --git a/modules/boards/config/locales/crowdin/js-uz.yml b/modules/boards/config/locales/crowdin/js-uz.yml
index c389f781826..78fb27318f7 100644
--- a/modules/boards/config/locales/crowdin/js-uz.yml
+++ b/modules/boards/config/locales/crowdin/js-uz.yml
@@ -1,14 +1,15 @@
#English strings go here
uz:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
- 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'
lists:
delete: 'Delete list'
version:
diff --git a/modules/boards/config/locales/crowdin/js-vi.yml b/modules/boards/config/locales/crowdin/js-vi.yml
index 190909601d3..db0aed56810 100644
--- a/modules/boards/config/locales/crowdin/js-vi.yml
+++ b/modules/boards/config/locales/crowdin/js-vi.yml
@@ -1,14 +1,15 @@
#English strings go here
vi:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Tạo bảng mới'
label_unnamed_board: 'Bảng không tên'
label_unnamed_list: 'Danh sách không tên'
label_board_type: 'Kiểu bảng'
- upsale:
- teaser_text: 'Bạn có muốn tự động hóa quy trình làm việc của mình với các bảng không? Các bảng nâng cao là một tiện ích bổ sung Enterprise. Vui lòng nâng cấp lên gói trả phí.'
- upgrade: 'Nâng cấp ngay'
lists:
delete: 'Xoá danh sách'
version:
diff --git a/modules/boards/config/locales/crowdin/js-zh-CN.yml b/modules/boards/config/locales/crowdin/js-zh-CN.yml
index 46c4f905bb3..1aa98fbdd9a 100644
--- a/modules/boards/config/locales/crowdin/js-zh-CN.yml
+++ b/modules/boards/config/locales/crowdin/js-zh-CN.yml
@@ -1,14 +1,15 @@
#English strings go here
zh-CN:
+ ee:
+ upsale:
+ board_view:
+ description: '是否希望使用面板自动化您的工作流?高级面板是企业版附加组件。请升级到付费计划。'
js:
boards:
create_new: '创建新面板'
label_unnamed_board: '未命名面板'
label_unnamed_list: '未命名列表'
label_board_type: '面板类型'
- upsale:
- teaser_text: '您想使用面板自动化您的工作流吗?高级面板是企业附加组件。请升级到付费计划。'
- upgrade: '立即升级'
lists:
delete: '删除列表'
version:
diff --git a/modules/boards/config/locales/crowdin/js-zh-TW.yml b/modules/boards/config/locales/crowdin/js-zh-TW.yml
index 21d0323761b..dde54b66c18 100644
--- a/modules/boards/config/locales/crowdin/js-zh-TW.yml
+++ b/modules/boards/config/locales/crowdin/js-zh-TW.yml
@@ -1,14 +1,15 @@
#English strings go here
zh-TW:
+ ee:
+ upsale:
+ board_view:
+ description: '想使用看板來自動化您的工作流程嗎?進階看板是企業版附加功能,請升級至付費方案以啟用此功能。'
js:
boards:
create_new: '建立看板'
label_unnamed_board: '未命名看板'
label_unnamed_list: '未命名列表'
label_board_type: '看板類型'
- upsale:
- teaser_text: '你想用看板來自動化你的工作流程嗎?進階看板是一項企業功能。請升級到付費計劃。'
- upgrade: '立即升級'
lists:
delete: '刪除列表'
version:
diff --git a/modules/boards/config/locales/crowdin/ka.yml b/modules/boards/config/locales/crowdin/ka.yml
index e937a22973f..daa1ec212c3 100644
--- a/modules/boards/config/locales/crowdin/ka.yml
+++ b/modules/boards/config/locales/crowdin/ka.yml
@@ -6,6 +6,10 @@ ka:
permission_show_board_views: "დაფების ჩვენება"
permission_manage_board_views: "დაფების მართვა"
project_module_board_view: "დაფები"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "დაფა"
label_boards: "დაფები"
@@ -34,6 +38,3 @@ ka:
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: 'გნებავთ თქვენი საწარმოო პროცესების ავტომატიზაცია Boards-ით? დამატებითი დაფები საწარმოო ვერსიის მოდულია. გადადით ფასიან ვერსიაზე.'
- upgrade: 'ახლავე განაახლე'
diff --git a/modules/boards/config/locales/crowdin/kk.yml b/modules/boards/config/locales/crowdin/kk.yml
index fec730ebf5b..51dc2f7df4c 100644
--- a/modules/boards/config/locales/crowdin/kk.yml
+++ b/modules/boards/config/locales/crowdin/kk.yml
@@ -6,6 +6,10 @@ kk:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ kk:
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: 'Өзіңіздегі жұмыс ағымын автоматтандырғыңыз келеді ме? Жетілдірілген тақталар Enterprise қосымшасы болып келеді. Ақылы жоспарға өтуіңізді сұраймыз.'
- upgrade: 'Upgrade now'
diff --git a/modules/boards/config/locales/crowdin/ko.yml b/modules/boards/config/locales/crowdin/ko.yml
index fda10e1aa48..514d8c18df7 100644
--- a/modules/boards/config/locales/crowdin/ko.yml
+++ b/modules/boards/config/locales/crowdin/ko.yml
@@ -6,6 +6,10 @@ ko:
permission_show_board_views: "보드 보기"
permission_manage_board_views: "보드 관리"
project_module_board_view: "보드"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "게시판"
label_boards: "보드"
@@ -34,6 +38,3 @@ ko:
하위 프로젝트에 대한 자동화된 열이 있는 보드입니다. 작업 패키지를 다른 목록으로 끌어다 놓으면 그에 따라 (하위) 프로젝트가 업데이트됩니다.
subtasks: >
하위 요소에 대한 자동화된 열이 있는 보드입니다. 작업 패키지를 다른 목록으로 끌어다 놓으면 그에 따라 부모가 업데이트됩니다.
- upsale:
- teaser_text: '보드에서 워크플로를 자동화하길 원하시나요? 고급 보드는 Enterprise 추가 기능입니다. 유료 플랜으로 업그레이드하세요.'
- upgrade: '지금 업그레이드'
diff --git a/modules/boards/config/locales/crowdin/lt.yml b/modules/boards/config/locales/crowdin/lt.yml
index 943f9759ea4..e392bc14ee0 100644
--- a/modules/boards/config/locales/crowdin/lt.yml
+++ b/modules/boards/config/locales/crowdin/lt.yml
@@ -6,6 +6,10 @@ lt:
permission_show_board_views: "Peržiūrėti lentas"
permission_manage_board_views: "Valdyti lentas"
project_module_board_view: "Lentos"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Lenta"
label_boards: "Lentos"
@@ -34,6 +38,3 @@ lt:
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 bf16ccf2531..becfdf3f3bb 100644
--- a/modules/boards/config/locales/crowdin/lv.yml
+++ b/modules/boards/config/locales/crowdin/lv.yml
@@ -6,6 +6,10 @@ lv:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ lv:
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 6401d3514b7..15e3930d088 100644
--- a/modules/boards/config/locales/crowdin/mn.yml
+++ b/modules/boards/config/locales/crowdin/mn.yml
@@ -6,6 +6,10 @@ mn:
permission_show_board_views: "Самбаруудыг үзэх"
permission_manage_board_views: "Самбаруудыг удирдах"
project_module_board_view: "Самбарууд"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Самбар"
label_boards: "Самбарууд"
@@ -34,6 +38,3 @@ mn:
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: 'Та самбарын тусламжтайгаар ажлын урсгалаа автоматжуулахыг хүсч байна уу? Нарийвчилсан самбарууд нь Enterprise нэмэлт юм. Төлбөртэй багц болгон шинэчилнэ үү.'
- upgrade: 'Одоо шинэчлэх'
diff --git a/modules/boards/config/locales/crowdin/ms.yml b/modules/boards/config/locales/crowdin/ms.yml
index f93e31aab32..c30bb059edd 100644
--- a/modules/boards/config/locales/crowdin/ms.yml
+++ b/modules/boards/config/locales/crowdin/ms.yml
@@ -6,6 +6,10 @@ ms:
permission_show_board_views: "Paparkan board"
permission_manage_board_views: "Urus papan"
project_module_board_view: "Board"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Board"
@@ -34,6 +38,3 @@ ms:
Board dengan kolum automatik untuk subprojek. Menarik pakej kerja ke senarai lain akan mengemas kini (sub-)projek sewajarnya.
subtasks: >
Board dengan kolum automatik untuk sub-elements. Menarik pakej kerja ke senarai lain akan mengemas kini (sub-)projek sewajarnya.
- upsale:
- teaser_text: 'Adakah anda ingin mengautomasikan aliran kerja dengan Board? Board lanjutan ada di dalam tambahan Enterprise. Sila naik taraf ke pelan berbayar.'
- upgrade: 'Naik taraf sekarang'
diff --git a/modules/boards/config/locales/crowdin/ne.yml b/modules/boards/config/locales/crowdin/ne.yml
index 45490d0c1af..5807b3d3a9f 100644
--- a/modules/boards/config/locales/crowdin/ne.yml
+++ b/modules/boards/config/locales/crowdin/ne.yml
@@ -6,6 +6,10 @@ ne:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "बोर्डहरु"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "बोर्ड"
label_boards: "बोर्डहरु"
@@ -34,6 +38,3 @@ ne:
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 a1dc7e70452..a270647d3a2 100644
--- a/modules/boards/config/locales/crowdin/nl.yml
+++ b/modules/boards/config/locales/crowdin/nl.yml
@@ -6,6 +6,10 @@ nl:
permission_show_board_views: "Bekijk borden"
permission_manage_board_views: "Beheer boards"
project_module_board_view: "Borden"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Bord"
label_boards: "Borden"
@@ -34,6 +38,3 @@ nl:
Bord met geautomatiseerde kolommen voor subprojecten. Werkpakketten naar andere lijsten verplaatsen, werkt het (sub)project dienovereenkomstig bij.
subtasks: >
Bord met geautomatiseerde kolommen voor subelementen. Het verslepen van werkpakketten naar andere lijsten werkt de bovenliggende map bij.
- upsale:
- teaser_text: 'Wilt u uw workflows automatiseren met Borden? Geavanceerde borden zijn een Enterprise add-on. Upgrade naar een betaald plan.'
- upgrade: 'Nu upgraden'
diff --git a/modules/boards/config/locales/crowdin/no.yml b/modules/boards/config/locales/crowdin/no.yml
index 4910340d70c..a3c70a3bc0a 100644
--- a/modules/boards/config/locales/crowdin/no.yml
+++ b/modules/boards/config/locales/crowdin/no.yml
@@ -6,6 +6,10 @@
permission_show_board_views: "Vis tavler"
permission_manage_board_views: "Administrer tavler"
project_module_board_view: "Tavler"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Tavle"
label_boards: "Tavler"
@@ -34,6 +38,3 @@
Tavle med automatiserte kolonner for underprosjekter. Å dra arbeidspakker til andre lister oppdaterer (under)prosjekter tilsvarende.
subtasks: >
Tavle med automatiserte kolonner for underprosjekter. Å dra oppgaver til andre lister oppdaterer overordnet prosjekt tilsvarende.
- upsale:
- teaser_text: 'Ønsker du å automatisere dine arbeidsflyter med Tavler? Avanserte tavler er et Enterprise add-on. Vennligst oppgrader til et betalt abonnement.'
- upgrade: 'Oppgrader nå'
diff --git a/modules/boards/config/locales/crowdin/pl.yml b/modules/boards/config/locales/crowdin/pl.yml
index 67c5811a3dc..4e56edff0a1 100644
--- a/modules/boards/config/locales/crowdin/pl.yml
+++ b/modules/boards/config/locales/crowdin/pl.yml
@@ -6,6 +6,10 @@ pl:
permission_show_board_views: "Wyświetl tablice"
permission_manage_board_views: "Zarządzaj tablicami"
project_module_board_view: "Tablice"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Tablica"
label_boards: "Tablice"
@@ -34,6 +38,3 @@ pl:
Tablica z zautomatyzowanymi kolumnami projektów składowych. Przeciąganie pakietów roboczych na inne listy odpowiednio aktualizuje (pod)projekt.
subtasks: >
Tablica z zautomatyzowanymi kolumnami elementów składowych. Przeciąganie pakietów roboczych na inne listy odpowiednio aktualizuje elementy nadrzędne.
- upsale:
- teaser_text: 'Czy chcesz zautomatyzować swoje przepływy pracy za pomocą tablic? Zaawansowane tablice to funkcja dodatku Enterprise. Przejdź na płatny plan.'
- upgrade: 'Uaktualnij teraz'
diff --git a/modules/boards/config/locales/crowdin/pt-BR.yml b/modules/boards/config/locales/crowdin/pt-BR.yml
index e7436ae9c33..0f7a2566888 100644
--- a/modules/boards/config/locales/crowdin/pt-BR.yml
+++ b/modules/boards/config/locales/crowdin/pt-BR.yml
@@ -6,6 +6,10 @@ pt-BR:
permission_show_board_views: "Visualizar quadros"
permission_manage_board_views: "Gerenciar quadros"
project_module_board_view: "Quadros"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Quadro"
label_boards: "Quadros"
@@ -34,6 +38,3 @@ pt-BR:
Quadro com colunas automatizadas para subprojetos. Arrastar pacotes de trabalho para outras listas atualiza os (sub)projetos de acordo.
subtasks: >
Quadro com colunas automatizadas para subelementos. Arrastar pacotes de trabalho para outras listas atualiza os pais de acordo.
- upsale:
- teaser_text: 'Gostaria de automatizar seu fluxo de trabalho com Quadros? Quadros avançados são um complemento do Enterprise. Atualize para um plano pago.'
- upgrade: 'Faça o upgrade agora'
diff --git a/modules/boards/config/locales/crowdin/pt-PT.yml b/modules/boards/config/locales/crowdin/pt-PT.yml
index a88e3d5bf33..000d3a64ed9 100644
--- a/modules/boards/config/locales/crowdin/pt-PT.yml
+++ b/modules/boards/config/locales/crowdin/pt-PT.yml
@@ -6,6 +6,10 @@ pt-PT:
permission_show_board_views: "Ver quadros"
permission_manage_board_views: "Gerir quadros"
project_module_board_view: "Quadros"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Quadro"
label_boards: "Quadros"
@@ -34,6 +38,3 @@ pt-PT:
Quadro com colunas automatizadas para subprojetos. Arrastar pacotes de trabalho para outras listas atualiza o (sub)projeto de acordo.
subtasks: >
Quadro com colunas automatizadas para sub-elementos. Arrastar pacotes de trabalho para outras listas atualiza o elemento principal de acordo.
- upsale:
- teaser_text: 'Gostaria de automatizar os seus fluxos de trabalho com Quadros? Os quadros avançados são um complemento Enterprise. Faça upgrade para um plano pago.'
- upgrade: 'Fazer o upgrade agora'
diff --git a/modules/boards/config/locales/crowdin/ro.yml b/modules/boards/config/locales/crowdin/ro.yml
index a420f7f9e02..77f64ab7e46 100644
--- a/modules/boards/config/locales/crowdin/ro.yml
+++ b/modules/boards/config/locales/crowdin/ro.yml
@@ -6,6 +6,10 @@ ro:
permission_show_board_views: "Vezi panouri"
permission_manage_board_views: "Gestionați plăcile"
project_module_board_view: "Panouri"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Panou"
label_boards: "Panouri"
@@ -34,6 +38,3 @@ ro:
Tablă cu coloane automate pentru subproiecte. Glisarea pachetelor de lucru în alte liste actualizează (sub)proiectul în mod corespunzător.
subtasks: >
Tablă cu coloane automate pentru subelemente. Glisarea pachetelor de lucru în alte liste actualizează în mod corespunzător lista părinte.
- upsale:
- teaser_text: 'Doriți să vă automatizați fluxurile de lucru cu Boards? Tablourile avansate sunt un add-on pentru Enterprise. Vă rugăm să treceți la un plan plătit.'
- upgrade: 'Actualizează acum'
diff --git a/modules/boards/config/locales/crowdin/ru.yml b/modules/boards/config/locales/crowdin/ru.yml
index 9686e19e036..9e6f734533e 100644
--- a/modules/boards/config/locales/crowdin/ru.yml
+++ b/modules/boards/config/locales/crowdin/ru.yml
@@ -6,6 +6,10 @@ ru:
permission_show_board_views: "Просмотр досок"
permission_manage_board_views: "Управление досками"
project_module_board_view: "Доски"
+ ee:
+ upsale:
+ board_view:
+ description: 'Хотите автоматизировать работу с досками? Обновите платный план до корпоративного.'
boards:
label_board: "Доска"
label_boards: "Доски"
@@ -34,6 +38,3 @@ ru:
Доска с автоматизированными колонками для подпроектов. Перетаскивание пакетов работ в другие списки соответствующим образом обновляет подпроект.
subtasks: >
Доска с автоматизированными колонками для подзадач. Перетаскивание пакетов работ в другие списки соответствующим образом обновляет родительский список.
- upsale:
- teaser_text: 'Хотите автоматизировать работу с досками? Обновите платный план до корпоративного.'
- upgrade: 'Обновить сейчас'
diff --git a/modules/boards/config/locales/crowdin/rw.yml b/modules/boards/config/locales/crowdin/rw.yml
index 8711d541dcb..6c8dd0cb675 100644
--- a/modules/boards/config/locales/crowdin/rw.yml
+++ b/modules/boards/config/locales/crowdin/rw.yml
@@ -6,6 +6,10 @@ rw:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ rw:
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 687df39b219..dafad51a44a 100644
--- a/modules/boards/config/locales/crowdin/si.yml
+++ b/modules/boards/config/locales/crowdin/si.yml
@@ -6,6 +6,10 @@ si:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "පුවරු"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "මණ්ඩලය"
label_boards: "පුවරු"
@@ -34,6 +38,3 @@ si:
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 f72b8cf0bf0..32bb4ada491 100644
--- a/modules/boards/config/locales/crowdin/sk.yml
+++ b/modules/boards/config/locales/crowdin/sk.yml
@@ -6,6 +6,10 @@ sk:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Nástenky"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Nástenka"
label_boards: "Nástenky"
@@ -34,6 +38,3 @@ sk:
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: 'Prejdite na vyššiu verziu'
diff --git a/modules/boards/config/locales/crowdin/sl.yml b/modules/boards/config/locales/crowdin/sl.yml
index 7b2137ac136..99f363d0251 100644
--- a/modules/boards/config/locales/crowdin/sl.yml
+++ b/modules/boards/config/locales/crowdin/sl.yml
@@ -6,6 +6,10 @@ sl:
permission_show_board_views: "Prikaži table"
permission_manage_board_views: "Upravljaj table"
project_module_board_view: "Table"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Tabla"
label_boards: "Table"
@@ -34,6 +38,3 @@ sl:
Tabla s samodejnimi stolpci za podprojekte. Če premaknete delovne pakete na druge sezname, boste ustrezno posodobili (pod)projekt.
subtasks: >
Tabla s samodejnimi stolpci za podelemente. Če premaknete delovne pakete na druge sezname, boste ustrezno posodobili nadrazred.
- 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: 'Nadgradi zdaj'
diff --git a/modules/boards/config/locales/crowdin/sr.yml b/modules/boards/config/locales/crowdin/sr.yml
index fb159df983d..136db9adf02 100644
--- a/modules/boards/config/locales/crowdin/sr.yml
+++ b/modules/boards/config/locales/crowdin/sr.yml
@@ -6,6 +6,10 @@ sr:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ sr:
Tabla sa automatizovanim kolonama za potprojekte. Prevlačenje radnog paketa na druge liste osvežava stanje potprojekta.
subtasks: >
Tabla sa automatizovanim kolonama za podelemente. Prevlačenje radnog paketa na druge liste osvežava stanje izvornog elementa.
- 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: 'Nadogradite'
diff --git a/modules/boards/config/locales/crowdin/sv.yml b/modules/boards/config/locales/crowdin/sv.yml
index 7cc177ed7a1..93b95399b01 100644
--- a/modules/boards/config/locales/crowdin/sv.yml
+++ b/modules/boards/config/locales/crowdin/sv.yml
@@ -6,6 +6,10 @@ sv:
permission_show_board_views: "Visa tavlor"
permission_manage_board_views: "Hantera tavlor"
project_module_board_view: "Tavlor"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Tavla"
label_boards: "Tavlor"
@@ -34,6 +38,3 @@ sv:
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: 'Uppgradera nu'
diff --git a/modules/boards/config/locales/crowdin/th.yml b/modules/boards/config/locales/crowdin/th.yml
index 53800234975..55054a19f2d 100644
--- a/modules/boards/config/locales/crowdin/th.yml
+++ b/modules/boards/config/locales/crowdin/th.yml
@@ -6,6 +6,10 @@ th:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ th:
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 6fd9ef37d1d..9e4cb60916e 100644
--- a/modules/boards/config/locales/crowdin/tr.yml
+++ b/modules/boards/config/locales/crowdin/tr.yml
@@ -6,6 +6,10 @@ tr:
permission_show_board_views: "Panoları görüntüleme"
permission_manage_board_views: "Panoları yönetme"
project_module_board_view: "Panolar"
+ ee:
+ upsale:
+ board_view:
+ description: 'İş akışlarınızı Panolar ile otomatikleştirmek ister misiniz? Gelişmiş panolar Enterprise eklentidir. Lütfen ücretli bir plana yükseltin.'
boards:
label_board: "Pano"
label_boards: "Panolar"
@@ -34,6 +38,3 @@ tr:
Alt projeler için otomatik sütunlara sahip pano. İş paketlerini diğer listelere sürüklemek üzere (alt)projeyi buna göre günceller.
subtasks: >
Alt öğeler için otomatik sütunlara sahip pano. İş paketlerini diğer listelere sürüklemek üzere üst öğeyi buna göre günceller.
- upsale:
- teaser_text: 'İş akışlarınızı Panolar ile otomatikleştirmek ister misiniz? Gelişmiş panolar Enterprise eklentidir. Lütfen ücretli bir plana yükseltin.'
- upgrade: 'Şimdi yükselt'
diff --git a/modules/boards/config/locales/crowdin/uk.yml b/modules/boards/config/locales/crowdin/uk.yml
index 4c64c76beb7..3eaeb45c2b1 100644
--- a/modules/boards/config/locales/crowdin/uk.yml
+++ b/modules/boards/config/locales/crowdin/uk.yml
@@ -6,6 +6,10 @@ uk:
permission_show_board_views: "Дивитись дошки"
permission_manage_board_views: "Керувати дошками"
project_module_board_view: "Дошки"
+ ee:
+ upsale:
+ board_view:
+ description: 'Хочете автоматизувати робочі процеси за допомогою Дощок? Поліпшені дошки – це доповнення версії Enterprise. Перейдіть на платний план.'
boards:
label_board: "Дошка"
label_boards: "Дошки"
@@ -34,6 +38,3 @@ uk:
Дошка з автоматизованими стовпцями для підпроєктів. Перетягування пакетів робіт в інші списки відповідно оновлює (під)проєкти.
subtasks: >
Дошка з автоматизованими стовпцями для піделементів. Перетягування пакетів робіт в інші списки відповідно оновлює батьківський елемент.
- upsale:
- teaser_text: 'Хочете автоматизувати робочі процеси за допомогою дощок? Поліпшені дошки – це доповнення версії Enterprise. Перейдіть на платний план.'
- upgrade: 'Перейти'
diff --git a/modules/boards/config/locales/crowdin/uz.yml b/modules/boards/config/locales/crowdin/uz.yml
index 38bb869af2b..65719985761 100644
--- a/modules/boards/config/locales/crowdin/uz.yml
+++ b/modules/boards/config/locales/crowdin/uz.yml
@@ -6,6 +6,10 @@ uz:
permission_show_board_views: "View boards"
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -34,6 +38,3 @@ uz:
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 60c21e162de..00e3da90907 100644
--- a/modules/boards/config/locales/crowdin/vi.yml
+++ b/modules/boards/config/locales/crowdin/vi.yml
@@ -6,6 +6,10 @@ vi:
permission_show_board_views: "Xem bảng"
permission_manage_board_views: "Quản lý bảng"
project_module_board_view: "Bảng"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Bảng"
label_boards: "Các bảng"
@@ -34,6 +38,3 @@ vi:
Bảng với các cột tự động cho các dự án con. Kéo các gói công việc vào các danh sách khác sẽ cập nhật dự án (con) tương ứng.
subtasks: >
Bảng với các cột tự động cho các phần tử phụ. Kéo các gói công việc vào các danh sách khác sẽ cập nhật phần tử cha tương ứng.
- upsale:
- teaser_text: 'Bạn có muốn tự động hóa quy trình làm việc của mình với các bảng không? Các bảng nâng cao là một tiện ích bổ sung Enterprise. Vui lòng nâng cấp lên gói trả phí.'
- upgrade: 'Nâng cấp ngay'
diff --git a/modules/boards/config/locales/crowdin/zh-CN.yml b/modules/boards/config/locales/crowdin/zh-CN.yml
index 5fed58cc397..e748ceabab3 100644
--- a/modules/boards/config/locales/crowdin/zh-CN.yml
+++ b/modules/boards/config/locales/crowdin/zh-CN.yml
@@ -6,6 +6,10 @@ zh-CN:
permission_show_board_views: "视图面板"
permission_manage_board_views: "管理面板"
project_module_board_view: "面板"
+ ee:
+ upsale:
+ board_view:
+ description: '是否希望使用面板自动化您的工作流?高级面板是企业版附加组件。请升级到付费计划。'
boards:
label_board: "面板"
label_boards: "面板"
@@ -34,6 +38,3 @@ zh-CN:
为子项目自动生成列的面板。将工作包拖动到其他列表会相应地更新(子)项目。
subtasks: >
为子元素自动生成列的面板。将工作包拖动到其他列表会相应地更新父项。
- upsale:
- teaser_text: '是否希望使用面板自动执行您的工作流?高级面板是企业版附加组件。请升级到付费计划。'
- upgrade: '立即升级'
diff --git a/modules/boards/config/locales/crowdin/zh-TW.yml b/modules/boards/config/locales/crowdin/zh-TW.yml
index 7c20ad5667a..a58a783ed7e 100644
--- a/modules/boards/config/locales/crowdin/zh-TW.yml
+++ b/modules/boards/config/locales/crowdin/zh-TW.yml
@@ -6,6 +6,10 @@ zh-TW:
permission_show_board_views: "檢視看板"
permission_manage_board_views: "管理看板"
project_module_board_view: "看板"
+ ee:
+ upsale:
+ board_view:
+ description: '想使用看板來自動化您的工作流程嗎?進階看板是企業版附加功能,請升級至付費方案以啟用此功能。'
boards:
label_board: "看板"
label_boards: "看板"
@@ -34,6 +38,3 @@ zh-TW:
具有子專案面板。將工作項目拖動到其他列表會更新(子)專案。
subtasks: >
具有子元素自動列的看板。將工作項目移動到其他列表會相應地更新主要工作項目。
- upsale:
- teaser_text: '你想用看板來自動化你的工作流程嗎?高級看板是一項企業功能。請升級到付費計劃。'
- upgrade: '立即升級'
diff --git a/modules/boards/config/locales/en.yml b/modules/boards/config/locales/en.yml
index 02514fafb78..2412c528976 100644
--- a/modules/boards/config/locales/en.yml
+++ b/modules/boards/config/locales/en.yml
@@ -8,6 +8,10 @@ en:
permission_manage_board_views: "Manage boards"
project_module_board_view: "Boards"
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
boards:
label_board: "Board"
label_boards: "Boards"
@@ -41,7 +45,3 @@ en:
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/js-en.yml b/modules/boards/config/locales/js-en.yml
index 3c3ad9fbf2d..91960a988d4 100644
--- a/modules/boards/config/locales/js-en.yml
+++ b/modules/boards/config/locales/js-en.yml
@@ -1,15 +1,15 @@
# English strings go here
en:
+ ee:
+ upsale:
+ board_view:
+ description: 'Would you like to automate your workflows with Boards? Advanced boards are an Enterprise add-on. Please upgrade to a paid plan.'
js:
boards:
create_new: 'Create new board'
label_unnamed_board: 'Unnamed board'
label_unnamed_list: 'Unnamed list'
label_board_type: 'Board type'
-
- 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'
lists:
delete: 'Delete list'
diff --git a/modules/boards/spec/features/boards_global_create_spec.rb b/modules/boards/spec/features/boards_global_create_spec.rb
index 8b51a1df3e8..3bdcc098d23 100644
--- a/modules/boards/spec/features/boards_global_create_spec.rb
+++ b/modules/boards/spec/features/boards_global_create_spec.rb
@@ -6,7 +6,6 @@ require_relative "support/board_new_page"
RSpec.describe "Boards",
"Creating a view from a Global Context",
:js,
- :selenium,
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]) }
diff --git a/modules/boards/spec/features/support/board_page.rb b/modules/boards/spec/features/support/board_page.rb
index 84043b58388..d742452f088 100644
--- a/modules/boards/spec/features/support/board_page.rb
+++ b/modules/boards/spec/features/support/board_page.rb
@@ -217,6 +217,7 @@ module Pages
def save
page.find(".editable-toolbar-title--save").click
+ wait_for_lists_reload
expect_and_dismiss_toaster message: "Successful update."
end
diff --git a/modules/budgets/app/controllers/budgets_controller.rb b/modules/budgets/app/controllers/budgets_controller.rb
index f820b9b9fd8..21b263e90ea 100644
--- a/modules/budgets/app/controllers/budgets_controller.rb
+++ b/modules/budgets/app/controllers/budgets_controller.rb
@@ -199,8 +199,6 @@ class BudgetsController < ApplicationController
# This function comes directly from issues_controller.rb (Redmine 0.8.4)
@budget = Budget.includes(:project, :author).find_by(id: params[:id])
@project = @budget.project if @budget
- rescue ActiveRecord::RecordNotFound
- render_404
end
def find_budgets
@@ -216,14 +214,10 @@ class BudgetsController < ApplicationController
# TODO: let users bulk edit/move/destroy budgets from different projects
render_error "Can not bulk edit/move/destroy cost objects from different projects" and return false
end
- rescue ActiveRecord::RecordNotFound
- render_404
end
def find_optional_project
@project = Project.find(params[:project_id]) if params[:project_id].present?
- rescue ActiveRecord::RecordNotFound
- render_404
end
def render_item_as_json(element_id, costs, unit, project, permission)
diff --git a/modules/budgets/config/locales/crowdin/cs.yml b/modules/budgets/config/locales/crowdin/cs.yml
index 72dd2ee8925..231267126ec 100644
--- a/modules/budgets/config/locales/crowdin/cs.yml
+++ b/modules/budgets/config/locales/crowdin/cs.yml
@@ -27,7 +27,7 @@ cs:
budget:
author: "Autor"
available: "Dostupné"
- budget: "Plánované"
+ budget: "Rozpočet"
budget_ratio: "Stráveno (poměr)"
description: "Popis"
spent: "Strávený čas"
diff --git a/modules/calendar/app/controllers/calendar/calendars_controller.rb b/modules/calendar/app/controllers/calendar/calendars_controller.rb
index 373cb2bec67..4d34662cece 100644
--- a/modules/calendar/app/controllers/calendar/calendars_controller.rb
+++ b/modules/calendar/app/controllers/calendar/calendars_controller.rb
@@ -106,8 +106,6 @@ module ::Calendar
@view = Query
.visible(current_user)
.find(params[:id])
- rescue ActiveRecord::RecordNotFound
- render_404
end
end
end
diff --git a/modules/calendar/app/controllers/calendar/ical_controller.rb b/modules/calendar/app/controllers/calendar/ical_controller.rb
index 2ebf12661bd..a250c2d01cd 100644
--- a/modules/calendar/app/controllers/calendar/ical_controller.rb
+++ b/modules/calendar/app/controllers/calendar/ical_controller.rb
@@ -33,15 +33,10 @@ module ::Calendar
no_authorization_required! :show
def show
- begin
- call = ::Calendar::ICalResponseService.new.call(
- ical_token_string: params[:ical_token],
- query_id: params[:id]
- )
- rescue ActiveRecord::RecordNotFound
- render_404
- return
- end
+ call = ::Calendar::ICalResponseService.new.call(
+ ical_token_string: params[:ical_token],
+ query_id: params[:id]
+ )
if call.present? && call.success?
send_data call.result, filename: "openproject_calendar_#{DateTime.now.to_i}.ics"
diff --git a/modules/calendar/spec/features/calendar_widget_spec.rb b/modules/calendar/spec/features/calendar_widget_spec.rb
index 99c2731a422..8957ade0dd3 100644
--- a/modules/calendar/spec/features/calendar_widget_spec.rb
+++ b/modules/calendar/spec/features/calendar_widget_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe "Calendar Widget", :js, with_settings: { start_of_week: 1 } do
due_date: Time.zone.today.beginning_of_week.next_occurring(:thursday))
end
shared_let(:meeting) do
- create(:structured_meeting, title: "Weekly", project:, start_time: Time.zone.tomorrow + 10.hours)
+ create(:meeting, title: "Weekly", project:, start_time: Time.zone.tomorrow + 10.hours)
end
let(:overview_page) do
diff --git a/modules/costs/app/components/time_entries/entry_dialog_component.html.erb b/modules/costs/app/components/time_entries/entry_dialog_component.html.erb
index 836bbdb7192..6dbcc8c26b9 100644
--- a/modules/costs/app/components/time_entries/entry_dialog_component.html.erb
+++ b/modules/costs/app/components/time_entries/entry_dialog_component.html.erb
@@ -15,7 +15,7 @@
<% if time_entry.persisted? %>
<% flex.with_column do %>
<%= render(
- Primer::ButtonComponent.new(
+ Primer::Beta::Button.new(
scheme: :danger,
test_selector: "destroy-time-entry-button",
data: {
@@ -32,10 +32,10 @@
<% flex.with_column(classes: "time-entry-button-push") %>
<% flex.with_column do %>
<%= render(
- Primer::ButtonComponent.new(data: { "close-dialog-id": "time-entry-dialog" })
+ Primer::Beta::Button.new(data: { "close-dialog-id": "time-entry-dialog" })
) { I18n.t("button_cancel") } %>
<%= render(
- Primer::ButtonComponent.new(
+ Primer::Beta::Button.new(
scheme: :primary,
type: :submit,
test_selector: "create-time-entry-button",
diff --git a/modules/costs/app/controllers/admin/cost_types_controller.rb b/modules/costs/app/controllers/admin/cost_types_controller.rb
index 0cb40984649..cc6cf118847 100644
--- a/modules/costs/app/controllers/admin/cost_types_controller.rb
+++ b/modules/costs/app/controllers/admin/cost_types_controller.rb
@@ -151,8 +151,6 @@ module Admin
def find_cost_type
@cost_type = CostType.find(params[:id])
- rescue ActiveRecord::RecordNotFound
- render_404
end
def show_local_breadcrumb
diff --git a/modules/costs/app/controllers/costlog_controller.rb b/modules/costs/app/controllers/costlog_controller.rb
index b1811eb2420..531664914d3 100644
--- a/modules/costs/app/controllers/costlog_controller.rb
+++ b/modules/costs/app/controllers/costlog_controller.rb
@@ -110,8 +110,6 @@ class CostlogController < ApplicationController
render_404
false
end
- rescue ActiveRecord::RecordNotFound
- render_404
end
def find_associated_objects
diff --git a/modules/costs/app/controllers/hourly_rates_controller.rb b/modules/costs/app/controllers/hourly_rates_controller.rb
index 0b46db90a00..ae75c8a5f40 100644
--- a/modules/costs/app/controllers/hourly_rates_controller.rb
+++ b/modules/costs/app/controllers/hourly_rates_controller.rb
@@ -172,19 +172,13 @@ class HourlyRatesController < ApplicationController
def find_project
@project = Project.find(params[:project_id])
- rescue ActiveRecord::RecordNotFound
- render_404
end
def find_optional_project
@project = params[:project_id].blank? ? nil : Project.find(params[:project_id])
- rescue ActiveRecord::RecordNotFound
- render_404
end
def find_user
@user = params[:id] ? User.find(params[:id]) : User.current
- rescue ActiveRecord::RecordNotFound
- render_404
end
end
diff --git a/modules/costs/app/models/time_entry.rb b/modules/costs/app/models/time_entry.rb
index a665bd6c1f4..c0ab8b203b5 100644
--- a/modules/costs/app/models/time_entry.rb
+++ b/modules/costs/app/models/time_entry.rb
@@ -168,15 +168,14 @@ class TimeEntry < ApplicationRecord
end
class << self
- def can_track_start_and_end_time?(_project: nil)
- OpenProject::FeatureDecisions.track_start_and_end_times_for_time_entries_active? &&
- Setting.allow_tracking_start_and_end_times?
- # TODO: Add project check when we have decided if we also want a project specific flag
+ def can_track_start_and_end_time?
+ Setting.allow_tracking_start_and_end_times?
end
- def must_track_start_and_end_time?(_project: nil)
- can_track_start_and_end_time? && Setting.enforce_tracking_start_and_end_times?
- # TODO: Add project check when we have decided if we also want a project specific flag
+ def must_track_start_and_end_time?
+ EnterpriseToken.allows_to?(:time_entry_time_restrictions) &&
+ can_track_start_and_end_time? &&
+ Setting.enforce_tracking_start_and_end_times?
end
end
diff --git a/modules/costs/app/views/admin/costs_settings/show.html.erb b/modules/costs/app/views/admin/costs_settings/show.html.erb
index 6c133299d25..a16e4bc92a4 100644
--- a/modules/costs/app/views/admin/costs_settings/show.html.erb
+++ b/modules/costs/app/views/admin/costs_settings/show.html.erb
@@ -44,30 +44,32 @@ See COPYRIGHT and LICENSE files for more details.
<%=
render(Primer::Alpha::TabPanels.new(label: I18n.t(:project_module_costs))) do |component|
- if OpenProject::FeatureDecisions.track_start_and_end_times_for_time_entries_active?
- component.with_tab(selected: true, id: "tab-time") do |tab|
- tab.with_text { I18n.t(:label_time) }
- tab.with_panel do
- admin_settings_primer_form_with(scope: :settings, action: :update, method: :patch) do |time_form|
- render_inline_settings_form(time_form) do |form|
- form.html_content do
- render(Primer::Beta::Subhead.new(hide_border: true)) do |subhead|
- subhead.with_heading(size: :medium) { I18n.t(:label_mandatory_fields) }
- subhead.with_description { I18n.t(:description_time_settings) }
- end
+ component.with_tab(selected: true, id: "tab-time") do |tab|
+ tab.with_text { I18n.t(:label_time) }
+ tab.with_panel do
+ admin_settings_primer_form_with(scope: :settings, action: :update, method: :patch) do |time_form|
+ render_inline_settings_form(time_form) do |form|
+ form.html_content do
+ render(Primer::Beta::Subhead.new(hide_border: true)) do |subhead|
+ subhead.with_heading(size: :medium) { I18n.t(:label_mandatory_fields) }
+ subhead.with_description { I18n.t(:description_time_settings) }
end
-
- form.check_box(name: :allow_tracking_start_and_end_times)
- form.check_box(name: :enforce_tracking_start_and_end_times)
-
- form.submit
end
+
+ form.check_box(name: :allow_tracking_start_and_end_times)
+
+ form.html_content do
+ render(::EnterpriseEdition::BannerComponent.new(:time_entry_time_restrictions, dismissable: false))
+ end
+ form.check_box(name: :enforce_tracking_start_and_end_times, disabled: !EnterpriseToken.allows_to?(:time_entry_time_restrictions))
+
+ form.submit
end
end
end
end
- component.with_tab(id: "tab-costs", selected: !OpenProject::FeatureDecisions.track_start_and_end_times_for_time_entries_active?) do |tab|
+ component.with_tab(id: "tab-costs") do |tab|
tab.with_text { I18n.t(:label_costs) }
tab.with_panel do
admin_settings_primer_form_with(scope: :settings, action: :update, method: :patch) do |time_form|
diff --git a/modules/costs/config/locales/crowdin/af.yml b/modules/costs/config/locales/crowdin/af.yml
index 094de9ea149..3fd13cc8be6 100644
--- a/modules/costs/config/locales/crowdin/af.yml
+++ b/modules/costs/config/locales/crowdin/af.yml
@@ -185,3 +185,9 @@ af:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/ar.yml b/modules/costs/config/locales/crowdin/ar.yml
index 6dbc6fd6604..44f034d3092 100644
--- a/modules/costs/config/locales/crowdin/ar.yml
+++ b/modules/costs/config/locales/crowdin/ar.yml
@@ -201,3 +201,9 @@ ar:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/az.yml b/modules/costs/config/locales/crowdin/az.yml
index dfcfe34362c..142c1368cc7 100644
--- a/modules/costs/config/locales/crowdin/az.yml
+++ b/modules/costs/config/locales/crowdin/az.yml
@@ -185,3 +185,9 @@ az:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/be.yml b/modules/costs/config/locales/crowdin/be.yml
index 449c5141c49..554a8e9a1bf 100644
--- a/modules/costs/config/locales/crowdin/be.yml
+++ b/modules/costs/config/locales/crowdin/be.yml
@@ -193,3 +193,9 @@ be:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/bg.yml b/modules/costs/config/locales/crowdin/bg.yml
index f05a8f147b3..c63a7c4c62f 100644
--- a/modules/costs/config/locales/crowdin/bg.yml
+++ b/modules/costs/config/locales/crowdin/bg.yml
@@ -185,3 +185,9 @@ bg:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/ca.yml b/modules/costs/config/locales/crowdin/ca.yml
index 7701b550dd4..df7c293f01a 100644
--- a/modules/costs/config/locales/crowdin/ca.yml
+++ b/modules/costs/config/locales/crowdin/ca.yml
@@ -185,3 +185,9 @@ ca:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/ckb-IR.yml b/modules/costs/config/locales/crowdin/ckb-IR.yml
index 3e27a398b9e..bfb0511d80a 100644
--- a/modules/costs/config/locales/crowdin/ckb-IR.yml
+++ b/modules/costs/config/locales/crowdin/ckb-IR.yml
@@ -185,3 +185,9 @@ ckb-IR:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/cs.yml b/modules/costs/config/locales/crowdin/cs.yml
index b5fa35bf427..5dfbb990aaf 100644
--- a/modules/costs/config/locales/crowdin/cs.yml
+++ b/modules/costs/config/locales/crowdin/cs.yml
@@ -193,3 +193,9 @@ cs:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/da.yml b/modules/costs/config/locales/crowdin/da.yml
index d212de64340..030d1eb6d5d 100644
--- a/modules/costs/config/locales/crowdin/da.yml
+++ b/modules/costs/config/locales/crowdin/da.yml
@@ -185,3 +185,9 @@ da:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/de.yml b/modules/costs/config/locales/crowdin/de.yml
index 94b0bf6adc9..cb4ffb92804 100644
--- a/modules/costs/config/locales/crowdin/de.yml
+++ b/modules/costs/config/locales/crowdin/de.yml
@@ -185,3 +185,9 @@ de:
errors:
validation:
start_time_different_date: "Der Datumsteil von startTime (%{start_time}) muss mit dem Datum von spentOn (%{spent_on}) übereinstimmen."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/el.yml b/modules/costs/config/locales/crowdin/el.yml
index 2edf6b7d9d6..f68e8af48d1 100644
--- a/modules/costs/config/locales/crowdin/el.yml
+++ b/modules/costs/config/locales/crowdin/el.yml
@@ -185,3 +185,9 @@ el:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/eo.yml b/modules/costs/config/locales/crowdin/eo.yml
index 83822c67519..5556eade11d 100644
--- a/modules/costs/config/locales/crowdin/eo.yml
+++ b/modules/costs/config/locales/crowdin/eo.yml
@@ -185,3 +185,9 @@ eo:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/es.yml b/modules/costs/config/locales/crowdin/es.yml
index 57f47f89748..6052127ffa3 100644
--- a/modules/costs/config/locales/crowdin/es.yml
+++ b/modules/costs/config/locales/crowdin/es.yml
@@ -185,3 +185,9 @@ es:
errors:
validation:
start_time_different_date: "La parte de fecha de startTime (%{start_time}) debe ser la misma que la fecha de spentOn (%{spent_on})."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/et.yml b/modules/costs/config/locales/crowdin/et.yml
index 4df9fc169ec..442e62d2cce 100644
--- a/modules/costs/config/locales/crowdin/et.yml
+++ b/modules/costs/config/locales/crowdin/et.yml
@@ -185,3 +185,9 @@ et:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/eu.yml b/modules/costs/config/locales/crowdin/eu.yml
index e21d16f836f..c1f1c50f343 100644
--- a/modules/costs/config/locales/crowdin/eu.yml
+++ b/modules/costs/config/locales/crowdin/eu.yml
@@ -185,3 +185,9 @@ eu:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/fa.yml b/modules/costs/config/locales/crowdin/fa.yml
index ab22f011f4e..009fa31a441 100644
--- a/modules/costs/config/locales/crowdin/fa.yml
+++ b/modules/costs/config/locales/crowdin/fa.yml
@@ -185,3 +185,9 @@ fa:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/fi.yml b/modules/costs/config/locales/crowdin/fi.yml
index bc928679d8d..56f9ec32253 100644
--- a/modules/costs/config/locales/crowdin/fi.yml
+++ b/modules/costs/config/locales/crowdin/fi.yml
@@ -185,3 +185,9 @@ fi:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/fil.yml b/modules/costs/config/locales/crowdin/fil.yml
index e19f9a0c070..f896eb2b609 100644
--- a/modules/costs/config/locales/crowdin/fil.yml
+++ b/modules/costs/config/locales/crowdin/fil.yml
@@ -185,3 +185,9 @@ fil:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/fr.yml b/modules/costs/config/locales/crowdin/fr.yml
index 2c12937d870..e54214c22a8 100644
--- a/modules/costs/config/locales/crowdin/fr.yml
+++ b/modules/costs/config/locales/crowdin/fr.yml
@@ -185,3 +185,9 @@ fr:
errors:
validation:
start_time_different_date: "La partie date de startTime (%{start_time}) doit être identique à la date de spentOn (%{spent_on})."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/he.yml b/modules/costs/config/locales/crowdin/he.yml
index fb3f9e3412a..acbd3095b29 100644
--- a/modules/costs/config/locales/crowdin/he.yml
+++ b/modules/costs/config/locales/crowdin/he.yml
@@ -193,3 +193,9 @@ he:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/hi.yml b/modules/costs/config/locales/crowdin/hi.yml
index 1887db3be39..f6a19341abc 100644
--- a/modules/costs/config/locales/crowdin/hi.yml
+++ b/modules/costs/config/locales/crowdin/hi.yml
@@ -185,3 +185,9 @@ hi:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/hr.yml b/modules/costs/config/locales/crowdin/hr.yml
index ca735033b2e..3330b757bad 100644
--- a/modules/costs/config/locales/crowdin/hr.yml
+++ b/modules/costs/config/locales/crowdin/hr.yml
@@ -189,3 +189,9 @@ hr:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/hu.yml b/modules/costs/config/locales/crowdin/hu.yml
index 925faa1be7f..33f1102b79c 100644
--- a/modules/costs/config/locales/crowdin/hu.yml
+++ b/modules/costs/config/locales/crowdin/hu.yml
@@ -185,3 +185,9 @@ hu:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/id.yml b/modules/costs/config/locales/crowdin/id.yml
index bd0d9f7c850..398d69e3b4a 100644
--- a/modules/costs/config/locales/crowdin/id.yml
+++ b/modules/costs/config/locales/crowdin/id.yml
@@ -181,3 +181,9 @@ id:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/it.yml b/modules/costs/config/locales/crowdin/it.yml
index 00464fbafc2..3c636d84918 100644
--- a/modules/costs/config/locales/crowdin/it.yml
+++ b/modules/costs/config/locales/crowdin/it.yml
@@ -185,3 +185,9 @@ it:
errors:
validation:
start_time_different_date: "La parte data di startTime (%{start_time}) deve essere uguale alla data di spentOn (%{spent_on})."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/ja.yml b/modules/costs/config/locales/crowdin/ja.yml
index 2e4e94bdc64..ac91b307e84 100644
--- a/modules/costs/config/locales/crowdin/ja.yml
+++ b/modules/costs/config/locales/crowdin/ja.yml
@@ -181,3 +181,9 @@ ja:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/ka.yml b/modules/costs/config/locales/crowdin/ka.yml
index 2860908e588..fbda354f666 100644
--- a/modules/costs/config/locales/crowdin/ka.yml
+++ b/modules/costs/config/locales/crowdin/ka.yml
@@ -185,3 +185,9 @@ ka:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/kk.yml b/modules/costs/config/locales/crowdin/kk.yml
index c8094582d28..946bbc77deb 100644
--- a/modules/costs/config/locales/crowdin/kk.yml
+++ b/modules/costs/config/locales/crowdin/kk.yml
@@ -185,3 +185,9 @@ kk:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/ko.yml b/modules/costs/config/locales/crowdin/ko.yml
index 616d1c9e338..80e8d3481ea 100644
--- a/modules/costs/config/locales/crowdin/ko.yml
+++ b/modules/costs/config/locales/crowdin/ko.yml
@@ -181,3 +181,9 @@ ko:
errors:
validation:
start_time_different_date: "startTime(%{start_time})의 날짜 부분은 spentOn(%{spent_on}) 날짜와 동일해야 합니다."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/lt.yml b/modules/costs/config/locales/crowdin/lt.yml
index 530218141e8..33d9ea88974 100644
--- a/modules/costs/config/locales/crowdin/lt.yml
+++ b/modules/costs/config/locales/crowdin/lt.yml
@@ -193,3 +193,9 @@ lt:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/lv.yml b/modules/costs/config/locales/crowdin/lv.yml
index b0f1b033e45..79c3aadaffa 100644
--- a/modules/costs/config/locales/crowdin/lv.yml
+++ b/modules/costs/config/locales/crowdin/lv.yml
@@ -189,3 +189,9 @@ lv:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/mn.yml b/modules/costs/config/locales/crowdin/mn.yml
index 44de4796c26..877b889beba 100644
--- a/modules/costs/config/locales/crowdin/mn.yml
+++ b/modules/costs/config/locales/crowdin/mn.yml
@@ -185,3 +185,9 @@ mn:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/ms.yml b/modules/costs/config/locales/crowdin/ms.yml
index e579572bc85..5d8917ba075 100644
--- a/modules/costs/config/locales/crowdin/ms.yml
+++ b/modules/costs/config/locales/crowdin/ms.yml
@@ -181,3 +181,9 @@ ms:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/ne.yml b/modules/costs/config/locales/crowdin/ne.yml
index b2136f95dd6..6f475678fda 100644
--- a/modules/costs/config/locales/crowdin/ne.yml
+++ b/modules/costs/config/locales/crowdin/ne.yml
@@ -185,3 +185,9 @@ ne:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/nl.yml b/modules/costs/config/locales/crowdin/nl.yml
index 995b1010331..1d0c75b4de1 100644
--- a/modules/costs/config/locales/crowdin/nl.yml
+++ b/modules/costs/config/locales/crowdin/nl.yml
@@ -185,3 +185,9 @@ nl:
errors:
validation:
start_time_different_date: "Het datumgedeelte van start tijd (%{start_time}) moet hetzelfde zijn als de uitgegeven op (%{spent_on}) datum."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/no.yml b/modules/costs/config/locales/crowdin/no.yml
index 58f6fba308d..22e80a91012 100644
--- a/modules/costs/config/locales/crowdin/no.yml
+++ b/modules/costs/config/locales/crowdin/no.yml
@@ -185,3 +185,9 @@
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/pl.yml b/modules/costs/config/locales/crowdin/pl.yml
index ab90adfbc03..97b450f84b3 100644
--- a/modules/costs/config/locales/crowdin/pl.yml
+++ b/modules/costs/config/locales/crowdin/pl.yml
@@ -193,3 +193,9 @@ pl:
errors:
validation:
start_time_different_date: "Część daty startTime (%{start_time}) musi być taka sama jak data spentOn (%{spent_on})."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/pt-BR.yml b/modules/costs/config/locales/crowdin/pt-BR.yml
index 93a2b3d078f..d2be29199c4 100644
--- a/modules/costs/config/locales/crowdin/pt-BR.yml
+++ b/modules/costs/config/locales/crowdin/pt-BR.yml
@@ -185,3 +185,9 @@ pt-BR:
errors:
validation:
start_time_different_date: "A data de início (%{start_time}) deve ser a mesma da data de registro (%{spent_on})."
+ ee:
+ features:
+ time_entry_time_restrictions: Exigir o rastreamento exato de tempo
+ upsale:
+ time_entry_time_restrictions:
+ description: "Melhore o rastreamento do seu tempo exigindo a hora exata de início e término do registro."
diff --git a/modules/costs/config/locales/crowdin/pt-PT.yml b/modules/costs/config/locales/crowdin/pt-PT.yml
index 2e308790988..1dc8201a6f1 100644
--- a/modules/costs/config/locales/crowdin/pt-PT.yml
+++ b/modules/costs/config/locales/crowdin/pt-PT.yml
@@ -185,3 +185,9 @@ pt-PT:
errors:
validation:
start_time_different_date: "A parte da data de startTime (%{start_time}) tem de ser a mesma que a data de spentOn (%{spent_on})."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/ro.yml b/modules/costs/config/locales/crowdin/ro.yml
index 2c5127bdd2d..76df95813a7 100644
--- a/modules/costs/config/locales/crowdin/ro.yml
+++ b/modules/costs/config/locales/crowdin/ro.yml
@@ -189,3 +189,9 @@ ro:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/ru.yml b/modules/costs/config/locales/crowdin/ru.yml
index 551ede090cc..98c1346e8f2 100644
--- a/modules/costs/config/locales/crowdin/ru.yml
+++ b/modules/costs/config/locales/crowdin/ru.yml
@@ -193,3 +193,9 @@ ru:
errors:
validation:
start_time_different_date: "Дата startTime (%{start_time}) должна быть такой же, как дата spentOn (%{spent_on})."
+ ee:
+ features:
+ time_entry_time_restrictions: Требует точного отслеживания времени
+ upsale:
+ time_entry_time_restrictions:
+ description: "Улучшите отслеживание времени, требуя точного времени начала и окончания при записи трудозатрат."
diff --git a/modules/costs/config/locales/crowdin/rw.yml b/modules/costs/config/locales/crowdin/rw.yml
index 79be38f55d1..3d2a3008372 100644
--- a/modules/costs/config/locales/crowdin/rw.yml
+++ b/modules/costs/config/locales/crowdin/rw.yml
@@ -185,3 +185,9 @@ rw:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/si.yml b/modules/costs/config/locales/crowdin/si.yml
index 9585b7b2665..e86e7249691 100644
--- a/modules/costs/config/locales/crowdin/si.yml
+++ b/modules/costs/config/locales/crowdin/si.yml
@@ -185,3 +185,9 @@ si:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/sk.yml b/modules/costs/config/locales/crowdin/sk.yml
index 2c742fc3933..1bf5ef7db23 100644
--- a/modules/costs/config/locales/crowdin/sk.yml
+++ b/modules/costs/config/locales/crowdin/sk.yml
@@ -193,3 +193,9 @@ sk:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/sl.yml b/modules/costs/config/locales/crowdin/sl.yml
index d4a9c21edfc..20a2c8840f7 100644
--- a/modules/costs/config/locales/crowdin/sl.yml
+++ b/modules/costs/config/locales/crowdin/sl.yml
@@ -193,3 +193,9 @@ sl:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/sr.yml b/modules/costs/config/locales/crowdin/sr.yml
index a2af1d04acd..ac8b5f65c9b 100644
--- a/modules/costs/config/locales/crowdin/sr.yml
+++ b/modules/costs/config/locales/crowdin/sr.yml
@@ -189,3 +189,9 @@ sr:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/sv.yml b/modules/costs/config/locales/crowdin/sv.yml
index e57c5915ddd..815f8d2ba7f 100644
--- a/modules/costs/config/locales/crowdin/sv.yml
+++ b/modules/costs/config/locales/crowdin/sv.yml
@@ -185,3 +185,9 @@ sv:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/th.yml b/modules/costs/config/locales/crowdin/th.yml
index 06d2ea7d1ce..5677ed2b149 100644
--- a/modules/costs/config/locales/crowdin/th.yml
+++ b/modules/costs/config/locales/crowdin/th.yml
@@ -181,3 +181,9 @@ th:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/tr.yml b/modules/costs/config/locales/crowdin/tr.yml
index cc6627d01e0..d1f56cc321c 100644
--- a/modules/costs/config/locales/crowdin/tr.yml
+++ b/modules/costs/config/locales/crowdin/tr.yml
@@ -185,3 +185,9 @@ tr:
errors:
validation:
start_time_different_date: "StartTime (%{start_time}) öğesinin tarih kısmı, spentOn (%{spent_on}) tarihi ile aynı olmalıdır."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/uk.yml b/modules/costs/config/locales/crowdin/uk.yml
index 0cc5fc0180f..cb6b6ae399c 100644
--- a/modules/costs/config/locales/crowdin/uk.yml
+++ b/modules/costs/config/locales/crowdin/uk.yml
@@ -193,3 +193,9 @@ uk:
errors:
validation:
start_time_different_date: "Частина дати в значенні startTime (%{start_time}) має збігатися зі значенням дати spentOn (%{spent_on})."
+ ee:
+ features:
+ time_entry_time_restrictions: Вимагати точного відстеження часу
+ upsale:
+ time_entry_time_restrictions:
+ description: "Удоскональте відстеження часу, вимагаючи введення точного часу початку й завершення робіт."
diff --git a/modules/costs/config/locales/crowdin/uz.yml b/modules/costs/config/locales/crowdin/uz.yml
index 7ae9b042a07..022b94079d3 100644
--- a/modules/costs/config/locales/crowdin/uz.yml
+++ b/modules/costs/config/locales/crowdin/uz.yml
@@ -185,3 +185,9 @@ uz:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/vi.yml b/modules/costs/config/locales/crowdin/vi.yml
index 4194e038b22..057918a6211 100644
--- a/modules/costs/config/locales/crowdin/vi.yml
+++ b/modules/costs/config/locales/crowdin/vi.yml
@@ -181,3 +181,9 @@ vi:
errors:
validation:
start_time_different_date: "Phần ngày của thời gian bắt đầu (%{start_time}) phải giống với ngày tiêu phí (%{spent_on})."
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/config/locales/crowdin/zh-CN.yml b/modules/costs/config/locales/crowdin/zh-CN.yml
index d4553406fbf..8604a45d06d 100644
--- a/modules/costs/config/locales/crowdin/zh-CN.yml
+++ b/modules/costs/config/locales/crowdin/zh-CN.yml
@@ -181,3 +181,9 @@ zh-CN:
errors:
validation:
start_time_different_date: "开始时间 (%{start_time}) 的日期部分必须与 支出日期(%{spent_on})相同。"
+ ee:
+ features:
+ time_entry_time_restrictions: 需要准确的时间跟踪
+ upsale:
+ time_entry_time_restrictions:
+ description: "通过要求在记录时间时提供确切的开始和结束时间来改进你的时间跟踪。"
diff --git a/modules/costs/config/locales/crowdin/zh-TW.yml b/modules/costs/config/locales/crowdin/zh-TW.yml
index a48ee261b83..af60d890516 100644
--- a/modules/costs/config/locales/crowdin/zh-TW.yml
+++ b/modules/costs/config/locales/crowdin/zh-TW.yml
@@ -181,3 +181,9 @@ zh-TW:
errors:
validation:
start_time_different_date: "startTime (%{start_time}) 的日期部分必須與 spentOn (%{spent_on}) 的日期相同。"
+ ee:
+ features:
+ time_entry_time_restrictions: 需精確追蹤時間
+ upsale:
+ time_entry_time_restrictions:
+ description: "提升時間追蹤效率!記錄工時時輸入精確的開始與結束時間。"
diff --git a/modules/costs/config/locales/en.yml b/modules/costs/config/locales/en.yml
index a8c08763869..dd77b2529e8 100644
--- a/modules/costs/config/locales/en.yml
+++ b/modules/costs/config/locales/en.yml
@@ -119,7 +119,6 @@ en:
description_costs_settings: "Define the desired format for the costs in all projects."
description_time_settings: "Define which fields are mandatory to fill when logging time in all projects."
-
group_by_others: "not in any group"
label_between: "between"
@@ -208,3 +207,10 @@ en:
errors:
validation:
start_time_different_date: "Date part of startTime (%{start_time}) must be the same as the spentOn (%{spent_on}) date."
+
+ ee:
+ features:
+ time_entry_time_restrictions: Require exact time tracking
+ upsale:
+ time_entry_time_restrictions:
+ description: "Improve your time tracking by requiring exact start and finish times when logging time."
diff --git a/modules/costs/lib/costs/engine.rb b/modules/costs/lib/costs/engine.rb
index e0268b78608..32538e828fe 100644
--- a/modules/costs/lib/costs/engine.rb
+++ b/modules/costs/lib/costs/engine.rb
@@ -153,11 +153,6 @@ module Costs
::Settings::Definition.add "enforce_tracking_start_and_end_times", default: false, format: :boolean
end
- initializer "costs.feature_decisions" do
- OpenProject::FeatureDecisions.add :track_start_and_end_times_for_time_entries,
- description: "Allows admins to enable tracking start and end times for time entries"
- end
-
activity_provider :time_entries, class_name: "Activities::TimeEntryActivityProvider", default: false
patches %i[Project User PermittedParams]
diff --git a/modules/costs/spec/features/time_entry_dialog_spec.rb b/modules/costs/spec/features/time_entry_dialog_spec.rb
index a295e01bb6c..467563f38cc 100644
--- a/modules/costs/spec/features/time_entry_dialog_spec.rb
+++ b/modules/costs/spec/features/time_entry_dialog_spec.rb
@@ -30,7 +30,7 @@
require_relative "../spec_helper"
-RSpec.describe "time entry dialog", :js, with_flag: :track_start_and_end_times_for_time_entries do
+RSpec.describe "time entry dialog", :js do
include Redmine::I18n
shared_let(:project) { create(:project_with_types) }
@@ -80,7 +80,11 @@ RSpec.describe "time entry dialog", :js, with_flag: :track_start_and_end_times_f
end
context "when start and end time is enforced",
- with_settings: { allow_tracking_start_and_end_times: true, enforce_tracking_start_and_end_times: true } do
+ with_ee: %i[time_entry_time_restrictions],
+ with_settings: {
+ allow_tracking_start_and_end_times: true,
+ enforce_tracking_start_and_end_times: true
+ } do
it "shows fields to track start and end times" do
time_logging_modal.shows_field("start_time", true)
time_logging_modal.requires_field("start_time")
diff --git a/modules/costs/spec/models/time_entry_spec.rb b/modules/costs/spec/models/time_entry_spec.rb
index 8ef9fad7413..b03088352b8 100644
--- a/modules/costs/spec/models/time_entry_spec.rb
+++ b/modules/costs/spec/models/time_entry_spec.rb
@@ -414,24 +414,12 @@ RSpec.describe TimeEntry do
end
describe ".can_track_start_and_end_time?" do
- context "with the feature flag enabled", with_flag: { track_start_and_end_times_for_time_entries: true } do
- context "with the setting enabled", with_settings: { allow_tracking_start_and_end_times: true } do
- it { expect(described_class).to be_can_track_start_and_end_time }
- end
-
- context "with the setting disabled", with_settings: { allow_tracking_start_and_end_times: false } do
- it { expect(described_class).not_to be_can_track_start_and_end_time }
- end
+ context "with the setting enabled", with_settings: { allow_tracking_start_and_end_times: true } do
+ it { expect(described_class).to be_can_track_start_and_end_time }
end
- context "with the feature flag disabled", with_flag: { track_start_and_end_times_for_time_entries: false } do
- context "with the setting enabled", with_settings: { allow_tracking_start_and_end_times: true } do
- it { expect(described_class).not_to be_can_track_start_and_end_time }
- end
-
- context "with the setting disabled", with_settings: { allow_tracking_start_and_end_times: false } do
- it { expect(described_class).not_to be_can_track_start_and_end_time }
- end
+ context "with the setting disabled", with_settings: { allow_tracking_start_and_end_times: false } do
+ it { expect(described_class).not_to be_can_track_start_and_end_time }
end
end
@@ -545,7 +533,29 @@ RSpec.describe TimeEntry do
end
describe ".must_track_start_and_end_time?" do
- context "with the feature flag enabled", with_flag: { track_start_and_end_times_for_time_entries: true } do
+ context "when the EnterpriseToken does not allow enforcement", with_ee: [] do
+ context "with the allow setting enabled", with_settings: { allow_tracking_start_and_end_times: true } do
+ context "with the enforce setting enabled", with_settings: { enforce_tracking_start_and_end_times: true } do
+ it { expect(described_class).not_to be_must_track_start_and_end_time }
+ end
+
+ context "with the enforce setting disabled", with_settings: { enforce_tracking_start_and_end_times: false } do
+ it { expect(described_class).not_to be_must_track_start_and_end_time }
+ end
+ end
+
+ context "with the allow setting disabled", with_settings: { allow_tracking_start_and_end_times: false } do
+ context "with the enforce setting enabled", with_settings: { enforce_tracking_start_and_end_times: true } do
+ it { expect(described_class).not_to be_must_track_start_and_end_time }
+ end
+
+ context "with the enforce setting disabled", with_settings: { enforce_tracking_start_and_end_times: false } do
+ it { expect(described_class).not_to be_must_track_start_and_end_time }
+ end
+ end
+ end
+
+ context "when the EnterpriseToken allows enforcement", with_ee: [:time_entry_time_restrictions] do
context "with the allow setting enabled", with_settings: { allow_tracking_start_and_end_times: true } do
context "with the enforce setting enabled", with_settings: { enforce_tracking_start_and_end_times: true } do
it { expect(described_class).to be_must_track_start_and_end_time }
@@ -566,27 +576,5 @@ RSpec.describe TimeEntry do
end
end
end
-
- context "with the feature flag disabled", with_flag: { track_start_and_end_times_for_time_entries: false } do
- context "with the allow setting enabled", with_settings: { allow_tracking_start_and_end_times: true } do
- context "with the enforce setting enabled", with_settings: { enforce_tracking_start_and_end_times: true } do
- it { expect(described_class).not_to be_must_track_start_and_end_time }
- end
-
- context "with the enforce setting disabled", with_settings: { enforce_tracking_start_and_end_times: false } do
- it { expect(described_class).not_to be_must_track_start_and_end_time }
- end
- end
-
- context "with the allow setting disabled", with_settings: { allow_tracking_start_and_end_times: false } do
- context "with the enforce setting enabled", with_settings: { enforce_tracking_start_and_end_times: true } do
- it { expect(described_class).not_to be_must_track_start_and_end_time }
- end
-
- context "with the enforce setting disabled", with_settings: { enforce_tracking_start_and_end_times: false } do
- it { expect(described_class).not_to be_must_track_start_and_end_time }
- end
- end
- end
end
end
diff --git a/modules/costs/spec/requests/api/time_entries/create_resource_spec.rb b/modules/costs/spec/requests/api/time_entries/create_resource_spec.rb
index 2c0c4c09ba7..8d035ab6bb0 100644
--- a/modules/costs/spec/requests/api/time_entries/create_resource_spec.rb
+++ b/modules/costs/spec/requests/api/time_entries/create_resource_spec.rb
@@ -33,7 +33,6 @@ require "rack/test"
RSpec.describe "API v3 Time Entries resource",
content_type: :json,
- with_flag: :track_start_and_end_times_for_time_entries,
with_settings: { allow_tracking_start_and_end_times: true } do
include Rack::Test::Methods
include API::V3::Utilities::PathHelper
diff --git a/modules/costs/spec/requests/api/time_entry_resource_spec.rb b/modules/costs/spec/requests/api/time_entry_resource_spec.rb
index 12700aab0c2..907de868eb5 100644
--- a/modules/costs/spec/requests/api/time_entry_resource_spec.rb
+++ b/modules/costs/spec/requests/api/time_entry_resource_spec.rb
@@ -330,9 +330,7 @@ RSpec.describe "API v3 time_entry resource" do
end
end
- context "when start- & end-time tracking is enabled",
- with_flag: { track_start_and_end_times_for_time_entries: true },
- with_settings: { allow_tracking_start_and_end_times: true } do
+ context "when start- & end-time tracking is enabled", with_settings: { allow_tracking_start_and_end_times: true } do
context "when start and end time were tracked" do
let!(:time_entry) do
create(:time_entry, :with_start_and_end_time, project:, work_package:, user: current_user)
@@ -370,9 +368,7 @@ RSpec.describe "API v3 time_entry resource" do
end
end
- context "when start- & end-time tracking is disabled",
- with_flag: { track_start_and_end_times_for_time_entries: false },
- with_settings: { allow_tracking_start_and_end_times: false } do
+ context "when start- & end-time tracking is disabled", with_settings: { allow_tracking_start_and_end_times: false } do
context "when start and end time were tracked" do
let!(:time_entry) do
create(:time_entry, :with_start_and_end_time, project:, work_package:, user: current_user)
diff --git a/modules/documents/app/services/notifications/create_from_model_service/document_strategy.rb b/modules/documents/app/services/notifications/create_from_model_service/document_strategy.rb
index 626eabe62fd..274b9484589 100644
--- a/modules/documents/app/services/notifications/create_from_model_service/document_strategy.rb
+++ b/modules/documents/app/services/notifications/create_from_model_service/document_strategy.rb
@@ -31,7 +31,7 @@ module Notifications::CreateFromModelService::DocumentStrategy
%i(subscribed)
end
- def self.permission
+ def self.permission(*)
:view_documents
end
diff --git a/modules/grids/config/locales/crowdin/js-af.yml b/modules/grids/config/locales/crowdin/js-af.yml
index e1536f7dd3d..0098c9d803f 100644
--- a/modules/grids/config/locales/crowdin/js-af.yml
+++ b/modules/grids/config/locales/crowdin/js-af.yml
@@ -1,12 +1,13 @@
af:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Verwyder legstuk'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-ar.yml b/modules/grids/config/locales/crowdin/js-ar.yml
index 7a65b5ccdf6..55604a3f1f6 100644
--- a/modules/grids/config/locales/crowdin/js-ar.yml
+++ b/modules/grids/config/locales/crowdin/js-ar.yml
@@ -1,12 +1,13 @@
ar:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'إزالة الأداة'
configure: 'Configure widget'
- upsale:
- text: "بعض الأدوات، مثل أداة الرسم البياني لحزمة العمل، متوفرة فقط في النسخة المدفوعة."
- link: 'طبعة المؤسسة.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-az.yml b/modules/grids/config/locales/crowdin/js-az.yml
index ade3e49eca3..fca38c80acc 100644
--- a/modules/grids/config/locales/crowdin/js-az.yml
+++ b/modules/grids/config/locales/crowdin/js-az.yml
@@ -1,12 +1,13 @@
az:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Remove widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-be.yml b/modules/grids/config/locales/crowdin/js-be.yml
index f5f09812499..39bce6d9e32 100644
--- a/modules/grids/config/locales/crowdin/js-be.yml
+++ b/modules/grids/config/locales/crowdin/js-be.yml
@@ -1,12 +1,13 @@
be:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Remove widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-bg.yml b/modules/grids/config/locales/crowdin/js-bg.yml
index 25e33ef3d12..6cc6eac5959 100644
--- a/modules/grids/config/locales/crowdin/js-bg.yml
+++ b/modules/grids/config/locales/crowdin/js-bg.yml
@@ -1,12 +1,13 @@
bg:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Премахване на притурка'
configure: 'Конфигурирайте приспособление'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-ca.yml b/modules/grids/config/locales/crowdin/js-ca.yml
index 87cd2e570e2..aaf1dd716fb 100644
--- a/modules/grids/config/locales/crowdin/js-ca.yml
+++ b/modules/grids/config/locales/crowdin/js-ca.yml
@@ -1,12 +1,13 @@
ca:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Afegeix widget'
remove: 'Elimina els widgets'
configure: 'Configurar widget'
- upsale:
- text: "Alguns widgets com la gràfica de paquet de treball només estan disponibles en l'edició Enterprise."
- link: 'Edició Enterprise.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-ckb-IR.yml b/modules/grids/config/locales/crowdin/js-ckb-IR.yml
index 5cb4d185aa4..1f764e16be1 100644
--- a/modules/grids/config/locales/crowdin/js-ckb-IR.yml
+++ b/modules/grids/config/locales/crowdin/js-ckb-IR.yml
@@ -1,12 +1,13 @@
ckb-IR:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Remove widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-cs.yml b/modules/grids/config/locales/crowdin/js-cs.yml
index e4b595b71f7..d7db4211a10 100644
--- a/modules/grids/config/locales/crowdin/js-cs.yml
+++ b/modules/grids/config/locales/crowdin/js-cs.yml
@@ -1,12 +1,13 @@
cs:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Přidat widget'
remove: 'Odstranit widget'
configure: 'Konfigurovat widget'
- upsale:
- text: "Některé widgety, jako například widget s grafem pracovního balíčku, jsou dostupné pouze v Enterprise Edici."
- link: 'Enterprise Edice'
widgets:
missing_permission: "Nemáte potřebná oprávnění k zobrazení tohoto widgetu."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-da.yml b/modules/grids/config/locales/crowdin/js-da.yml
index 12a0d14f08f..c9b95c4e32a 100644
--- a/modules/grids/config/locales/crowdin/js-da.yml
+++ b/modules/grids/config/locales/crowdin/js-da.yml
@@ -1,12 +1,13 @@
da:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Fjern widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-de.yml b/modules/grids/config/locales/crowdin/js-de.yml
index 558f117f194..bc1499b975b 100644
--- a/modules/grids/config/locales/crowdin/js-de.yml
+++ b/modules/grids/config/locales/crowdin/js-de.yml
@@ -1,12 +1,13 @@
de:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Widget hinzufügen'
remove: 'Widget entfernen'
configure: 'Widget konfigurieren'
- upsale:
- text: "Einige Widgets wie das Arbeitspaket-Graphen Widget sind nur in der Enterprise Edition verfügbar."
- link: 'Enterprise Edition'
widgets:
missing_permission: "Sie verfügen nicht über die erforderlichen Berechtigungen, um dieses Steuerelement anzuzeigen."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-el.yml b/modules/grids/config/locales/crowdin/js-el.yml
index 0b5bbdc5dca..9eb492f1c77 100644
--- a/modules/grids/config/locales/crowdin/js-el.yml
+++ b/modules/grids/config/locales/crowdin/js-el.yml
@@ -1,12 +1,13 @@
el:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Προσθήκη widget'
remove: 'Αφαίρεση widget'
configure: 'Ρύθμιση widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-eo.yml b/modules/grids/config/locales/crowdin/js-eo.yml
index c7e64543162..1262ad69e5f 100644
--- a/modules/grids/config/locales/crowdin/js-eo.yml
+++ b/modules/grids/config/locales/crowdin/js-eo.yml
@@ -1,12 +1,13 @@
eo:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Remove widget'
configure: 'Agordi kromprogrameton'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-es.yml b/modules/grids/config/locales/crowdin/js-es.yml
index 58b9ae28783..6919f46a15a 100644
--- a/modules/grids/config/locales/crowdin/js-es.yml
+++ b/modules/grids/config/locales/crowdin/js-es.yml
@@ -1,12 +1,13 @@
es:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Agregar widget'
remove: 'Quitar widget'
configure: 'Configurar widget'
- upsale:
- text: "Algunos widgets, como el widget de gráfico de paquetes de trabajo, solo están disponibles con Enterprise."
- link: 'Enterprise.'
widgets:
missing_permission: "No tiene los permisos necesarios para ver este widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-et.yml b/modules/grids/config/locales/crowdin/js-et.yml
index 44388aae054..2860fa636b9 100644
--- a/modules/grids/config/locales/crowdin/js-et.yml
+++ b/modules/grids/config/locales/crowdin/js-et.yml
@@ -1,12 +1,13 @@
et:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Eemalda vidin'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-eu.yml b/modules/grids/config/locales/crowdin/js-eu.yml
index 9ed3287e56b..0f6f10d3e00 100644
--- a/modules/grids/config/locales/crowdin/js-eu.yml
+++ b/modules/grids/config/locales/crowdin/js-eu.yml
@@ -1,12 +1,13 @@
eu:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Remove widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-fa.yml b/modules/grids/config/locales/crowdin/js-fa.yml
index e7d5867fb22..687b11a4606 100644
--- a/modules/grids/config/locales/crowdin/js-fa.yml
+++ b/modules/grids/config/locales/crowdin/js-fa.yml
@@ -1,12 +1,13 @@
fa:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'افزودن ویجت'
remove: 'حذف ویجت'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-fi.yml b/modules/grids/config/locales/crowdin/js-fi.yml
index b4b80ccaa74..f4c016d50f1 100644
--- a/modules/grids/config/locales/crowdin/js-fi.yml
+++ b/modules/grids/config/locales/crowdin/js-fi.yml
@@ -1,12 +1,13 @@
fi:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Lisää widgetti'
remove: 'Poista widgetti'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-fil.yml b/modules/grids/config/locales/crowdin/js-fil.yml
index 299f50b0fe2..36282b76dd1 100644
--- a/modules/grids/config/locales/crowdin/js-fil.yml
+++ b/modules/grids/config/locales/crowdin/js-fil.yml
@@ -1,12 +1,13 @@
fil:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Alisin ang widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-fr.yml b/modules/grids/config/locales/crowdin/js-fr.yml
index 9ac5762537c..f379ab0abc7 100644
--- a/modules/grids/config/locales/crowdin/js-fr.yml
+++ b/modules/grids/config/locales/crowdin/js-fr.yml
@@ -1,12 +1,13 @@
fr:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Ajouter un widget'
remove: 'Supprimer le widget'
configure: 'Configurer le widget'
- upsale:
- text: "Certains composants, comme le composant de graphique des lots de travaux, ne sont disponibles que dans l'édition Entreprise."
- link: 'Version Entreprise'
widgets:
missing_permission: "Vous n'avez pas les autorisations nécessaires pour voir ce widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-he.yml b/modules/grids/config/locales/crowdin/js-he.yml
index 4708c6e6180..70ba37a3c03 100644
--- a/modules/grids/config/locales/crowdin/js-he.yml
+++ b/modules/grids/config/locales/crowdin/js-he.yml
@@ -1,12 +1,13 @@
he:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'הוסף ווידג''ט'
remove: 'הסר רכיב'
configure: 'הגדר תוסף'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-hi.yml b/modules/grids/config/locales/crowdin/js-hi.yml
index b8eb54597db..13159f03b02 100644
--- a/modules/grids/config/locales/crowdin/js-hi.yml
+++ b/modules/grids/config/locales/crowdin/js-hi.yml
@@ -1,12 +1,13 @@
hi:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'विजेट हटाएँ'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-hr.yml b/modules/grids/config/locales/crowdin/js-hr.yml
index f0e3a8f2545..30f7d3a12c6 100644
--- a/modules/grids/config/locales/crowdin/js-hr.yml
+++ b/modules/grids/config/locales/crowdin/js-hr.yml
@@ -1,12 +1,13 @@
hr:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Ukloni widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-hu.yml b/modules/grids/config/locales/crowdin/js-hu.yml
index d379b289bb3..4dd247f4301 100644
--- a/modules/grids/config/locales/crowdin/js-hu.yml
+++ b/modules/grids/config/locales/crowdin/js-hu.yml
@@ -1,12 +1,13 @@
hu:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Widget hozzáadása'
remove: 'Widget eltávolítása'
configure: 'Widget konfigurálása'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "Nincs meg a szükséges jogosultsága a widget megtekintéséhez."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-id.yml b/modules/grids/config/locales/crowdin/js-id.yml
index 59c015bf709..234430ceb16 100644
--- a/modules/grids/config/locales/crowdin/js-id.yml
+++ b/modules/grids/config/locales/crowdin/js-id.yml
@@ -1,12 +1,13 @@
id:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Tambahkan widget'
remove: 'Hapus widget'
configure: 'Konfigurasi Widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-it.yml b/modules/grids/config/locales/crowdin/js-it.yml
index 37d389c5092..40e4344ebcb 100644
--- a/modules/grids/config/locales/crowdin/js-it.yml
+++ b/modules/grids/config/locales/crowdin/js-it.yml
@@ -1,12 +1,13 @@
it:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Aggiungi widget'
remove: 'Rimuovi widget'
configure: 'Configura il widget'
- upsale:
- text: "Alcuni widget, come il widget del grafico del pacchetto di lavoro, sono disponibili soltanto nell'edizione per Imprese."
- link: 'Enterprise edition.'
widgets:
missing_permission: "Non hai i permessi necessari per visualizzare questo widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-ja.yml b/modules/grids/config/locales/crowdin/js-ja.yml
index a32f7b4743b..8c6465c54a8 100644
--- a/modules/grids/config/locales/crowdin/js-ja.yml
+++ b/modules/grids/config/locales/crowdin/js-ja.yml
@@ -1,12 +1,13 @@
ja:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'ウィジェットを追加'
remove: 'ウィジェットを削除'
configure: 'ウィジェットの設定'
- upsale:
- text: "ワークパッケージグラフウィジェットなど、一部のウィジェットはEnterpriseエディションでのみ利用可能です。"
- link: 'Enterprise edition'
widgets:
missing_permission: "このウィジェットを観覧するために必要な権限がありません。"
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-ka.yml b/modules/grids/config/locales/crowdin/js-ka.yml
index ff07a403a75..3fd122d4cb6 100644
--- a/modules/grids/config/locales/crowdin/js-ka.yml
+++ b/modules/grids/config/locales/crowdin/js-ka.yml
@@ -1,12 +1,13 @@
ka:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'ვიჯეტის დამატება'
remove: 'ვიჯეტის წაშლა'
configure: 'Configure widget'
- upsale:
- text: "ზოგიერთი ვიჯეტი, როგორიცაა სამუშაო პაკეტის გრაფიკის ვიჯეტი, ხელმისაწვდომია, მხოლოდ, საწარმოო ვერსიაში."
- link: 'საწარმოო რელიზი.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-kk.yml b/modules/grids/config/locales/crowdin/js-kk.yml
index f8086f04f7e..ca07107cd28 100644
--- a/modules/grids/config/locales/crowdin/js-kk.yml
+++ b/modules/grids/config/locales/crowdin/js-kk.yml
@@ -1,12 +1,13 @@
kk:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Remove widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-ko.yml b/modules/grids/config/locales/crowdin/js-ko.yml
index 40bf7515da2..62da8598a1a 100644
--- a/modules/grids/config/locales/crowdin/js-ko.yml
+++ b/modules/grids/config/locales/crowdin/js-ko.yml
@@ -1,12 +1,13 @@
ko:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: '위젯 추가'
remove: '위젯 제거'
configure: '위젯 구성'
- upsale:
- text: "작업 패키지 그래프 위젯 같은 일부 위젯은 다음에서만 사용 가능합니다:"
- link: 'Enterprise edition'
widgets:
missing_permission: "이 위젯을 보는 데 필요한 권한이 없습니다."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-lt.yml b/modules/grids/config/locales/crowdin/js-lt.yml
index e9de607146a..e1e949455ee 100644
--- a/modules/grids/config/locales/crowdin/js-lt.yml
+++ b/modules/grids/config/locales/crowdin/js-lt.yml
@@ -1,12 +1,13 @@
lt:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Pridėti valdiklį'
remove: 'Išimti valdiklį'
configure: 'Valdiklio nustatymai'
- upsale:
- text: "Kai kurie valdikliai, pavyzdžiui darbo paketų grafo valdiklis, galimi tik Enterprise versijoje."
- link: 'Enterprise versija.'
widgets:
missing_permission: "Jūs neturite teisų šio valdiklio peržiūrai."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-lv.yml b/modules/grids/config/locales/crowdin/js-lv.yml
index 820d351e228..345f5c4ce25 100644
--- a/modules/grids/config/locales/crowdin/js-lv.yml
+++ b/modules/grids/config/locales/crowdin/js-lv.yml
@@ -1,12 +1,13 @@
lv:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Noņemt logrīku'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-mn.yml b/modules/grids/config/locales/crowdin/js-mn.yml
index cf387305788..e33d0e04304 100644
--- a/modules/grids/config/locales/crowdin/js-mn.yml
+++ b/modules/grids/config/locales/crowdin/js-mn.yml
@@ -1,12 +1,13 @@
mn:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Remove widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-ms.yml b/modules/grids/config/locales/crowdin/js-ms.yml
index 384be958bfa..5675c4aec1a 100644
--- a/modules/grids/config/locales/crowdin/js-ms.yml
+++ b/modules/grids/config/locales/crowdin/js-ms.yml
@@ -1,12 +1,13 @@
ms:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Tambah widget'
remove: 'Buang widget'
configure: 'Konfigurasi widget'
- upsale:
- text: "Beberapa widget seperti widget graf pakej kerja, hanya tersedia dalam edisi Enterprise."
- link: 'Edisi Enterprise'
widgets:
missing_permission: "Anda tidak mempunyai kebenaran yang diperlukan untuk melihat widget ini."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-ne.yml b/modules/grids/config/locales/crowdin/js-ne.yml
index ca8239ec337..0b5cbd03037 100644
--- a/modules/grids/config/locales/crowdin/js-ne.yml
+++ b/modules/grids/config/locales/crowdin/js-ne.yml
@@ -1,12 +1,13 @@
ne:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Remove widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-nl.yml b/modules/grids/config/locales/crowdin/js-nl.yml
index b10dfa58a67..1d959ce9cb6 100644
--- a/modules/grids/config/locales/crowdin/js-nl.yml
+++ b/modules/grids/config/locales/crowdin/js-nl.yml
@@ -1,12 +1,13 @@
nl:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Widget toevoegen'
remove: 'Widget verwijderen'
configure: 'Widget configureren'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "U hebt niet de vereiste rechten om deze widget te bekijken."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-no.yml b/modules/grids/config/locales/crowdin/js-no.yml
index f2765211a92..2034e548fb1 100644
--- a/modules/grids/config/locales/crowdin/js-no.yml
+++ b/modules/grids/config/locales/crowdin/js-no.yml
@@ -1,12 +1,13 @@
"no":
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Legg til widget'
remove: 'Fjern widget'
configure: 'Konfigurer widget'
- upsale:
- text: "Noen widgeter, som arbeidspakke-diagram-modulen, er bare tilgjengelig i Enterprise versjonen."
- link: 'Enterprise edition.'
widgets:
missing_permission: "Du har ikke nødvendige tillatelser til å se denne widgeten."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-pl.yml b/modules/grids/config/locales/crowdin/js-pl.yml
index 418068ad0bb..847ed3914a4 100644
--- a/modules/grids/config/locales/crowdin/js-pl.yml
+++ b/modules/grids/config/locales/crowdin/js-pl.yml
@@ -1,12 +1,13 @@
pl:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Dodaj widżet'
remove: 'Usuń widżet'
configure: 'Konfiguruj widżet'
- upsale:
- text: "Niektóre widżety, takie jak widżet wykresu pakietu roboczego, są dostępne jedynie w wersji Enterprise."
- link: 'Wersja Enterprise.'
widgets:
missing_permission: "Nie masz uprawnień niezbędnych do wyświetlenia tego widżetu."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-pt-BR.yml b/modules/grids/config/locales/crowdin/js-pt-BR.yml
index 24a18061209..29a8b85b6a4 100644
--- a/modules/grids/config/locales/crowdin/js-pt-BR.yml
+++ b/modules/grids/config/locales/crowdin/js-pt-BR.yml
@@ -1,12 +1,13 @@
pt-BR:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Adicionar widget'
remove: 'Remover widget'
configure: 'Configurar widget'
- upsale:
- text: "Alguns widgets, como o widget gráfico do pacote de trabalho, só estão disponíveis na edição Enterprise."
- link: 'Edição Enterprise.'
widgets:
missing_permission: "Você não tem as permissões necessárias para visualizar este widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-pt-PT.yml b/modules/grids/config/locales/crowdin/js-pt-PT.yml
index 5e0d7fc08ff..878374eddd8 100644
--- a/modules/grids/config/locales/crowdin/js-pt-PT.yml
+++ b/modules/grids/config/locales/crowdin/js-pt-PT.yml
@@ -1,12 +1,13 @@
pt-PT:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Adicionar widget'
remove: 'Remover o widget'
configure: 'Configurar widget'
- upsale:
- text: "Alguns widgets, como o widget gráfico do pacote de trabalho, estão disponíveis apenas na edição Enterprise."
- link: 'Edição Enterprise.'
widgets:
missing_permission: "Não tem as permissões necessárias para visualizar este widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-ro.yml b/modules/grids/config/locales/crowdin/js-ro.yml
index 93c6f2f41da..6791a2c01a8 100644
--- a/modules/grids/config/locales/crowdin/js-ro.yml
+++ b/modules/grids/config/locales/crowdin/js-ro.yml
@@ -1,12 +1,13 @@
ro:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Adaugă widget'
remove: 'Eliminați widget-ul'
configure: 'Configurați widget-ul'
- upsale:
- text: "Unele widget-uri, cum ar fi widget-ul grafic pachet de lucru, sunt disponibile doar în Enterprise Edition."
- link: 'Ediția Enterprise.'
widgets:
missing_permission: "Nu ai drepturile necesare pentru a vizualiza acest widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-ru.yml b/modules/grids/config/locales/crowdin/js-ru.yml
index ee965915014..6cdc68df279 100644
--- a/modules/grids/config/locales/crowdin/js-ru.yml
+++ b/modules/grids/config/locales/crowdin/js-ru.yml
@@ -1,12 +1,13 @@
ru:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Некоторые виджеты, например виджет графиков пакетов работ, доступны только в корпоративной версии."
js:
grid:
add_widget: 'Добавить виджет'
remove: 'Удалить виджет'
configure: 'Настроить виджет'
- upsale:
- text: "Некоторые виджеты, как и виджет пакета работ, доступны только в корпоративной версии."
- link: 'Корпоративная версия.'
widgets:
missing_permission: "У вас нет необходимых прав для просмотра этого виджета."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-rw.yml b/modules/grids/config/locales/crowdin/js-rw.yml
index 50773d58770..7086167f199 100644
--- a/modules/grids/config/locales/crowdin/js-rw.yml
+++ b/modules/grids/config/locales/crowdin/js-rw.yml
@@ -1,12 +1,13 @@
rw:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Remove widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-si.yml b/modules/grids/config/locales/crowdin/js-si.yml
index 1647dbf7069..4b396dd150a 100644
--- a/modules/grids/config/locales/crowdin/js-si.yml
+++ b/modules/grids/config/locales/crowdin/js-si.yml
@@ -1,12 +1,13 @@
si:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Remove widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-sk.yml b/modules/grids/config/locales/crowdin/js-sk.yml
index cf292e515c0..d403c4ccbe1 100644
--- a/modules/grids/config/locales/crowdin/js-sk.yml
+++ b/modules/grids/config/locales/crowdin/js-sk.yml
@@ -1,12 +1,13 @@
sk:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Odstrániť widget'
configure: 'Configurovať widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-sl.yml b/modules/grids/config/locales/crowdin/js-sl.yml
index 972d0d87c23..e84003120fc 100644
--- a/modules/grids/config/locales/crowdin/js-sl.yml
+++ b/modules/grids/config/locales/crowdin/js-sl.yml
@@ -1,12 +1,13 @@
sl:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Dodaj gradnik'
remove: 'Odstrani gradnik'
configure: 'Oblikuj gradnik'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-sr.yml b/modules/grids/config/locales/crowdin/js-sr.yml
index 877c88df87e..9e5d5b51982 100644
--- a/modules/grids/config/locales/crowdin/js-sr.yml
+++ b/modules/grids/config/locales/crowdin/js-sr.yml
@@ -1,12 +1,13 @@
sr:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Remove widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-sv.yml b/modules/grids/config/locales/crowdin/js-sv.yml
index 558997e2f19..7e277ea2551 100644
--- a/modules/grids/config/locales/crowdin/js-sv.yml
+++ b/modules/grids/config/locales/crowdin/js-sv.yml
@@ -1,12 +1,13 @@
sv:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Lägg till widget'
remove: 'Ta bort widget'
configure: 'Konfigurera widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-th.yml b/modules/grids/config/locales/crowdin/js-th.yml
index 46edef79479..2385248a058 100644
--- a/modules/grids/config/locales/crowdin/js-th.yml
+++ b/modules/grids/config/locales/crowdin/js-th.yml
@@ -1,12 +1,13 @@
th:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'เอาเครื่องมือออก'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-tr.yml b/modules/grids/config/locales/crowdin/js-tr.yml
index abcd7a37364..52c37dfbb1a 100644
--- a/modules/grids/config/locales/crowdin/js-tr.yml
+++ b/modules/grids/config/locales/crowdin/js-tr.yml
@@ -1,12 +1,13 @@
tr:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "İş paketi grafiği gibi bazı bileşenler yalnızca Enterprise sürümünde mevcuttur."
js:
grid:
add_widget: 'Bileşen ekle'
remove: 'Bileşeni kaldır'
configure: 'Bileşeni yapılandır'
- upsale:
- text: "İş paketi grafiği pencere öğesi gibi bazı pencere öğeleri yalnızca Enterprise sürümünde mevcuttur."
- link: 'Enterprise sürümü.'
widgets:
missing_permission: "Bu bileşeni görüntülemek için gerekli izinlere sahip değilsiniz."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-uk.yml b/modules/grids/config/locales/crowdin/js-uk.yml
index d88f0a9423c..df15b9e9eb0 100644
--- a/modules/grids/config/locales/crowdin/js-uk.yml
+++ b/modules/grids/config/locales/crowdin/js-uk.yml
@@ -1,12 +1,13 @@
uk:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Деякі віджети, наприклад віджет графіка пакета робіт, доступні тільки у випуску Enterprise."
js:
grid:
add_widget: 'Додати віджет'
remove: 'Видалити віджет'
configure: 'Налаштувати віджет'
- upsale:
- text: "Деякі віджети, наприклад віджет графіку пакет робіт, доступні тільки у версії Enterprise."
- link: 'Версія Enterprise.'
widgets:
missing_permission: "У вас немає дозволів для перегляду цього віджета."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-uz.yml b/modules/grids/config/locales/crowdin/js-uz.yml
index 0ac506f4418..1c31b8a03bf 100644
--- a/modules/grids/config/locales/crowdin/js-uz.yml
+++ b/modules/grids/config/locales/crowdin/js-uz.yml
@@ -1,12 +1,13 @@
uz:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Remove widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-vi.yml b/modules/grids/config/locales/crowdin/js-vi.yml
index 22d78672c2f..af840fb5a34 100644
--- a/modules/grids/config/locales/crowdin/js-vi.yml
+++ b/modules/grids/config/locales/crowdin/js-vi.yml
@@ -1,12 +1,13 @@
vi:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Thêm widget'
remove: 'Xóa widget'
configure: 'Cấu hình widget'
- upsale:
- text: "Một số tiện ích, như tiện ích biểu đồ gói công việc, chỉ khả dụng trong phiên bản Enterprise."
- link: 'Phiên bản doanh nghiệp.'
widgets:
missing_permission: "Bạn không có quyền cần thiết để xem widget này."
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-zh-CN.yml b/modules/grids/config/locales/crowdin/js-zh-CN.yml
index 0724b7ae66d..3b6ddc4d2d2 100644
--- a/modules/grids/config/locales/crowdin/js-zh-CN.yml
+++ b/modules/grids/config/locales/crowdin/js-zh-CN.yml
@@ -1,12 +1,13 @@
zh-CN:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "某些微件,例如工作包图微件,仅在企业版提供。"
js:
grid:
add_widget: '添加微件'
remove: '移除微件'
configure: '配置微件'
- upsale:
- text: "某些微件,如工作包图微件,只在企业版中提供。"
- link: '企业版。'
widgets:
missing_permission: "您没有查看此微件所需的权限。"
custom_text:
diff --git a/modules/grids/config/locales/crowdin/js-zh-TW.yml b/modules/grids/config/locales/crowdin/js-zh-TW.yml
index 3cffb78b448..0255dc8e812 100644
--- a/modules/grids/config/locales/crowdin/js-zh-TW.yml
+++ b/modules/grids/config/locales/crowdin/js-zh-TW.yml
@@ -1,12 +1,13 @@
zh-TW:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "某些組件,如工作項目圖形組件,只在企業版提供。"
js:
grid:
add_widget: '新增小工具'
remove: '移除小工具'
configure: '設定小工具'
- upsale:
- text: "某些組件,如工作包圖組件,只在企業版提供。"
- link: '企業版。'
widgets:
missing_permission: "您沒有必要的權限來檢視小工具。"
custom_text:
diff --git a/modules/grids/config/locales/js-en.yml b/modules/grids/config/locales/js-en.yml
index dfa0eecf0af..2e4303c605a 100644
--- a/modules/grids/config/locales/js-en.yml
+++ b/modules/grids/config/locales/js-en.yml
@@ -1,12 +1,13 @@
en:
+ ee:
+ upsale:
+ grid_widget_wp_graph:
+ description: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
js:
grid:
add_widget: 'Add widget'
remove: 'Remove widget'
configure: 'Configure widget'
- upsale:
- text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
- link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
diff --git a/modules/grids/spec/support/pages/grid.rb b/modules/grids/spec/support/pages/grid.rb
index ae40b50d0be..6d36327ea60 100644
--- a/modules/grids/spec/support/pages/grid.rb
+++ b/modules/grids/spec/support/pages/grid.rb
@@ -56,11 +56,8 @@ module Pages
def expect_add_widget_enterprise_edition_notice(row_number, column_number, location)
within_add_widget_modal(row_number, column_number, location) do
- expect(page)
- .to have_content(I18n.t("js.grid.add_widget"))
-
- expect(page)
- .to have_css(".op-toast.-ee-upsale", text: I18n.t("js.upsale.ee_only"))
+ expect(page).to have_content(I18n.t("js.grid.add_widget"))
+ expect(page).to have_text "Available only through the Basic enterprise plan"
end
end
diff --git a/modules/ldap_groups/app/controllers/ldap_groups/synchronized_filters_controller.rb b/modules/ldap_groups/app/controllers/ldap_groups/synchronized_filters_controller.rb
index 2b6882dac60..3df60f92b0a 100644
--- a/modules/ldap_groups/app/controllers/ldap_groups/synchronized_filters_controller.rb
+++ b/modules/ldap_groups/app/controllers/ldap_groups/synchronized_filters_controller.rb
@@ -73,8 +73,6 @@ module LdapGroups
def find_filter
@filter = SynchronizedFilter.find(params[:ldap_filter_id])
- rescue ActiveRecord::RecordNotFound
- render_404
end
def check_ee
diff --git a/modules/ldap_groups/app/controllers/ldap_groups/synchronized_groups_controller.rb b/modules/ldap_groups/app/controllers/ldap_groups/synchronized_groups_controller.rb
index ec7dd07cb07..21431035e27 100644
--- a/modules/ldap_groups/app/controllers/ldap_groups/synchronized_groups_controller.rb
+++ b/modules/ldap_groups/app/controllers/ldap_groups/synchronized_groups_controller.rb
@@ -52,8 +52,6 @@ module LdapGroups
def find_group
@group = SynchronizedGroup.find(params[:ldap_group_id])
- rescue ActiveRecord::RecordNotFound
- render_404
end
def check_ee
diff --git a/modules/ldap_groups/app/views/ldap_groups/synchronized_groups/upsale.html.erb b/modules/ldap_groups/app/views/ldap_groups/synchronized_groups/upsale.html.erb
index 15303fcff31..0858f361177 100644
--- a/modules/ldap_groups/app/views/ldap_groups/synchronized_groups/upsale.html.erb
+++ b/modules/ldap_groups/app/views/ldap_groups/synchronized_groups/upsale.html.erb
@@ -1,9 +1,3 @@
<% html_title(t(:label_administration), t("ldap_groups.synchronized_groups.plural")) -%>
-<%= render template: "common/upsale",
- locals: {
- feature_title: t("ldap_groups.synchronized_groups.plural"),
- feature_description: t("ldap_groups.synchronized_groups.upsale.description"),
- feature_reference: "enterprise-ldap-groups",
- feature_image: "enterprise/ldap-groups.jpg"
- } %>
+<%= render EnterpriseEdition::UpsalePageComponent.new(:ldap_groups, image: "enterprise/ldap-groups.jpg") %>
diff --git a/modules/ldap_groups/config/locales/crowdin/af.yml b/modules/ldap_groups/config/locales/crowdin/af.yml
index 504d2efdacc..191a5954420 100644
--- a/modules/ldap_groups/config/locales/crowdin/af.yml
+++ b/modules/ldap_groups/config/locales/crowdin/af.yml
@@ -1,4 +1,9 @@
af:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/ar.yml b/modules/ldap_groups/config/locales/crowdin/ar.yml
index c25b898d7d3..19f31d72a6f 100644
--- a/modules/ldap_groups/config/locales/crowdin/ar.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ar.yml
@@ -1,4 +1,9 @@
ar:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/az.yml b/modules/ldap_groups/config/locales/crowdin/az.yml
index 4b850331ed3..2bc2f0b93f1 100644
--- a/modules/ldap_groups/config/locales/crowdin/az.yml
+++ b/modules/ldap_groups/config/locales/crowdin/az.yml
@@ -1,4 +1,9 @@
az:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/be.yml b/modules/ldap_groups/config/locales/crowdin/be.yml
index bace7808112..b6adf49384b 100644
--- a/modules/ldap_groups/config/locales/crowdin/be.yml
+++ b/modules/ldap_groups/config/locales/crowdin/be.yml
@@ -1,4 +1,9 @@
be:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/bg.yml b/modules/ldap_groups/config/locales/crowdin/bg.yml
index 19438645be7..24c3f9dd826 100644
--- a/modules/ldap_groups/config/locales/crowdin/bg.yml
+++ b/modules/ldap_groups/config/locales/crowdin/bg.yml
@@ -1,4 +1,9 @@
bg:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/ca.yml b/modules/ldap_groups/config/locales/crowdin/ca.yml
index b5bc6afcb98..c8e5af2997c 100644
--- a/modules/ldap_groups/config/locales/crowdin/ca.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ca.yml
@@ -1,4 +1,9 @@
ca:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/ckb-IR.yml b/modules/ldap_groups/config/locales/crowdin/ckb-IR.yml
index 5481e0c0dc8..79994e2ae78 100644
--- a/modules/ldap_groups/config/locales/crowdin/ckb-IR.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ckb-IR.yml
@@ -1,4 +1,9 @@
ckb-IR:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/cs.yml b/modules/ldap_groups/config/locales/crowdin/cs.yml
index e8664cb92a6..e7821babfbc 100644
--- a/modules/ldap_groups/config/locales/crowdin/cs.yml
+++ b/modules/ldap_groups/config/locales/crowdin/cs.yml
@@ -1,4 +1,9 @@
cs:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP skupiny"
description: "Synchronizace členství ve skupině LDAP."
diff --git a/modules/ldap_groups/config/locales/crowdin/da.yml b/modules/ldap_groups/config/locales/crowdin/da.yml
index 3a8081a9bcf..e4c75a8d74f 100644
--- a/modules/ldap_groups/config/locales/crowdin/da.yml
+++ b/modules/ldap_groups/config/locales/crowdin/da.yml
@@ -1,4 +1,9 @@
da:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/de.yml b/modules/ldap_groups/config/locales/crowdin/de.yml
index 48dc23bb6a9..7d17ef5efbe 100644
--- a/modules/ldap_groups/config/locales/crowdin/de.yml
+++ b/modules/ldap_groups/config/locales/crowdin/de.yml
@@ -1,4 +1,9 @@
de:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP-Gruppen"
description: "Synchronisierung von LDAP-Gruppenmitgliedschaften."
diff --git a/modules/ldap_groups/config/locales/crowdin/el.yml b/modules/ldap_groups/config/locales/crowdin/el.yml
index c67e347b38f..c28517bef2a 100644
--- a/modules/ldap_groups/config/locales/crowdin/el.yml
+++ b/modules/ldap_groups/config/locales/crowdin/el.yml
@@ -1,4 +1,9 @@
el:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/eo.yml b/modules/ldap_groups/config/locales/crowdin/eo.yml
index 192f91fdee0..ee44403a90a 100644
--- a/modules/ldap_groups/config/locales/crowdin/eo.yml
+++ b/modules/ldap_groups/config/locales/crowdin/eo.yml
@@ -1,4 +1,9 @@
eo:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/es.yml b/modules/ldap_groups/config/locales/crowdin/es.yml
index c9782d2bcb1..e752e24080b 100644
--- a/modules/ldap_groups/config/locales/crowdin/es.yml
+++ b/modules/ldap_groups/config/locales/crowdin/es.yml
@@ -1,4 +1,9 @@
es:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "Grupos LDAP de OpenProject"
description: "Sincronización de miembros de grupos LDAP."
diff --git a/modules/ldap_groups/config/locales/crowdin/et.yml b/modules/ldap_groups/config/locales/crowdin/et.yml
index eeea3453883..85611744d8e 100644
--- a/modules/ldap_groups/config/locales/crowdin/et.yml
+++ b/modules/ldap_groups/config/locales/crowdin/et.yml
@@ -1,4 +1,9 @@
et:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/eu.yml b/modules/ldap_groups/config/locales/crowdin/eu.yml
index d03f6d669d5..c95c8e272fd 100644
--- a/modules/ldap_groups/config/locales/crowdin/eu.yml
+++ b/modules/ldap_groups/config/locales/crowdin/eu.yml
@@ -1,4 +1,9 @@
eu:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/fa.yml b/modules/ldap_groups/config/locales/crowdin/fa.yml
index b27d02dac24..7c4bb5c8649 100644
--- a/modules/ldap_groups/config/locales/crowdin/fa.yml
+++ b/modules/ldap_groups/config/locales/crowdin/fa.yml
@@ -1,4 +1,9 @@
fa:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/fi.yml b/modules/ldap_groups/config/locales/crowdin/fi.yml
index 9ae68174722..3c3cdfe0335 100644
--- a/modules/ldap_groups/config/locales/crowdin/fi.yml
+++ b/modules/ldap_groups/config/locales/crowdin/fi.yml
@@ -1,4 +1,9 @@
fi:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/fil.yml b/modules/ldap_groups/config/locales/crowdin/fil.yml
index daf9925f353..384839d9ead 100644
--- a/modules/ldap_groups/config/locales/crowdin/fil.yml
+++ b/modules/ldap_groups/config/locales/crowdin/fil.yml
@@ -1,4 +1,9 @@
fil:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/fr.yml b/modules/ldap_groups/config/locales/crowdin/fr.yml
index 34cf3aeb67b..2cb4263c58d 100644
--- a/modules/ldap_groups/config/locales/crowdin/fr.yml
+++ b/modules/ldap_groups/config/locales/crowdin/fr.yml
@@ -1,4 +1,9 @@
fr:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "Groupes LDAP OpenProject"
description: "Synchronisation des membres du groupe LDAP."
diff --git a/modules/ldap_groups/config/locales/crowdin/he.yml b/modules/ldap_groups/config/locales/crowdin/he.yml
index a5988df0972..6c8a26a0128 100644
--- a/modules/ldap_groups/config/locales/crowdin/he.yml
+++ b/modules/ldap_groups/config/locales/crowdin/he.yml
@@ -1,4 +1,9 @@
he:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/hi.yml b/modules/ldap_groups/config/locales/crowdin/hi.yml
index f65de53bff2..0ba94040fe1 100644
--- a/modules/ldap_groups/config/locales/crowdin/hi.yml
+++ b/modules/ldap_groups/config/locales/crowdin/hi.yml
@@ -1,4 +1,9 @@
hi:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/hr.yml b/modules/ldap_groups/config/locales/crowdin/hr.yml
index 0a7b10db10e..6b0187f28b1 100644
--- a/modules/ldap_groups/config/locales/crowdin/hr.yml
+++ b/modules/ldap_groups/config/locales/crowdin/hr.yml
@@ -1,4 +1,9 @@
hr:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/hu.yml b/modules/ldap_groups/config/locales/crowdin/hu.yml
index 62cdbcb56b6..19b2257abbb 100644
--- a/modules/ldap_groups/config/locales/crowdin/hu.yml
+++ b/modules/ldap_groups/config/locales/crowdin/hu.yml
@@ -1,4 +1,9 @@
hu:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/id.yml b/modules/ldap_groups/config/locales/crowdin/id.yml
index 27079bd7081..44ef8fb1f3e 100644
--- a/modules/ldap_groups/config/locales/crowdin/id.yml
+++ b/modules/ldap_groups/config/locales/crowdin/id.yml
@@ -1,4 +1,9 @@
id:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Sinkronisasi grup LDAP dengan grup OpenProject untuk mengatur pengguna, mengubah hak akses, dan memberikan fasilitas manajemen antar grup.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/it.yml b/modules/ldap_groups/config/locales/crowdin/it.yml
index cab70579e72..17f3030356c 100644
--- a/modules/ldap_groups/config/locales/crowdin/it.yml
+++ b/modules/ldap_groups/config/locales/crowdin/it.yml
@@ -1,4 +1,9 @@
it:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "Gruppi OpenProject LDAP"
description: "Sincronizzazione dei membri del gruppo LDAP."
diff --git a/modules/ldap_groups/config/locales/crowdin/ja.yml b/modules/ldap_groups/config/locales/crowdin/ja.yml
index f7863567f89..ca39b5a512b 100644
--- a/modules/ldap_groups/config/locales/crowdin/ja.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ja.yml
@@ -1,4 +1,9 @@
ja:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP グループ"
description: "LDAP グループメンバシップの同期。"
diff --git a/modules/ldap_groups/config/locales/crowdin/ka.yml b/modules/ldap_groups/config/locales/crowdin/ka.yml
index 80555834e14..d46f7b9556b 100644
--- a/modules/ldap_groups/config/locales/crowdin/ka.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ka.yml
@@ -1,4 +1,9 @@
ka:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/kk.yml b/modules/ldap_groups/config/locales/crowdin/kk.yml
index 27b9317e67d..169261ae920 100644
--- a/modules/ldap_groups/config/locales/crowdin/kk.yml
+++ b/modules/ldap_groups/config/locales/crowdin/kk.yml
@@ -1,4 +1,9 @@
kk:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/ko.yml b/modules/ldap_groups/config/locales/crowdin/ko.yml
index b09135e58ee..1e94ed45983 100644
--- a/modules/ldap_groups/config/locales/crowdin/ko.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ko.yml
@@ -1,4 +1,9 @@
ko:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP 그룹"
description: "LDAP 그룹 멤버십 동기화."
diff --git a/modules/ldap_groups/config/locales/crowdin/lt.yml b/modules/ldap_groups/config/locales/crowdin/lt.yml
index a8902b6da44..311b4ad8e7a 100644
--- a/modules/ldap_groups/config/locales/crowdin/lt.yml
+++ b/modules/ldap_groups/config/locales/crowdin/lt.yml
@@ -1,4 +1,9 @@
lt:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP grupės"
description: "LDAP grupių narysčių sinchronizavimas."
diff --git a/modules/ldap_groups/config/locales/crowdin/lv.yml b/modules/ldap_groups/config/locales/crowdin/lv.yml
index 8f528ec21a1..f37db888ce5 100644
--- a/modules/ldap_groups/config/locales/crowdin/lv.yml
+++ b/modules/ldap_groups/config/locales/crowdin/lv.yml
@@ -1,4 +1,9 @@
lv:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/mn.yml b/modules/ldap_groups/config/locales/crowdin/mn.yml
index 3e0ab60c656..3f83d2cbbe9 100644
--- a/modules/ldap_groups/config/locales/crowdin/mn.yml
+++ b/modules/ldap_groups/config/locales/crowdin/mn.yml
@@ -1,4 +1,9 @@
mn:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/ms.yml b/modules/ldap_groups/config/locales/crowdin/ms.yml
index 1b0a8bde7c5..19c721f229e 100644
--- a/modules/ldap_groups/config/locales/crowdin/ms.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ms.yml
@@ -1,4 +1,9 @@
ms:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "Kumpulan LDAP OpenProject"
description: "Penyelarasan keahlian kumpulan LDAP"
diff --git a/modules/ldap_groups/config/locales/crowdin/ne.yml b/modules/ldap_groups/config/locales/crowdin/ne.yml
index 7c293e7fe5d..c16b26771ec 100644
--- a/modules/ldap_groups/config/locales/crowdin/ne.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ne.yml
@@ -1,4 +1,9 @@
ne:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/nl.yml b/modules/ldap_groups/config/locales/crowdin/nl.yml
index 7393431a1d9..94bf1ee27d3 100644
--- a/modules/ldap_groups/config/locales/crowdin/nl.yml
+++ b/modules/ldap_groups/config/locales/crowdin/nl.yml
@@ -1,4 +1,9 @@
nl:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/no.yml b/modules/ldap_groups/config/locales/crowdin/no.yml
index 238c941769b..c99e2afd569 100644
--- a/modules/ldap_groups/config/locales/crowdin/no.yml
+++ b/modules/ldap_groups/config/locales/crowdin/no.yml
@@ -1,4 +1,9 @@
"no":
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP-grupper"
description: "Synkronisering av LDAP gruppe medlemskap."
diff --git a/modules/ldap_groups/config/locales/crowdin/pl.yml b/modules/ldap_groups/config/locales/crowdin/pl.yml
index 5445738c6f8..4d3c4628c35 100644
--- a/modules/ldap_groups/config/locales/crowdin/pl.yml
+++ b/modules/ldap_groups/config/locales/crowdin/pl.yml
@@ -1,4 +1,9 @@
pl:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject Grupy LDAP"
description: "Synchronizacja członkostwa grupy LDAP."
diff --git a/modules/ldap_groups/config/locales/crowdin/pt-BR.yml b/modules/ldap_groups/config/locales/crowdin/pt-BR.yml
index 45214dd41e9..7aee95b9c44 100644
--- a/modules/ldap_groups/config/locales/crowdin/pt-BR.yml
+++ b/modules/ldap_groups/config/locales/crowdin/pt-BR.yml
@@ -1,4 +1,9 @@
pt-BR:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'Sincronização de grupo LDAP'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "Grupos LDAP do OpenProject"
description: "Sincronização de associações de grupos LDAP."
diff --git a/modules/ldap_groups/config/locales/crowdin/pt-PT.yml b/modules/ldap_groups/config/locales/crowdin/pt-PT.yml
index 5933ff1ba54..c87de9d4298 100644
--- a/modules/ldap_groups/config/locales/crowdin/pt-PT.yml
+++ b/modules/ldap_groups/config/locales/crowdin/pt-PT.yml
@@ -1,4 +1,9 @@
pt-PT:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "Grupos LDAP do OpenProject"
description: "Sincronização de associações de grupos LDAP."
diff --git a/modules/ldap_groups/config/locales/crowdin/ro.yml b/modules/ldap_groups/config/locales/crowdin/ro.yml
index 26f441f208f..c627c6ca401 100644
--- a/modules/ldap_groups/config/locales/crowdin/ro.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ro.yml
@@ -1,4 +1,9 @@
ro:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/ru.yml b/modules/ldap_groups/config/locales/crowdin/ru.yml
index 22b5893f2b2..a3dd7ba6a5e 100644
--- a/modules/ldap_groups/config/locales/crowdin/ru.yml
+++ b/modules/ldap_groups/config/locales/crowdin/ru.yml
@@ -1,4 +1,9 @@
ru:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'Синхронизация LDAP-группы'
+ description: 'Синхронизировать группы LDAP с группами OpenProject для управления пользователями, изменить их права доступа и облегчить управление пользователями по группам.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP группы"
description: "Синхронизация членства в LDAP группах."
diff --git a/modules/ldap_groups/config/locales/crowdin/rw.yml b/modules/ldap_groups/config/locales/crowdin/rw.yml
index e45db3a8ba3..25fab1b41d2 100644
--- a/modules/ldap_groups/config/locales/crowdin/rw.yml
+++ b/modules/ldap_groups/config/locales/crowdin/rw.yml
@@ -1,4 +1,9 @@
rw:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/si.yml b/modules/ldap_groups/config/locales/crowdin/si.yml
index d610f98fdfe..788b43f4191 100644
--- a/modules/ldap_groups/config/locales/crowdin/si.yml
+++ b/modules/ldap_groups/config/locales/crowdin/si.yml
@@ -1,4 +1,9 @@
si:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/sk.yml b/modules/ldap_groups/config/locales/crowdin/sk.yml
index 99527874f2b..a4f0b942054 100644
--- a/modules/ldap_groups/config/locales/crowdin/sk.yml
+++ b/modules/ldap_groups/config/locales/crowdin/sk.yml
@@ -1,4 +1,9 @@
sk:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/sl.yml b/modules/ldap_groups/config/locales/crowdin/sl.yml
index 7ab1488803a..d33e0933205 100644
--- a/modules/ldap_groups/config/locales/crowdin/sl.yml
+++ b/modules/ldap_groups/config/locales/crowdin/sl.yml
@@ -1,4 +1,9 @@
sl:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/sr.yml b/modules/ldap_groups/config/locales/crowdin/sr.yml
index f11ce8a9fa3..2b1ab3c742f 100644
--- a/modules/ldap_groups/config/locales/crowdin/sr.yml
+++ b/modules/ldap_groups/config/locales/crowdin/sr.yml
@@ -1,4 +1,9 @@
sr:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/sv.yml b/modules/ldap_groups/config/locales/crowdin/sv.yml
index 564a8857d4a..30cbd670913 100644
--- a/modules/ldap_groups/config/locales/crowdin/sv.yml
+++ b/modules/ldap_groups/config/locales/crowdin/sv.yml
@@ -1,4 +1,9 @@
sv:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/th.yml b/modules/ldap_groups/config/locales/crowdin/th.yml
index f1800844a68..510fc71b3ef 100644
--- a/modules/ldap_groups/config/locales/crowdin/th.yml
+++ b/modules/ldap_groups/config/locales/crowdin/th.yml
@@ -1,4 +1,9 @@
th:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/tr.yml b/modules/ldap_groups/config/locales/crowdin/tr.yml
index daa3023ceb4..9b3e33bc5fe 100644
--- a/modules/ldap_groups/config/locales/crowdin/tr.yml
+++ b/modules/ldap_groups/config/locales/crowdin/tr.yml
@@ -1,4 +1,9 @@
tr:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP grup senkronizasyonu'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP grupları"
description: "LDAP grup üyeliklerinin senkronizasyonu."
diff --git a/modules/ldap_groups/config/locales/crowdin/uk.yml b/modules/ldap_groups/config/locales/crowdin/uk.yml
index f409070d179..322e2b22d6c 100644
--- a/modules/ldap_groups/config/locales/crowdin/uk.yml
+++ b/modules/ldap_groups/config/locales/crowdin/uk.yml
@@ -1,4 +1,9 @@
uk:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'Синхронізація групи LDAP'
+ description: 'Синхронізуйте LDAP-групи з групами OpenProject, щоб змінювати дозволи користувачів і спростити керування користувачами, зокрема з різних груп.'
plugin_openproject_ldap_groups:
name: "Групи LDAP OpenProject"
description: "Синхронізація членства в LDAP групах."
diff --git a/modules/ldap_groups/config/locales/crowdin/uz.yml b/modules/ldap_groups/config/locales/crowdin/uz.yml
index 46e62a80f21..e5d849219ce 100644
--- a/modules/ldap_groups/config/locales/crowdin/uz.yml
+++ b/modules/ldap_groups/config/locales/crowdin/uz.yml
@@ -1,4 +1,9 @@
uz:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/config/locales/crowdin/vi.yml b/modules/ldap_groups/config/locales/crowdin/vi.yml
index 9efd8a3ffc9..2477522d653 100644
--- a/modules/ldap_groups/config/locales/crowdin/vi.yml
+++ b/modules/ldap_groups/config/locales/crowdin/vi.yml
@@ -1,4 +1,9 @@
vi:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "Nhóm LDAP OpenProject"
description: "Đồng bộ hóa các thành viên nhóm LDAP."
diff --git a/modules/ldap_groups/config/locales/crowdin/zh-CN.yml b/modules/ldap_groups/config/locales/crowdin/zh-CN.yml
index 865f6146895..0b9a5cc6370 100644
--- a/modules/ldap_groups/config/locales/crowdin/zh-CN.yml
+++ b/modules/ldap_groups/config/locales/crowdin/zh-CN.yml
@@ -1,7 +1,12 @@
zh-CN:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP 组同步'
+ description: '与 OpenProject 组同步 LDAP 组以管理用户,更改他们的权限以便不同组的用户管理。'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP 组"
- description: "LDAP组成员同步。"
+ description: "LDAP 组成员同步。"
activerecord:
attributes:
ldap_groups/synchronized_group:
diff --git a/modules/ldap_groups/config/locales/crowdin/zh-TW.yml b/modules/ldap_groups/config/locales/crowdin/zh-TW.yml
index 2b0eaacb1a5..9be59366379 100644
--- a/modules/ldap_groups/config/locales/crowdin/zh-TW.yml
+++ b/modules/ldap_groups/config/locales/crowdin/zh-TW.yml
@@ -1,4 +1,9 @@
zh-TW:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP 群組同步'
+ description: '將 LDAP 群組與 OpenProject 群組同步,以管理使用者、變更其權限,並簡化跨群組的使用者管理。'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP 群組"
description: "LDAP組成員同步。"
@@ -10,7 +15,7 @@ zh-TW:
ldap_auth_source: 'LDAP 連線'
sync_users: '同步使用者'
ldap_groups/synchronized_filter:
- filter_string: 'LDAP篩選條件'
+ filter_string: '簡約登入目錄制約(LDAP)篩選'
auth_source: '驗證來源'
ldap_auth_source: 'LDAP 連線'
group_name_attribute: "群組名字屬性"
diff --git a/modules/ldap_groups/config/locales/en.yml b/modules/ldap_groups/config/locales/en.yml
index 106816d6f80..671b858795d 100644
--- a/modules/ldap_groups/config/locales/en.yml
+++ b/modules/ldap_groups/config/locales/en.yml
@@ -1,4 +1,9 @@
en:
+ ee:
+ upsale:
+ ldap_groups:
+ title: 'LDAP group synchronization'
+ description: 'Synchronize LDAP groups with OpenProject groups to manage users, change their permissions and facilitate user management across groups.'
plugin_openproject_ldap_groups:
name: "OpenProject LDAP groups"
description: "Synchronization of LDAP group memberships."
diff --git a/modules/ldap_groups/spec/features/administration_spec.rb b/modules/ldap_groups/spec/features/administration_spec.rb
index 0f5b5eb6f70..dd684cc2d57 100644
--- a/modules/ldap_groups/spec/features/administration_spec.rb
+++ b/modules/ldap_groups/spec/features/administration_spec.rb
@@ -10,7 +10,8 @@ RSpec.describe "LDAP group sync administration spec", :js do
context "without EE" do
it "shows upsale" do
- expect(page).to have_css(".upsale-notification")
+ expect(page).to have_enterprise_upsale_page
+ expect(page).to have_text "Available only through the Premium enterprise plan"
end
end
@@ -19,8 +20,7 @@ RSpec.describe "LDAP group sync administration spec", :js do
let!(:auth_source) { create(:ldap_auth_source, name: "ldap") }
it "allows synced group administration flow" do
- expect(page).to have_no_css(".upsale-notification")
-
+ expect(page).not_to have_enterprise_upsale_page
# Open create menu
page.find_test_selector("op-admin-synchronized-groups--button-new", text: I18n.t(:button_add)).click
# Create group
diff --git a/modules/meeting/app/components/meetings/index/dialog_component.html.erb b/modules/meeting/app/components/meetings/index/dialog_component.html.erb
index 6513667bd61..703820c53c2 100644
--- a/modules/meeting/app/components/meetings/index/dialog_component.html.erb
+++ b/modules/meeting/app/components/meetings/index/dialog_component.html.erb
@@ -21,7 +21,7 @@
dialog.with_footer do
component_collection do |modal_footer|
modal_footer.with_component(
- Primer::ButtonComponent.new(
+ Primer::Beta::Button.new(
data: { "close-dialog-id": "new-meeting-dialog" }
)
) do
@@ -29,7 +29,7 @@
end
modal_footer.with_component(
- Primer::ButtonComponent.new(
+ Primer::Beta::Button.new(
scheme: :primary,
form: "meeting-form",
type: :submit
diff --git a/modules/meeting/app/components/meetings/index/form_component.html.erb b/modules/meeting/app/components/meetings/index/form_component.html.erb
index e2b8d4e7b5e..8cef2252e96 100644
--- a/modules/meeting/app/components/meetings/index/form_component.html.erb
+++ b/modules/meeting/app/components/meetings/index/form_component.html.erb
@@ -115,10 +115,6 @@
end
end
end
- else
- modal_body.with_row do
- render(Meeting::Type.new(f))
- end
end
if @copy_from
diff --git a/modules/meeting/app/components/meetings/index_sub_header_component.html.erb b/modules/meeting/app/components/meetings/index_sub_header_component.html.erb
index 6243333c922..da37ddc4d52 100644
--- a/modules/meeting/app/components/meetings/index_sub_header_component.html.erb
+++ b/modules/meeting/app/components/meetings/index_sub_header_component.html.erb
@@ -31,7 +31,7 @@
menu.with_item(
label: I18n.t("meeting.types.one_time"),
tag: :a,
- href: polymorphic_path([:new_dialog, @project, :meetings], type: :structured),
+ href: polymorphic_path([:new_dialog, @project, :meetings]),
content_arguments: { data: { controller: "async-dialog" } }
) do |item|
item.with_description.with_content(t("meeting.types.structured_text"))
@@ -45,14 +45,6 @@
) do |item|
item.with_description.with_content(t("meeting.types.recurring_text"))
end
-
- menu.with_item(
- label: I18n.t("meeting.types.classic"),
- tag: :a,
- href: dynamic_path
- ) do |item|
- item.with_description.with_content(t("meeting.types.classic_text"))
- end
end
end
end
diff --git a/modules/meeting/app/components/meetings/row_component.rb b/modules/meeting/app/components/meetings/row_component.rb
index 3b82fd4e2bf..3ee43460c5c 100644
--- a/modules/meeting/app/components/meetings/row_component.rb
+++ b/modules/meeting/app/components/meetings/row_component.rb
@@ -122,7 +122,6 @@ module Meetings
href: copy_project_meeting_path(project, model),
content_arguments: {
data: {
- turbo: model.is_a?(StructuredMeeting),
turbo_stream: true
}
}) do |item|
diff --git a/modules/meeting/app/components/meetings/side_panel/details_form_component.html.erb b/modules/meeting/app/components/meetings/side_panel/details_form_component.html.erb
index 567773ead27..88155e15281 100644
--- a/modules/meeting/app/components/meetings/side_panel/details_form_component.html.erb
+++ b/modules/meeting/app/components/meetings/side_panel/details_form_component.html.erb
@@ -6,8 +6,7 @@
data: { turbo: true,
turbo_stream: true,
controller: "meetings--form",
- "application-target": "dynamic",
- "meetings--form-structured-value": true },
+ "application-target": "dynamic" },
url: update_details_project_meeting_path(@project, @meeting)
) do |f|
component_collection do |collection|
@@ -33,11 +32,11 @@
collection.with_component(Primer::Alpha::Dialog::Footer.new) do
component_collection do |modal_footer|
- modal_footer.with_component(Primer::ButtonComponent.new(data: { "close-dialog-id": "edit-meeting-details-dialog" })) do
+ modal_footer.with_component(Primer::Beta::Button.new(data: { "close-dialog-id": "edit-meeting-details-dialog" })) do
t("button_cancel")
end
- modal_footer.with_component(Primer::ButtonComponent.new(scheme: :primary, type: :submit)) do
+ modal_footer.with_component(Primer::Beta::Button.new(scheme: :primary, type: :submit)) do
t("button_save")
end
end
diff --git a/modules/meeting/app/components/meetings/side_panel/participants_dialog.html.erb b/modules/meeting/app/components/meetings/side_panel/participants_dialog.html.erb
index 18922ae2ba3..343d37ce3a7 100644
--- a/modules/meeting/app/components/meetings/side_panel/participants_dialog.html.erb
+++ b/modules/meeting/app/components/meetings/side_panel/participants_dialog.html.erb
@@ -135,11 +135,11 @@
if @meeting.editable?
d.with_footer do
component_collection do |footer|
- footer.with_component(Primer::ButtonComponent.new(data: { "close-dialog-id": "edit-participants-dialog" })) do
+ footer.with_component(Primer::Beta::Button.new(data: { "close-dialog-id": "edit-participants-dialog" })) do
t("button_cancel")
end
footer.with_component(
- Primer::ButtonComponent.new(
+ Primer::Beta::Button.new(
scheme: :primary,
form: "edit_participants_form",
data: { turbo: true },
diff --git a/modules/meeting/app/components/work_package_meetings_tab/add_work_package_to_meeting_dialog_component.html.erb b/modules/meeting/app/components/work_package_meetings_tab/add_work_package_to_meeting_dialog_component.html.erb
index c11239c337c..6b03a774dd9 100644
--- a/modules/meeting/app/components/work_package_meetings_tab/add_work_package_to_meeting_dialog_component.html.erb
+++ b/modules/meeting/app/components/work_package_meetings_tab/add_work_package_to_meeting_dialog_component.html.erb
@@ -18,7 +18,7 @@
d.with_footer do
component_collection do |buttons|
buttons.with_component(
- Primer::ButtonComponent.new(
+ Primer::Beta::Button.new(
data: {
"close-dialog-id": "add-work-package-to-meeting-dialog"
}
@@ -27,7 +27,7 @@
t("button_cancel")
end
buttons.with_component(
- Primer::ButtonComponent.new(
+ Primer::Beta::Button.new(
scheme: :primary,
form: "add-work-package-to-meeting-form",
data: { turbo: true },
diff --git a/modules/meeting/app/contracts/meeting_agenda_items/create_contract.rb b/modules/meeting/app/contracts/meeting_agenda_items/create_contract.rb
index 366e50470f7..3f24fdf397c 100644
--- a/modules/meeting/app/contracts/meeting_agenda_items/create_contract.rb
+++ b/modules/meeting/app/contracts/meeting_agenda_items/create_contract.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -34,7 +35,7 @@ module MeetingAgendaItems
validate :user_allowed_to_add, :validate_meeting_existence
def self.assignable_meetings(user)
- StructuredMeeting
+ Meeting
.open
.not_templated
.not_cancelled
diff --git a/modules/meeting/app/contracts/meeting_sections/create_contract.rb b/modules/meeting/app/contracts/meeting_sections/create_contract.rb
index 77f4c6e5de4..ec0c7a7bcad 100644
--- a/modules/meeting/app/contracts/meeting_sections/create_contract.rb
+++ b/modules/meeting/app/contracts/meeting_sections/create_contract.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -40,7 +41,7 @@ module MeetingSections
:validate_meeting_existence
def self.assignable_meetings(user)
- StructuredMeeting
+ Meeting
.open
.visible(user)
end
diff --git a/modules/meeting/app/contracts/meetings/create_contract.rb b/modules/meeting/app/contracts/meetings/create_contract.rb
index 83ab09d5a77..e69e334d292 100644
--- a/modules/meeting/app/contracts/meetings/create_contract.rb
+++ b/modules/meeting/app/contracts/meetings/create_contract.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -29,21 +30,13 @@
module Meetings
class CreateContract < BaseContract
- attribute :type
attribute :recurring_meeting_id
validate :user_allowed_to_add
- validate :type_in_allowed
validate :recurring_meeting_visible
private
- def type_in_allowed
- unless [StructuredMeeting.name, Meeting.name].include?(model.type)
- errors.add(:type, :inclusion)
- end
- end
-
def user_allowed_to_add
return if model.project.nil?
diff --git a/modules/meeting/app/controllers/meeting_contents_controller.rb b/modules/meeting/app/controllers/meeting_contents_controller.rb
deleted file mode 100644
index a40e4e37932..00000000000
--- a/modules/meeting/app/controllers/meeting_contents_controller.rb
+++ /dev/null
@@ -1,109 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-class MeetingContentsController < ApplicationController
- include AttachableServiceCall
- include PaginationHelper
-
- menu_item :meetings
-
- helper :watchers
- helper :wiki
- helper :meetings
- helper :meeting_contents
- helper :watchers
- helper :meetings
-
- before_action :load_and_authorize_in_optional_project
- before_action :find_meeting, :find_content
-
- def show # rubocop:disable Metrics/AbcSize
- if params[:id].present? && @content.last_journal.version == params[:id].to_i
- # Redirect links to the last version
- redirect_to project_meeting_path(@project, @meeting, tab: @content_type.sub(/^meeting_/, ""))
- return
- end
-
- # go to an old version if a version id is given
- @journaled_version = true
- @content = @content.at_version params[:id] if params[:id].present?
- render "meeting_contents/show"
- end
-
- def update # rubocop:disable Metrics/AbcSize
- call = attachable_update_call ::MeetingContents::UpdateService,
- model: @content,
- args: content_params
-
- if call.success?
- flash[:notice] = I18n.t(:notice_successful_update)
- redirect_back_or_default project_meeting_path(@project, @meeting)
- else
- flash.now[:error] = call.message
- params[:tab] ||= "minutes" if @meeting.agenda.present? && @meeting.agenda.locked?
- render "meetings/show", status: :unprocessable_entity
- end
- end
-
- def history
- # don't load text
- @content_versions = @content.journals.select("id, user_id, notes, created_at, version")
- .order(Arel.sql("version DESC"))
- .page(page_param)
- .per_page(per_page_param)
-
- render "meeting_contents/history", layout: !request.xhr?
- end
-
- def diff
- @diff = @content.diff(params[:version_to], params[:version_from])
- render "meeting_contents/diff"
- rescue ActiveRecord::RecordNotFound
- render_404
- end
-
- def default_breadcrumb
- MeetingsController.new.send(:default_breadcrumb)
- end
-
- private
-
- def find_meeting
- @meeting = Meeting.includes(:project, :author, :participants, :agenda, :minutes)
- .find(params[:meeting_id])
- @project = @meeting.project
- @author = User.current
- rescue ActiveRecord::RecordNotFound
- render_404
- end
-
- def content_params
- params.require(@content_type).permit(:text, :lock_version, :journal_notes)
- end
-end
diff --git a/modules/meeting/app/controllers/meeting_minutes_controller.rb b/modules/meeting/app/controllers/meeting_minutes_controller.rb
deleted file mode 100644
index 493ab6efcef..00000000000
--- a/modules/meeting/app/controllers/meeting_minutes_controller.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-class MeetingMinutesController < MeetingContentsController
- menu_item :meetings
-
- private
-
- def find_content
- @content = @meeting.minutes || @meeting.build_minutes
- @content_type = "meeting_minutes"
- end
-end
diff --git a/modules/meeting/app/controllers/meetings_controller.rb b/modules/meeting/app/controllers/meetings_controller.rb
index 3a49a1af0a3..7e42f08ddfa 100644
--- a/modules/meeting/app/controllers/meetings_controller.rb
+++ b/modules/meeting/app/controllers/meetings_controller.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -41,7 +42,6 @@ class MeetingsController < ApplicationController
before_action :prevent_template_destruction, only: :destroy
helper :watchers
- helper :meeting_contents
include MeetingsHelper
include Layout
include WatchersHelper
@@ -67,18 +67,14 @@ class MeetingsController < ApplicationController
:meetings
end
- def show # rubocop:disable Metrics/AbcSize
+ def show
respond_to do |format|
format.html do
html_title "#{t(:label_meeting)}: #{@meeting.title}"
- if @meeting.is_a?(StructuredMeeting)
- if @meeting.state == "cancelled"
- render_404
- else
- render(Meetings::ShowComponent.new(meeting: @meeting), layout: true)
- end
- elsif @meeting.agenda.present? && @meeting.agenda.locked?
- params[:tab] ||= "minutes"
+ if @meeting.state == "cancelled"
+ render_404
+ else
+ render(Meetings::ShowComponent.new(meeting: @meeting), layout: true)
end
end
end
@@ -256,7 +252,7 @@ class MeetingsController < ApplicationController
end
def update_title
- @meeting.update(title: structured_meeting_params[:title])
+ @meeting.update(title: meeting_params[:title])
if @meeting.errors.any?
update_header_component_via_turbo_stream(state: :edit)
@@ -270,7 +266,7 @@ class MeetingsController < ApplicationController
def update_details
call = ::Meetings::UpdateService
.new(user: current_user, model: @meeting)
- .call(structured_meeting_params)
+ .call(meeting_params)
if call.success?
update_header_component_via_turbo_stream
@@ -337,9 +333,7 @@ class MeetingsController < ApplicationController
@text = friendly_timezone_name(User.current.time_zone, period: meeting.start_time)
end
- prefix = params[:structured_meeting] ? "structured_" : ""
-
- add_caption_to_input_element_via_turbo_stream("input[name='#{prefix}meeting[start_time_hour]']",
+ add_caption_to_input_element_via_turbo_stream("input[name='meeting[start_time_hour]']",
caption: @text,
clean_other_captions: true)
@@ -420,7 +414,12 @@ class MeetingsController < ApplicationController
end
def build_meeting
- meeting = meeting_class.new
+ meeting =
+ if params[:type] == "recurring"
+ RecurringMeeting.new
+ else
+ Meeting.new
+ end
service = meeting.is_a?(RecurringMeeting) ? ::RecurringMeetings::SetAttributesService : ::Meetings::SetAttributesService
call = service
@@ -430,17 +429,6 @@ class MeetingsController < ApplicationController
@meeting = call.result
end
- def meeting_class
- case params[:type]
- when "recurring"
- RecurringMeeting
- when "structured"
- StructuredMeeting
- else
- Meeting
- end
- end
-
def global_upcoming_meetings
projects = Project.allowed_in_project(User.current, :view_meetings)
@@ -449,10 +437,8 @@ class MeetingsController < ApplicationController
def find_meeting
@meeting = Meeting
- .includes([:project, :author, { participants: :user }, :agenda, :minutes])
+ .includes([:project, :author, { participants: :user }, :sections, { agenda_items: :outcomes }])
.find(params[:id])
- rescue ActiveRecord::RecordNotFound
- render_404
end
def convert_params # rubocop:disable Metrics/AbcSize
@@ -466,7 +452,7 @@ class MeetingsController < ApplicationController
# Handle participants separately for each meeting type
@converted_params[:participants_attributes] ||= {}
- if copy_structured_meeting_participants?
+ if copy_meeting_participants?
create_participants
else
force_defaults
@@ -481,19 +467,11 @@ class MeetingsController < ApplicationController
params
.require(:meeting)
.permit(:title, :location, :start_time, :project_id,
- :duration, :start_date, :start_time_hour, :type,
+ :duration, :start_date, :start_time_hour,
participants_attributes: %i[email name invited attended user user_id meeting id])
end
end
- def structured_meeting_params
- if params[:structured_meeting].present?
- params
- .require(:structured_meeting)
- .permit(:title, :location, :start_time_hour, :duration, :start_date, :state, :lock_version)
- end
- end
-
def set_activity
@activity = Activities::Fetcher.new(User.current,
project: @project,
@@ -536,8 +514,6 @@ class MeetingsController < ApplicationController
return unless copied_from_meeting_id
@copy_from = Meeting.visible.find(copied_from_meeting_id)
- rescue ActiveRecord::RecordNotFound
- render_404
end
def copy_attributes
@@ -559,12 +535,6 @@ class MeetingsController < ApplicationController
end
def timezone_params
- meeting_params = if params[:meeting]
- params.require(:meeting)
- else
- params.require(:structured_meeting)
- end
-
- @timezone_params ||= meeting_params.permit(:start_date, :start_time_hour).compact_blank
+ @timezone_params ||= params.require(:meeting).permit(:start_date, :start_time_hour).compact_blank
end
end
diff --git a/modules/meeting/app/controllers/recurring_meetings_controller.rb b/modules/meeting/app/controllers/recurring_meetings_controller.rb
index d23b884e76f..901b5c40bef 100644
--- a/modules/meeting/app/controllers/recurring_meetings_controller.rb
+++ b/modules/meeting/app/controllers/recurring_meetings_controller.rb
@@ -310,14 +310,10 @@ class RecurringMeetingsController < ApplicationController
def find_optional_project
@project = Project.find(params[:project_id]) if params[:project_id].present?
- rescue ActiveRecord::RecordNotFound
- render_404
end
def find_meeting
@recurring_meeting = RecurringMeeting.visible.find(params[:id])
- rescue ActiveRecord::RecordNotFound
- render_404
end
def convert_params
@@ -341,14 +337,12 @@ class RecurringMeetingsController < ApplicationController
return unless copied_from_meeting_id
@copy_from = Meeting.visible.find(copied_from_meeting_id)
- rescue ActiveRecord::RecordNotFound
- render_404
end
def structured_meeting_params
- if params[:structured_meeting].present?
+ if params[:meeting].present?
params
- .require(:structured_meeting)
+ .require(:meeting)
end
end
diff --git a/modules/meeting/app/controllers/work_package_meetings_tab_controller.rb b/modules/meeting/app/controllers/work_package_meetings_tab_controller.rb
index bb92d783f02..85f127b415d 100644
--- a/modules/meeting/app/controllers/work_package_meetings_tab_controller.rb
+++ b/modules/meeting/app/controllers/work_package_meetings_tab_controller.rb
@@ -101,8 +101,6 @@ class WorkPackageMeetingsTabController < ApplicationController
def set_work_package
@work_package = WorkPackage.find(params[:work_package_id])
@project = @work_package.project # required for authorization via before_action
- rescue ActiveRecord::RecordNotFound
- render_404
end
def add_work_package_to_meeting_params
diff --git a/modules/meeting/app/forms/meeting/type.rb b/modules/meeting/app/forms/meeting/type.rb
deleted file mode 100644
index 9b055882db8..00000000000
--- a/modules/meeting/app/forms/meeting/type.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-class Meeting::Type < ApplicationForm
- form do |meeting_form|
- meeting_form.hidden(name: :type, value: @builder.object.class.name)
- end
-end
diff --git a/modules/meeting/app/helpers/meeting_contents_helper.rb b/modules/meeting/app/helpers/meeting_contents_helper.rb
deleted file mode 100644
index 9f049ca8617..00000000000
--- a/modules/meeting/app/helpers/meeting_contents_helper.rb
+++ /dev/null
@@ -1,133 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-module MeetingContentsHelper
- def can_edit_meeting_content?(content, content_type)
- authorize_for(content_type.pluralize, "update") && content.editable?
- end
-
- def saved_meeting_content_text_present?(content)
- !content.new_record? && content.text.present? && !content.text.empty?
- end
-
- def show_meeting_content_editor?(content, content_type)
- can_edit_meeting_content?(content, content_type) && (!saved_meeting_content_text_present?(content) || content.changed?)
- end
-
- def meeting_content_context_menu(content, content_type)
- menu = []
- menu << meeting_agenda_toggle_status_link(content, content_type)
- menu << meeting_content_edit_link(content_type) if can_edit_meeting_content?(content, content_type)
- menu << meeting_content_history_link(content_type, content.meeting)
-
- menu.join(" ")
- end
-
- def meeting_agenda_toggle_status_link(content, content_type)
- if content.meeting.agenda.present? && content.meeting.agenda.locked?
- open_meeting_agenda_link(content_type, content.meeting)
- else
- close_meeting_agenda_link(content_type, content.meeting)
- end
- end
-
- def close_meeting_agenda_link(content_type, meeting)
- case content_type
- when "meeting_agenda"
- content_tag :li, "", class: "toolbar-item" do
- link_to_if_authorized({ controller: "/meeting_agendas",
- action: "close",
- meeting_id: meeting },
- method: :put,
- data: { confirm: I18n.t(:text_meeting_closing_are_you_sure) },
- class: "meetings--close-meeting-button button") do
- text_with_icon(I18n.t(:label_meeting_close), "icon-locked")
- end
- end
- when "meeting_minutes"
- content_tag :li, "", class: "toolbar-item" do
- link_to_if_authorized({ controller: "/meeting_agendas",
- action: "close",
- meeting_id: meeting },
- method: :put,
- class: "button") do
- text_with_icon(I18n.t(:label_meeting_agenda_close), "icon-locked")
- end
- end
- end
- end
-
- def open_meeting_agenda_link(content_type, meeting)
- return unless content_type == "meeting_agenda"
-
- content_tag :li, "", class: "toolbar-item" do
- link_to_if_authorized({ controller: "/meeting_agendas",
- action: "open",
- meeting_id: meeting },
- method: :put,
- class: "button",
- data: { confirm: I18n.t(:text_meeting_agenda_open_are_you_sure) }) do
- text_with_icon(I18n.t(:label_meeting_open), "icon-unlocked")
- end
- end
- end
-
- def meeting_content_edit_link(_content_type)
- content_tag :li, "", class: "toolbar-item" do
- link_to "",
- class: "button button--edit-agenda",
- data: {
- action: "meeting-content#enableEditState",
- "meeting-content-target": "editButton"
- },
- accesskey: accesskey(:edit) do
- text_with_icon(I18n.t(:label_edit), "icon-edit")
- end
- end
- end
-
- def meeting_content_history_link(content_type, meeting)
- content_tag :li, "", class: "toolbar-item" do
- link_to_if_authorized({ controller: "/" + content_type.pluralize,
- action: "history",
- meeting_id: meeting },
- aria: { label: t(:label_history) },
- title: t(:label_history),
- class: "button") do
- text_with_icon(I18n.t(:label_history), "icon-activity-history")
- end
- end
- end
-
- def text_with_icon(text, icon)
- op_icon("button--icon #{icon}") +
- " " +
- content_tag("span", text, class: "button--text")
- end
-end
diff --git a/modules/meeting/app/helpers/meetings_helper.rb b/modules/meeting/app/helpers/meetings_helper.rb
index 26431138ead..09cac2c8b78 100644
--- a/modules/meeting/app/helpers/meetings_helper.rb
+++ b/modules/meeting/app/helpers/meetings_helper.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -86,8 +87,8 @@ module MeetingsHelper
request.path == meetings_path && @project.nil?
end
- def copy_structured_meeting_participants?
- @copy_from.is_a?(StructuredMeeting) && params[:meeting][:copy_participants] == "1"
+ def copy_meeting_participants?
+ params[:meeting][:copy_participants] == "1"
end
def create_participants
diff --git a/modules/meeting/app/models/activities/meeting_activity_provider.rb b/modules/meeting/app/models/activities/meeting_activity_provider.rb
index 287db51c4bb..ae96497d5e9 100644
--- a/modules/meeting/app/models/activities/meeting_activity_provider.rb
+++ b/modules/meeting/app/models/activities/meeting_activity_provider.rb
@@ -29,20 +29,9 @@
class Activities::MeetingActivityProvider < Activities::BaseActivityProvider
activity_provider_for type: "meetings",
- activities: %i[meeting meeting_content],
+ activities: %i[meeting],
permission: :view_meetings
- def extend_event_query(query)
- case activity
- when :meeting_content
- query.join(meetings_table).on(activity_journals_table[:meeting_id].eq(meetings_table[:id]))
- join_cond = journals_table[:journable_type].eq("MeetingContent")
- query.join(meeting_contents_table).on(journals_table[:journable_id].eq(meeting_contents_table[:id]).and(join_cond))
- else
- super
- end
- end
-
def event_query_projection
case activity
when :meeting
@@ -54,7 +43,6 @@ class Activities::MeetingActivityProvider < Activities::BaseActivityProvider
]
else
[
- projection_statement(meeting_contents_table, :type, "meeting_content_type"),
projection_statement(meetings_table, :id, "meeting_id"),
projection_statement(meetings_table, :title, "meeting_title"),
projection_statement(meetings_table, :project_id, "project_id")
@@ -63,7 +51,7 @@ class Activities::MeetingActivityProvider < Activities::BaseActivityProvider
end
def activitied_type
- activity == :meeting ? Meeting : MeetingContent
+ Meeting
end
def projects_reference_table
@@ -76,56 +64,30 @@ class Activities::MeetingActivityProvider < Activities::BaseActivityProvider
end
def activity_journals_table
- @activity_journals_table ||= case activity
- when :meeting
- Meeting.journal_class.arel_table
- else
- MeetingContent.journal_class.arel_table
- end
+ @activity_journals_table ||= Meeting.journal_class.arel_table
end
protected
- def event_name(event)
- case event["event_description"]
- when "Agenda closed"
- I18n.t("meeting_agenda_closed", scope: "events")
- when "Agenda opened"
- I18n.t("meeting_agenda_opened", scope: "events")
- when "Minutes created"
- I18n.t("meeting_minutes_created", scope: "events")
- else
- super
- end
- end
-
def event_title(event)
- case activity
- when :meeting
- start_time = if event["meeting_start_time"].is_a?(String)
- DateTime.parse(event["meeting_start_time"])
- else
- event["meeting_start_time"]
- end
- end_time = start_time + event["meeting_duration"].to_f.hours
+ start_time =
+ if event["meeting_start_time"].is_a?(String)
+ DateTime.parse(event["meeting_start_time"])
+ else
+ event["meeting_start_time"]
+ end
- fstart_with = format_date start_time
- fstart_without = format_time start_time, include_date: false
- fend_without = format_time end_time, include_date: false
+ end_time = start_time + event["meeting_duration"].to_f.hours
- "#{I18n.t(:label_meeting)}: #{event['meeting_title']} (#{fstart_with} #{fstart_without}-#{fend_without})"
- else
- "#{event['meeting_content_type'].constantize.model_name.human}: #{event['meeting_title']}"
- end
+ fstart_with = format_date start_time
+ fstart_without = format_time start_time, include_date: false
+ fend_without = format_time end_time, include_date: false
+
+ "#{I18n.t(:label_meeting)}: #{event['meeting_title']} (#{fstart_with} #{fstart_without}-#{fend_without})"
end
def event_type(event)
- case activity
- when :meeting
- "meeting"
- else
- event["meeting_content_type"].include?("Agenda") ? "meeting-agenda" : "meeting-minutes"
- end
+ "meeting"
end
def event_path(event)
@@ -160,10 +122,6 @@ class Activities::MeetingActivityProvider < Activities::BaseActivityProvider
@meetings_table ||= Meeting.arel_table
end
- def meeting_contents_table
- @meeting_contents_table ||= MeetingContent.arel_table
- end
-
def activity_id(event)
activity == :meeting ? event["journable_id"] : event["meeting_id"]
end
diff --git a/modules/meeting/app/models/meeting.rb b/modules/meeting/app/models/meeting.rb
index 8a939351f8e..105f95a9f97 100644
--- a/modules/meeting/app/models/meeting.rb
+++ b/modules/meeting/app/models/meeting.rb
@@ -41,6 +41,8 @@ class Meeting < ApplicationRecord
belongs_to :recurring_meeting, optional: true
has_one :scheduled_meeting, inverse_of: :meeting
+ # Legacy association to minutes, agendas, contents
+ # to be removed in 17.0
has_one :agenda, dependent: :destroy, class_name: "MeetingAgenda"
has_one :minutes, dependent: :destroy, class_name: "MeetingMinutes"
has_many :contents, -> { readonly }, class_name: "MeetingContent"
@@ -50,8 +52,10 @@ class Meeting < ApplicationRecord
class_name: "MeetingParticipant",
after_add: :send_participant_added_mail
- has_many :sections, dependent: :destroy, class_name: "MeetingSection"
- has_many :agenda_items, dependent: :destroy, class_name: "MeetingAgendaItem"
+ has_many :agenda_items, dependent: :destroy, class_name: "MeetingAgendaItem", inverse_of: :meeting
+ has_many :sections, dependent: :delete_all, class_name: "MeetingSection"
+
+ accepts_nested_attributes_for :agenda_items
scope :templated, -> { where(template: true) }
scope :not_templated, -> { where(template: false) }
@@ -91,12 +95,12 @@ class Meeting < ApplicationRecord
acts_as_searchable columns: [
"#{table_name}.title",
- "#{MeetingContent.table_name}.text",
"#{MeetingAgendaItem.table_name}.title",
- "#{MeetingAgendaItem.table_name}.notes"
+ "#{MeetingAgendaItem.table_name}.notes",
+ "#{MeetingOutcome.table_name}.notes"
],
- include: %i[contents project agenda_items],
- references: %i[meeting_contents agenda_items],
+ include: [:project, { agenda_items: :outcomes }],
+ references: %i[agenda_items outcomes],
date_column: "#{table_name}.created_at"
include Meeting::Journalized
@@ -157,10 +161,6 @@ class Meeting < ApplicationRecord
title
end
- def text
- agenda.text if agenda.present?
- end
-
def templated?
!!template
end
@@ -207,28 +207,6 @@ class Meeting < ApplicationRecord
by_start_year_month_date
end
- def close_agenda_and_copy_to_minutes!
- Meeting.transaction do
- agenda.lock!
-
- attachments = agenda.attachments.map { |a| [a, a.copy] }
- original_text = String(agenda.text)
- minutes = create_minutes(text: original_text,
- journal_notes: I18n.t("events.meeting_minutes_created"),
- attachments: attachments.map(&:last))
-
- # substitute attachment references in text to use the respective copied attachments
- updated_text = original_text.gsub(/(?<=\(\/api\/v3\/attachments\/)\d+(?=\/content\))/) do |id|
- old_id = id.to_i
- new_id = attachments.select { |a, _| a.id == old_id }.map { |_, a| a.id }.first
-
- new_id || -1
- end
-
- minutes.update text: updated_text if updated_text != original_text
- end
- end
-
alias :original_participants_attributes= :participants_attributes=
def participants_attributes=(attrs)
@@ -249,6 +227,55 @@ class Meeting < ApplicationRecord
.where(user_id: available_members)
end
+ # triggered by MeetingAgendaItem#after_create/after_destroy/after_save
+ def calculate_agenda_item_time_slots
+ current_time = start_time
+ MeetingAgendaItem.transaction do
+ changed_items = agenda_items.includes(:meeting_section).order("meeting_sections.position", :position).map do |top|
+ start_time = current_time
+ current_time += top.duration_in_minutes&.minutes || 0.minutes
+ end_time = current_time
+ top.assign_attributes(start_time:, end_time:)
+ top
+ end
+
+ # Disable optimistic locking in order to avoid causing `StaleObjectError`.
+ MeetingAgendaItem.skip_optimistic_locking do
+ MeetingAgendaItem.import(
+ changed_items,
+ on_duplicate_key_update: {
+ conflict_target: [:id],
+ columns: %i[meeting_id
+ author_id
+ title
+ notes
+ position
+ duration_in_minutes
+ start_time
+ end_time
+ created_at
+ updated_at
+ work_package_id
+ item_type
+ lock_version]
+ }
+ )
+ end
+ end
+ end
+
+ def agenda_items_sum_duration_in_minutes
+ agenda_items.sum(:duration_in_minutes)
+ end
+
+ def duration_exceeded_by_agenda_items?
+ agenda_items_sum_duration_in_minutes > (duration * 60)
+ end
+
+ def duration_exceeded_by_agenda_items_in_minutes
+ agenda_items_sum_duration_in_minutes - (duration * 60)
+ end
+
private
def add_new_participants_as_watcher
diff --git a/modules/meeting/app/models/meeting_agenda_item.rb b/modules/meeting/app/models/meeting_agenda_item.rb
index 41ecf554866..8b525f100e3 100644
--- a/modules/meeting/app/models/meeting_agenda_item.rb
+++ b/modules/meeting/app/models/meeting_agenda_item.rb
@@ -37,7 +37,7 @@ class MeetingAgendaItem < ApplicationRecord
enum :item_type, ITEM_TYPES
- belongs_to :meeting, class_name: "StructuredMeeting"
+ belongs_to :meeting
belongs_to :meeting_section, optional: false
belongs_to :work_package, class_name: "::WorkPackage"
has_one :project, through: :meeting
diff --git a/modules/meeting/app/models/meeting_outcome.rb b/modules/meeting/app/models/meeting_outcome.rb
index b0ccabc62c0..4e405d1f93a 100644
--- a/modules/meeting/app/models/meeting_outcome.rb
+++ b/modules/meeting/app/models/meeting_outcome.rb
@@ -30,6 +30,7 @@
class MeetingOutcome < ApplicationRecord
belongs_to :meeting_agenda_item
belongs_to :work_package
+ belongs_to :author, class_name: "User", optional: true
enum :kind, {
information: 0,
diff --git a/modules/meeting/app/models/structured_meeting.rb b/modules/meeting/app/models/structured_meeting.rb
deleted file mode 100644
index 27bb9af5029..00000000000
--- a/modules/meeting/app/models/structured_meeting.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-class StructuredMeeting < Meeting
- has_many :agenda_items,
- dependent: :destroy,
- foreign_key: "meeting_id",
- class_name: "MeetingAgendaItem",
- inverse_of: :meeting
- accepts_nested_attributes_for :agenda_items
-
- # triggered by MeetingAgendaItem#after_create/after_destroy/after_save
- def calculate_agenda_item_time_slots
- current_time = start_time
- MeetingAgendaItem.transaction do
- changed_items = agenda_items.includes(:meeting_section).order("meeting_sections.position", :position).map do |top|
- start_time = current_time
- current_time += top.duration_in_minutes&.minutes || 0.minutes
- end_time = current_time
- top.assign_attributes(start_time:, end_time:)
- top
- end
-
- # Disable optimistic locking in order to avoid causing `StaleObjectError`.
- MeetingAgendaItem.skip_optimistic_locking do
- MeetingAgendaItem.import(
- changed_items,
- on_duplicate_key_update: {
- conflict_target: [:id],
- columns: %i[meeting_id
- author_id
- title
- notes
- position
- duration_in_minutes
- start_time
- end_time
- created_at
- updated_at
- work_package_id
- item_type
- lock_version]
- }
- )
- end
- end
- end
-
- def agenda_items_sum_duration_in_minutes
- agenda_items.sum(:duration_in_minutes)
- end
-
- def duration_exceeded_by_agenda_items?
- agenda_items_sum_duration_in_minutes > (duration * 60)
- end
-
- def duration_exceeded_by_agenda_items_in_minutes
- agenda_items_sum_duration_in_minutes - (duration * 60)
- end
-end
diff --git a/modules/meeting/app/seeders/meetings/demo_data/meeting_series_seeder.rb b/modules/meeting/app/seeders/meetings/demo_data/meeting_series_seeder.rb
index c740bf50352..8a6ad74d00a 100644
--- a/modules/meeting/app/seeders/meetings/demo_data/meeting_series_seeder.rb
+++ b/modules/meeting/app/seeders/meetings/demo_data/meeting_series_seeder.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -51,7 +52,7 @@ module Meetings
end
def create_meeting_template!(series, model_data)
- template = StructuredMeeting.new(template_attributes(model_data))
+ template = Meeting.new(template_attributes(model_data))
template.template = true
template.recurring_meeting = series
diff --git a/modules/meeting/app/services/meeting_outcomes/set_attributes_service.rb b/modules/meeting/app/services/meeting_outcomes/set_attributes_service.rb
index bfa61e6594e..f673220c990 100644
--- a/modules/meeting/app/services/meeting_outcomes/set_attributes_service.rb
+++ b/modules/meeting/app/services/meeting_outcomes/set_attributes_service.rb
@@ -30,5 +30,10 @@
module MeetingOutcomes
class SetAttributesService < ::BaseServices::SetAttributes
+ def set_default_attributes(_params)
+ model.change_by_system do
+ model.author = user
+ end
+ end
end
end
diff --git a/modules/meeting/app/services/meetings/copy_service.rb b/modules/meeting/app/services/meetings/copy_service.rb
index 544530ecb5a..dd06a393a32 100644
--- a/modules/meeting/app/services/meetings/copy_service.rb
+++ b/modules/meeting/app/services/meetings/copy_service.rb
@@ -114,31 +114,14 @@ module Meetings
end
def copy_meeting_agenda(copy)
- if meeting.is_a?(StructuredMeeting)
- meeting.sections.each do |section|
- copy.sections << section.dup
- copied_section = copy.reload.sections.last
- section.agenda_items.each do |agenda_item|
- copied_agenda_item = agenda_item.dup
- copied_agenda_item.meeting_id = copy.id
- copied_section.agenda_items << copied_agenda_item
- end
+ meeting.sections.each do |section|
+ copy.sections << section.dup
+ copied_section = copy.reload.sections.last
+ section.agenda_items.each do |agenda_item|
+ copied_agenda_item = agenda_item.dup
+ copied_agenda_item.meeting_id = copy.id
+ copied_section.agenda_items << copied_agenda_item
end
- else
- MeetingAgenda.create!(
- meeting: copy,
- author: user,
- text: meeting.agenda&.text,
- journal_notes: I18n.t("meeting.copied", id: meeting.id)
- )
- end
- end
-
- def copy_structured_meeting_participants(copy)
- meeting.participants.each do |participant|
- copied_participant = participant.dup
- copied_participant.meeting_id = copy.id
- copy.participants << copied_participant
end
end
end
diff --git a/modules/meeting/app/services/meetings/create_service.rb b/modules/meeting/app/services/meetings/create_service.rb
index b617d8eab90..ca377ee673f 100644
--- a/modules/meeting/app/services/meetings/create_service.rb
+++ b/modules/meeting/app/services/meetings/create_service.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -31,17 +32,6 @@ module Meetings
class CreateService < ::BaseServices::Create
protected
- def instance(params)
- # Setting the #type as attributes will not work
- # as the STI instance is not changed without using e.g., +becomes!+
- case params.delete(:type)
- when "StructuredMeeting"
- StructuredMeeting.new
- else
- Meeting.new
- end
- end
-
def after_perform(call)
if call.success? && Journal::NotificationConfiguration.active?
meeting = call.result
diff --git a/modules/meeting/app/services/recurring_meetings/create_service.rb b/modules/meeting/app/services/recurring_meetings/create_service.rb
index 6cf2b3d0831..19cd50bdb82 100644
--- a/modules/meeting/app/services/recurring_meetings/create_service.rb
+++ b/modules/meeting/app/services/recurring_meetings/create_service.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -44,7 +45,6 @@ module RecurringMeetings
def create_meeting_template(recurring_meeting)
params = @template_params.merge(
- type: "StructuredMeeting",
template: true,
recurring_meeting:,
project: recurring_meeting.project
diff --git a/modules/meeting/app/views/meeting_contents/_form.html.erb b/modules/meeting/app/views/meeting_contents/_form.html.erb
deleted file mode 100644
index a38011010ea..00000000000
--- a/modules/meeting/app/views/meeting_contents/_form.html.erb
+++ /dev/null
@@ -1,59 +0,0 @@
-<%#-- copyright
-OpenProject is an open source project management software.
-Copyright (C) the OpenProject GmbH
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License version 3.
-
-OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-Copyright (C) 2006-2013 Jean-Philippe Lang
-Copyright (C) 2010-2013 the ChiliProject Team
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-See COPYRIGHT and LICENSE files for more details.
-
-++#%>
-
-<%= labelled_tabular_form_for content, :url => {:controller => '/' + content_type.pluralize, :action => 'update', :meeting_id => content.meeting}, :html => {:id => "#{content_type}_form", :method => :put} do |f| %>
- <%= error_messages_for content_type %>
-
- <% resource = ::API::V3::MeetingContents::MeetingContentRepresenter.new(content, current_user: current_user, embed_links: true) %>
-
-
- <%=
- f.text_area(
- :text,
- class: 'wiki-edit wiki-toolbar',
- no_label: true,
- resource: resource,
- label_options: { class: 'hidden-for-sighted' },
- with_text_formatting: true
- )
- %>
-
- <%= f.hidden_field :lock_version %>
-
-
<%= f.text_field :journal_notes, label: :comments %>
- <%= styled_button_tag t(:button_save),
- class: '-primary -with-icon icon-checkmark button--save-agenda',
- data: { disable_with: I18n.t(:label_loading) } %>
- <%= link_to t(:button_cancel),
- "#",
- data: {
- action: 'meeting-content#cancelEditState'
- },
- class: 'button -with-icon icon-cancel button--cancel-agenda' %>
-<% end %>
diff --git a/modules/meeting/app/views/meeting_contents/_show.html.erb b/modules/meeting/app/views/meeting_contents/_show.html.erb
deleted file mode 100644
index 36a229c7fc3..00000000000
--- a/modules/meeting/app/views/meeting_contents/_show.html.erb
+++ /dev/null
@@ -1,76 +0,0 @@
-<%#-- copyright
-OpenProject is an open source project management software.
-Copyright (C) the OpenProject GmbH
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License version 3.
-
-OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-Copyright (C) 2006-2013 Jean-Philippe Lang
-Copyright (C) 2010-2013 the ChiliProject Team
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-See COPYRIGHT and LICENSE files for more details.
-
-++#%>
-
-<%
- tab ||= locals[:tab_contents] if defined? locals
- if tab.present?
- content = tab[:content]
- content_type = tab[:content_type]
- end
--%>
-
-
-
- <%= toolbar title: t(:"label_#{content_type}") do %>
- <%= raw meeting_content_context_menu content, content_type %>
- <% end %>
-
-
- <% if can_edit_meeting_content?(content, content_type) -%>
-
- <%= render(partial: "meeting_contents/form", locals: { content: content, content_type: content_type }) %>
-
- <% end -%>
-
-
- <% if saved_meeting_content_text_present?(content) -%>
-
- <%= format_text(content.text, object: @meeting) %>
-
- <% else -%>
- <%= no_results_box %>
- <% end -%>
-
-
-<%# We cannot render attachments for journaled content %>
- <% unless local_assigns[:journaled_version] %>
- <% resource = ::API::V3::MeetingContents::MeetingContentRepresenter.new(content, current_user: current_user, embed_links: true) %>
- <%= list_attachments(
- resource,
- data: {
- "meeting-content-target": "attachments"
- }
- ) %>
- <% end %>
-
diff --git a/modules/meeting/app/views/meeting_contents/diff.html.erb b/modules/meeting/app/views/meeting_contents/diff.html.erb
deleted file mode 100644
index 693e3d92b45..00000000000
--- a/modules/meeting/app/views/meeting_contents/diff.html.erb
+++ /dev/null
@@ -1,55 +0,0 @@
-<%#-- copyright
-OpenProject is an open source project management software.
-Copyright (C) the OpenProject GmbH
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License version 3.
-
-OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-Copyright (C) 2006-2013 Jean-Philippe Lang
-Copyright (C) 2010-2013 the ChiliProject Team
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-See COPYRIGHT and LICENSE files for more details.
-
-++#%>
-
-<% html_title "#{t(:label_meeting_diff)}: #{@meeting.title}" %>
-<%= toolbar title: t(:"label_#{@content_type}"),
- link_to: link_to(@meeting, @meeting) do %>
- <% if authorize_for(@content_type.pluralize, :history) %>
-
- <%= link_to({ controller: "/#{@content_type.pluralize}", action: "history", meeting_id: @meeting }, class: "button") do %>
- <%= op_icon("button--icon icon-wiki") %>
- <%= t(:label_history) %>
- <% end %>
-
- <% end %>
-<% end %>
-
-
-<%= t(:label_version) %> <%= link_to @diff.content_from.version, send(:"#{@content_type}_version_path", @meeting, @diff.content_from.version) %>
-(<%= link_to_user(@diff.content_from.user) %>, <%= format_time(@diff.content_from.created_at) %>)
-→
-<%= t(:label_version) %> <%= link_to @diff.content_to.version, send(:"#{@content_type}_version_path", @meeting, @diff.content_to.version) %>/<%= @content.last_journal&.version || 0 %>
-(<%= link_to_user(@diff.content_to.user) %>, <%= format_time(@diff.content_to.created_at) %>)
-
-
-
-
-
-<%= simple_format_without_paragraph @diff.to_html %>
-
diff --git a/modules/meeting/app/views/meeting_contents/history.html.erb b/modules/meeting/app/views/meeting_contents/history.html.erb
deleted file mode 100644
index 261849c3f42..00000000000
--- a/modules/meeting/app/views/meeting_contents/history.html.erb
+++ /dev/null
@@ -1,148 +0,0 @@
-<%#-- copyright
-OpenProject is an open source project management software.
-Copyright (C) the OpenProject GmbH
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License version 3.
-
-OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-Copyright (C) 2006-2013 Jean-Philippe Lang
-Copyright (C) 2010-2013 the ChiliProject Team
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-See COPYRIGHT and LICENSE files for more details.
-
-++#%>
-
-<% html_title "#{t(:label_history)}: #{@meeting.title}" %>
-
-<%= toolbar title: t(:"label_#{@content_type}"),
- link_to: link_to(@meeting, @meeting) %>
-
-
<%= t(:label_history) %>
-
-<%= form_tag({ action: "diff" }, method: :get) do %>
-
-
-
-
-
-
-
-
-
-
-
-
-
- |
-
- |
-
-
- |
-
-
- |
-
-
- |
-
-
- |
-
-
- |
-
-
-
- <% show_diff = @content_versions.size > 1 %>
- <% @content_versions.each_with_index do |content_version,index| %>
-
- |
- <%= if content_version.version == @content.last_journal.version
- link_to(content_version.version, tab_meeting_path(@meeting, tab: @content_type.sub(/^meeting_/, "")), id: "version-#{content_version.version}")
- else
- link_to(content_version.version, send(:"#{@content_type}_version_path", @meeting, content_version.version), id: "version-#{content_version.version}")
- end %>
- |
-
- <% if show_diff && (index < @content_versions.size-1) %>
- <%= radio_button_tag(
- "version_from",
- content_version.version,
- (index == 0),
- class: "meetings--checkbox-version-to",
- data: {
- "journal-history-target": "fromVersion",
- action: "journal-history#selectToVersion"
- }
- ) %>
-
- <% end %>
- |
-
- <% if show_diff && (index > 0) %>
- <%= radio_button_tag(
- "version_to",
- content_version.version,
- (index == 1),
- data: {
- "journal-history-target": "toVersion",
- action: "journal-history#selectFromVersion"
- }
- ) %>
-
- <% end %>
- |
- <%= format_time(content_version.created_at) %> |
- <%= User.find content_version.user_id %> |
- <%= h content_version.notes %> |
-
- <% end %>
-
-
-
-
-
-<%= styled_button_tag t(:label_view_diff), class: "-small -primary" if show_diff %>
-<%= pagination_links_full @content_versions %>
-<% end %>
diff --git a/modules/meeting/app/views/meetings/_form.html.erb b/modules/meeting/app/views/meetings/_form.html.erb
deleted file mode 100644
index 687604e9aeb..00000000000
--- a/modules/meeting/app/views/meetings/_form.html.erb
+++ /dev/null
@@ -1,156 +0,0 @@
-<%#-- copyright
-OpenProject is an open source project management software.
-Copyright (C) the OpenProject GmbH
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License version 3.
-
-OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-Copyright (C) 2006-2013 Jean-Philippe Lang
-Copyright (C) 2010-2013 the ChiliProject Team
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-See COPYRIGHT and LICENSE files for more details.
-
-++#%>
-
-<%= error_messages_for "meeting" %>
-
-
-
-
- <%= f.text_field :title, required: true, size: 60, container_class: "-wide" %>
-
-
- <% copy_from = local_assigns[:copy_from] %>
- <% if copy_from.present? %>
- <%= f.hidden_field :type, value: copy_from.type %>
- <%= hidden_field_tag "copied_from_meeting_id", copy_from.id %>
-
-
- <% elsif @meeting.new_record? %>
- <%= f.hidden_field :type, value: "Meeting" %>
- <% end %>
-
- <% if global_meeting_create_context? %>
-
- <% end %>
-
-
- <%= f.text_field :location, size: 60, container_class: "-wide" %>
-
-
-
-
-
-
- <%= render partial: "meetings/participants_section" %>
-
-
-
diff --git a/modules/meeting/app/views/meetings/new.html.erb b/modules/meeting/app/views/meetings/new.html.erb
deleted file mode 100644
index 1ea14428b71..00000000000
--- a/modules/meeting/app/views/meetings/new.html.erb
+++ /dev/null
@@ -1,73 +0,0 @@
-<%#-- copyright
-OpenProject is an open source project management software.
-Copyright (C) the OpenProject GmbH
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License version 3.
-
-OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-Copyright (C) 2006-2013 Jean-Philippe Lang
-Copyright (C) 2010-2013 the ChiliProject Team
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-See COPYRIGHT and LICENSE files for more details.
-
-++#%>
-<% copy_from = local_assigns[:copy_from] %>
-<% if copy_from
- page_title = t("meeting.copy.title", title: copy_from.title)
- breadcrumb_element = t("meeting.copy.title", title: "
#{copy_from.title}")
- else
- page_title = t(:label_meeting_new)
- breadcrumb_element = t(:label_meeting_new)
- end %>
-
-<% html_title page_title %>
-
-<%=
- render(Primer::OpenProject::PageHeader.new) do |header|
- header.with_title { page_title }
- header.with_breadcrumbs(
- [if @project.present?
- { href: project_overview_path(@project.id), text: @project.name }
- else
- { href: home_path, text: organization_name }
- end,
- { href: @project.present? ? project_meetings_path(@project.id) : meetings_path,
- text: I18n.t(:label_meeting_plural) },
- breadcrumb_element.html_safe]
- )
- end
-%>
-
-<%= labelled_tabular_form_for @meeting,
- as: :meeting,
- url: { controller: "/meetings", action: "create", project_id: @project },
- html: {
- id: "meeting-form",
- target: "_top",
- data: {
- turbo: false,
- controller: "refresh-on-form-changes",
- "refresh-on-form-changes-target": "form",
- "refresh-on-form-changes-turbo-stream-url-value": new_meeting_url
- }
- } do |f| -%>
- <%= render partial: "form", locals: { f:, copy_from: } %>
- <%= styled_button_tag t(:button_create), class: "-primary" %>
- <%= link_to t(:button_cancel), { action: "index", project_id: @project },
- class: "button" %>
-<% end %>
diff --git a/modules/meeting/app/views/meetings/show.html.erb b/modules/meeting/app/views/meetings/show.html.erb
deleted file mode 100644
index 85880071451..00000000000
--- a/modules/meeting/app/views/meetings/show.html.erb
+++ /dev/null
@@ -1,154 +0,0 @@
-<%#-- copyright
-OpenProject is an open source project management software.
-Copyright (C) the OpenProject GmbH
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License version 3.
-
-OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-Copyright (C) 2006-2013 Jean-Philippe Lang
-Copyright (C) 2010-2013 the ChiliProject Team
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-See COPYRIGHT and LICENSE files for more details.
-
-++#%>
-
-<%= toolbar title: t(:label_meeting),
- link_to: link_to(@meeting),
- html: { class: "meeting--main-toolbar -with-dropdown" } do %>
- <% unless User.current.anonymous? %>
-
-
- <%= watcher_link @meeting, User.current %>
-
-
- <% end %>
- <% if authorize_for(:meetings, :edit) %>
-
- <%= link_to(edit_project_meeting_path(@project, @meeting), class: "button", accesskey: accesskey(:edit)) do %>
- <%= op_icon("button--icon icon-edit") %>
- <%= t(:button_edit) %>
- <% end %>
-
- <% end %>
-
-
-
- <%= op_icon("button--icon icon-show-more") %>
-
-
-
-<% end %>
-
-
-
-
- <%= avatar(@meeting.author) %>
-
<%= authoring @meeting.created_at, @meeting.author %>
-
-
-
- <%= Meeting.human_attribute_name(:start_time) %>: <%= format_date @meeting.start_time %> <%= format_time @meeting.start_time, include_date: false %>
- - <%= format_time @meeting.end_time, include_date: false %> <%= formatted_time_zone_offset %>
-
-
-
- <%= Meeting.human_attribute_name(:location) %>: <%= auto_link(h(@meeting.location), link: :all, html: { target: "_blank" }) %>
-
-
-
-
- <%= Meeting.human_attribute_name(:participants_invited) %>: <%= format_participant_list @meeting.participants.invited %>
-
-
-
-
- <%= Meeting.human_attribute_name(:participants_attended) %>: <%= format_participant_list @meeting.participants.attended %>
-
-
-
-
-
-<%= render_tabs(
- [
- { name: "agenda",
- action: :create_meeting_agendas,
- partial: "meeting_contents/show",
- path: meeting_agenda_path(@meeting),
- label: :label_meeting_agenda,
- content: @meeting.agenda || @meeting.build_agenda,
- content_type: "meeting_agenda" },
- {
- name: "minutes",
- action: :create_meeting_minutes,
- partial: "meeting_contents/show",
- path: meeting_minutes_path(@meeting),
- label: :label_meeting_minutes, content: @meeting.minutes || @meeting.build_minutes,
- content_type: "meeting_minutes"
- }
- ]
- ) %>
-
-<% if @meeting.journals.changing.present? %>
-
-
<%= t(:label_history) %>
- <% @meeting.journals.each do |journal| %>
- <%= render_meeting_journal @meeting, journal %>
- <% end %>
-
-<% end %>
diff --git a/modules/meeting/config/locales/crowdin/af.yml b/modules/meeting/config/locales/crowdin/af.yml
index 1113c17b775..bf00967e1ee 100644
--- a/modules/meeting/config/locales/crowdin/af.yml
+++ b/modules/meeting/config/locales/crowdin/af.yml
@@ -70,11 +70,9 @@ af:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Meeting"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ af:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/ar.yml b/modules/meeting/config/locales/crowdin/ar.yml
index 55d73ae2ef0..707b4581537 100644
--- a/modules/meeting/config/locales/crowdin/ar.yml
+++ b/modules/meeting/config/locales/crowdin/ar.yml
@@ -74,11 +74,9 @@ ar:
invalid_time_format: "ليس وقتًا صالحًا. التصميم المطلوب: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "الاجتماع"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "جدول الأعمال"
- meeting_minutes: "محضر الجلسة"
meeting_section: "Section"
activity:
filter:
@@ -233,9 +231,6 @@ ar:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/az.yml b/modules/meeting/config/locales/crowdin/az.yml
index b0cba020d43..5e1f8a2ecb8 100644
--- a/modules/meeting/config/locales/crowdin/az.yml
+++ b/modules/meeting/config/locales/crowdin/az.yml
@@ -70,11 +70,9 @@ az:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Meeting"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ az:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/be.yml b/modules/meeting/config/locales/crowdin/be.yml
index 75d27ac9df6..f35bc3fa9d0 100644
--- a/modules/meeting/config/locales/crowdin/be.yml
+++ b/modules/meeting/config/locales/crowdin/be.yml
@@ -72,11 +72,9 @@ be:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Meeting"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Раздзел"
activity:
filter:
@@ -225,9 +223,6 @@ be:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/bg.yml b/modules/meeting/config/locales/crowdin/bg.yml
index c3c1397bdb6..98a47358cf9 100644
--- a/modules/meeting/config/locales/crowdin/bg.yml
+++ b/modules/meeting/config/locales/crowdin/bg.yml
@@ -70,11 +70,9 @@ bg:
invalid_time_format: "не е валидно време. Задължителен формат: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "среща"
+ meeting: "One-time meeting"
meeting_agenda_item: "Точка от дневния ред"
meeting_agenda: "Дневен ред"
- meeting_minutes: "Минути"
meeting_section: "Раздел"
activity:
filter:
@@ -217,9 +215,6 @@ bg:
new_date_time: "Нова дата/час"
label_mail_all_participants: "Изпрати имейл до всички участници"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/ca.yml b/modules/meeting/config/locales/crowdin/ca.yml
index a65f0d1ae37..c1fb78fa9fe 100644
--- a/modules/meeting/config/locales/crowdin/ca.yml
+++ b/modules/meeting/config/locales/crowdin/ca.yml
@@ -70,11 +70,9 @@ ca:
invalid_time_format: "no és una hora vàlida. El format requerit és: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Reunió"
+ meeting: "One-time meeting"
meeting_agenda_item: "Element d'agenda"
meeting_agenda: "Agenda"
- meeting_minutes: "Acta"
meeting_section: "Secció"
activity:
filter:
@@ -217,9 +215,6 @@ ca:
new_date_time: "Data/hora nova"
label_mail_all_participants: "Envia un correu a tots els participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/ckb-IR.yml b/modules/meeting/config/locales/crowdin/ckb-IR.yml
index 420191c7d02..0734d03c25a 100644
--- a/modules/meeting/config/locales/crowdin/ckb-IR.yml
+++ b/modules/meeting/config/locales/crowdin/ckb-IR.yml
@@ -70,11 +70,9 @@ ckb-IR:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Meeting"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ ckb-IR:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/cs.yml b/modules/meeting/config/locales/crowdin/cs.yml
index bf45a4b194d..958c8e2ffd9 100644
--- a/modules/meeting/config/locales/crowdin/cs.yml
+++ b/modules/meeting/config/locales/crowdin/cs.yml
@@ -72,11 +72,9 @@ cs:
invalid_time_format: "není platný čas. Požadovaný formát: HH:MM"
models:
recurring_meeting: "Opakovaná schůzka"
- structured_meeting: "Jednorázová schůzka"
- meeting: "Schůzka"
+ meeting: "One-time meeting"
meeting_agenda_item: "Pořad jednání"
meeting_agenda: "Agenda"
- meeting_minutes: "Zápis"
meeting_section: "Sekce"
activity:
filter:
@@ -225,9 +223,6 @@ cs:
new_date_time: "Nové datum/čas"
label_mail_all_participants: "Poslat e-mail všem účastníkům"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Strukturované"
one_time: "Jednorázový"
recurring: "Opakované"
recurring_text: "Vytvořte sérii schůzek s dynamickou šablonou pro každou událost."
@@ -341,7 +336,7 @@ cs:
notice_meeting_updated: "Tato stránka byla aktualizována někým jiným. Pro zobrazení změn znovu načtena."
permission_create_meetings: "Vytvořit schůzku\n"
permission_edit_meetings: "Upravit schůzku"
- permission_delete_meetings: "Odstranit schůzky"
+ permission_delete_meetings: "Smazat schůzku"
permission_view_meetings: "Zobrazit schůzky"
permission_create_meeting_agendas: "Vytvořit agendy schůzek"
permission_create_meeting_agendas_explanation: "Umožňuje upravovat obsah programu klasické schůzky."
diff --git a/modules/meeting/config/locales/crowdin/da.yml b/modules/meeting/config/locales/crowdin/da.yml
index 1e55cad97f9..989c69c6d0c 100644
--- a/modules/meeting/config/locales/crowdin/da.yml
+++ b/modules/meeting/config/locales/crowdin/da.yml
@@ -70,11 +70,9 @@ da:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Møde"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Dagsorden"
- meeting_minutes: "Referat"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ da:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/de.yml b/modules/meeting/config/locales/crowdin/de.yml
index a8f02b4bc48..eaab85b395b 100644
--- a/modules/meeting/config/locales/crowdin/de.yml
+++ b/modules/meeting/config/locales/crowdin/de.yml
@@ -70,11 +70,9 @@ de:
invalid_time_format: "ist keine gültige Zeit. Erforderliches Format: HH:MM"
models:
recurring_meeting: "Wiederkehrende Besprechung"
- structured_meeting: "Einmalige Besprechung"
- meeting: "Besprechung"
+ meeting: "One-time meeting"
meeting_agenda_item: "Tagesordnungspunkt"
meeting_agenda: "Agenda"
- meeting_minutes: "Protokoll"
meeting_section: "Abschnitt"
activity:
filter:
@@ -217,9 +215,6 @@ de:
new_date_time: "Neue(s) Datum/Zeit"
label_mail_all_participants: "E-Mail an alle Teilnehmer senden"
types:
- classic: "Klassisch (nicht unterstützt)"
- classic_text: "Hinweis: Klassische Meetings werden in der nächsten Version von OpenProject entfernt."
- structured: "Dynamisch"
one_time: "Einmalig"
recurring: "Terminserie"
recurring_text: "Erstellen Sie eine Terminserie mit einer dynamischen Vorlage und wiederkehrenden Besprechungen."
diff --git a/modules/meeting/config/locales/crowdin/el.yml b/modules/meeting/config/locales/crowdin/el.yml
index a4721012abe..d517416a27a 100644
--- a/modules/meeting/config/locales/crowdin/el.yml
+++ b/modules/meeting/config/locales/crowdin/el.yml
@@ -70,11 +70,9 @@ el:
invalid_time_format: "δεν είναι έγκυρη ώρα. Απαιτούμενη μορφοποίηση: ΩΩ:ΛΛ"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Συνάντηση"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Ατζέντα"
- meeting_minutes: "Πρακτικά"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ el:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/eo.yml b/modules/meeting/config/locales/crowdin/eo.yml
index f34cbd19092..ef127e9207e 100644
--- a/modules/meeting/config/locales/crowdin/eo.yml
+++ b/modules/meeting/config/locales/crowdin/eo.yml
@@ -70,11 +70,9 @@ eo:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Meeting"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ eo:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/es.yml b/modules/meeting/config/locales/crowdin/es.yml
index 9480e1b6c09..2ea302c1a24 100644
--- a/modules/meeting/config/locales/crowdin/es.yml
+++ b/modules/meeting/config/locales/crowdin/es.yml
@@ -70,11 +70,9 @@ es:
invalid_time_format: "no es una hora válida. Formato requerido: HH:MM"
models:
recurring_meeting: "Reunión periódica"
- structured_meeting: "Reunión única"
- meeting: "Reunión"
+ meeting: "Reunión única"
meeting_agenda_item: "Puntos de Agenda"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutas"
meeting_section: "Sección"
activity:
filter:
diff --git a/modules/meeting/config/locales/crowdin/et.yml b/modules/meeting/config/locales/crowdin/et.yml
index 64754d4cb57..92fb3912f81 100644
--- a/modules/meeting/config/locales/crowdin/et.yml
+++ b/modules/meeting/config/locales/crowdin/et.yml
@@ -70,11 +70,9 @@ et:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Koosolek"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Päevakava"
- meeting_minutes: "Minutit"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ et:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/eu.yml b/modules/meeting/config/locales/crowdin/eu.yml
index 8f42af6048f..6761be6bc9b 100644
--- a/modules/meeting/config/locales/crowdin/eu.yml
+++ b/modules/meeting/config/locales/crowdin/eu.yml
@@ -70,11 +70,9 @@ eu:
invalid_time_format: "ez da baliozko ordua. Eskatzen den formatua: OO:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Hitzordua"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Aktak"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ eu:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/fa.yml b/modules/meeting/config/locales/crowdin/fa.yml
index 762a457007f..8a5d9b62b5c 100644
--- a/modules/meeting/config/locales/crowdin/fa.yml
+++ b/modules/meeting/config/locales/crowdin/fa.yml
@@ -70,11 +70,9 @@ fa:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "جلسه"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "دستور کار"
- meeting_minutes: "دقیقه ها"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ fa:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/fi.yml b/modules/meeting/config/locales/crowdin/fi.yml
index eb93eb0f076..b17a867b772 100644
--- a/modules/meeting/config/locales/crowdin/fi.yml
+++ b/modules/meeting/config/locales/crowdin/fi.yml
@@ -70,11 +70,9 @@ fi:
invalid_time_format: "ei ole kelvollinen aika. Vaadittava muoto: TT:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Kokous"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Esityslista"
- meeting_minutes: "Pöytäkirja"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ fi:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/fil.yml b/modules/meeting/config/locales/crowdin/fil.yml
index 00885d93a85..177f9818089 100644
--- a/modules/meeting/config/locales/crowdin/fil.yml
+++ b/modules/meeting/config/locales/crowdin/fil.yml
@@ -70,11 +70,9 @@ fil:
invalid_time_format: "ay hindi balidong oras. Ang hinihinging format ay: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Pagpupulong"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ fil:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/fr.yml b/modules/meeting/config/locales/crowdin/fr.yml
index b8ae4ca1d4e..3e1504ae040 100644
--- a/modules/meeting/config/locales/crowdin/fr.yml
+++ b/modules/meeting/config/locales/crowdin/fr.yml
@@ -70,11 +70,9 @@ fr:
invalid_time_format: "n’est pas une heure valide. Format requis : HH:MM"
models:
recurring_meeting: "Réunion récurrente"
- structured_meeting: "Réunion ponctuelle"
- meeting: "Réunion"
+ meeting: "Réunion ponctuelle"
meeting_agenda_item: ""
meeting_agenda: "Ordre du jour"
- meeting_minutes: "Compte-rendu"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ fr:
new_date_time: "Nouvelle date/heure"
label_mail_all_participants: "Envoyer un mail à tous les participants"
types:
- classic: "Classique (non pris en charge)"
- classic_text: "Note : les réunions classiques seront supprimées dans la prochaine version d'OpenProject."
- structured: "Structurée"
one_time: "Ponctuelle"
recurring: "Récurrente"
recurring_text: "Créez une série de réunions avec un modèle dynamique pour chaque occurrence."
diff --git a/modules/meeting/config/locales/crowdin/he.yml b/modules/meeting/config/locales/crowdin/he.yml
index 4c536423b50..c54c0f1e73e 100644
--- a/modules/meeting/config/locales/crowdin/he.yml
+++ b/modules/meeting/config/locales/crowdin/he.yml
@@ -72,11 +72,9 @@ he:
invalid_time_format: "תבנית הזמן אינה תקינה. התבנית הנדרשת היא: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "פגישה"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "סדר היום"
- meeting_minutes: "דקות"
meeting_section: "Section"
activity:
filter:
@@ -225,9 +223,6 @@ he:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/hi.yml b/modules/meeting/config/locales/crowdin/hi.yml
index 364014c2466..05daf999429 100644
--- a/modules/meeting/config/locales/crowdin/hi.yml
+++ b/modules/meeting/config/locales/crowdin/hi.yml
@@ -70,11 +70,9 @@ hi:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Meeting"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ hi:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/hr.yml b/modules/meeting/config/locales/crowdin/hr.yml
index 639952337f7..1967eb6f5ec 100644
--- a/modules/meeting/config/locales/crowdin/hr.yml
+++ b/modules/meeting/config/locales/crowdin/hr.yml
@@ -71,11 +71,9 @@ hr:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Sastanak"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Dnevni red"
- meeting_minutes: "Minute"
meeting_section: "Section"
activity:
filter:
@@ -221,9 +219,6 @@ hr:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/hu.yml b/modules/meeting/config/locales/crowdin/hu.yml
index 8d85ed6c45d..546b9306443 100644
--- a/modules/meeting/config/locales/crowdin/hu.yml
+++ b/modules/meeting/config/locales/crowdin/hu.yml
@@ -70,11 +70,9 @@ hu:
invalid_time_format: "nem egy érvényes időpont. Előírt formátum: óó:pp"
models:
recurring_meeting: "Ismétlődő megbeszélés"
- structured_meeting: "One-time meeting"
- meeting: "Megbeszélés"
+ meeting: "One-time meeting"
meeting_agenda_item: "Napirendi pont"
meeting_agenda: "Napirend"
- meeting_minutes: "Jegyzőkönyv"
meeting_section: "Szekció "
activity:
filter:
@@ -217,9 +215,6 @@ hu:
new_date_time: "Új időpont"
label_mail_all_participants: "Email küldése minden résztvevőnek"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/id.yml b/modules/meeting/config/locales/crowdin/id.yml
index 5d6baefc6c8..84ca498ba8a 100644
--- a/modules/meeting/config/locales/crowdin/id.yml
+++ b/modules/meeting/config/locales/crowdin/id.yml
@@ -69,11 +69,9 @@ id:
invalid_time_format: "bukanlah waktu yang valid. Format seharusnya; JJ:MM"
models:
recurring_meeting: "Rapat berulang"
- structured_meeting: "Rapat satu waktu"
- meeting: "Rapat"
+ meeting: "One-time meeting"
meeting_agenda_item: "Item agenda"
meeting_agenda: "Agenda"
- meeting_minutes: "Laporan"
meeting_section: "Bagian"
activity:
filter:
@@ -213,9 +211,6 @@ id:
new_date_time: "Tanggal/waktu baru"
label_mail_all_participants: "Kirim email ke semua peserta"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/it.yml b/modules/meeting/config/locales/crowdin/it.yml
index 55df9b8d62b..448ad2018e2 100644
--- a/modules/meeting/config/locales/crowdin/it.yml
+++ b/modules/meeting/config/locales/crowdin/it.yml
@@ -70,11 +70,9 @@ it:
invalid_time_format: "non è un tempo valido. Formato richiesto: HH: mm"
models:
recurring_meeting: "Riunione ricorrente"
- structured_meeting: "Riunione una tantum"
- meeting: "Riunione"
+ meeting: "Riunione una tantum"
meeting_agenda_item: "Attività"
meeting_agenda: "Ordine del giorno"
- meeting_minutes: "Verbali"
meeting_section: "Sezione"
activity:
filter:
@@ -217,9 +215,6 @@ it:
new_date_time: "Nuova data/ora"
label_mail_all_participants: "Invia e-mail a tutti i partecipanti"
types:
- classic: "Classica (non supportata)"
- classic_text: "Nota: le riunioni classiche verranno rimosse nella prossima versione di OpenProject."
- structured: "Strutturato"
one_time: "Una tantum"
recurring: "Ricorrente"
recurring_text: "Crea una serie di riunioni con modello dinamico per ogni ricorrenza."
diff --git a/modules/meeting/config/locales/crowdin/ja.yml b/modules/meeting/config/locales/crowdin/ja.yml
index 9ce5c92d751..9198c371870 100644
--- a/modules/meeting/config/locales/crowdin/ja.yml
+++ b/modules/meeting/config/locales/crowdin/ja.yml
@@ -69,11 +69,9 @@ ja:
invalid_time_format: "有効な時間ではありません。必要なフォーマット: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "会議"
+ meeting: "One-time meeting"
meeting_agenda_item: "議題項目"
meeting_agenda: "アジェンダ"
- meeting_minutes: "議事録"
meeting_section: "セクション"
activity:
filter:
@@ -213,9 +211,6 @@ ja:
new_date_time: "新しい日付/時刻"
label_mail_all_participants: "すべての参加者にメールを送信"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/ka.yml b/modules/meeting/config/locales/crowdin/ka.yml
index 09f0cca68b2..7efe7341e53 100644
--- a/modules/meeting/config/locales/crowdin/ka.yml
+++ b/modules/meeting/config/locales/crowdin/ka.yml
@@ -70,11 +70,9 @@ ka:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "შეხვედრა"
+ meeting: "One-time meeting"
meeting_agenda_item: "განრიგის პუნქტი"
meeting_agenda: "დღის განრიგი"
- meeting_minutes: "წუთი"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ ka:
new_date_time: "ახალი თარიღი/დრო"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/kk.yml b/modules/meeting/config/locales/crowdin/kk.yml
index 54c5f2ddbdc..1ba246d14e0 100644
--- a/modules/meeting/config/locales/crowdin/kk.yml
+++ b/modules/meeting/config/locales/crowdin/kk.yml
@@ -70,11 +70,9 @@ kk:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Meeting"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ kk:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/ko.yml b/modules/meeting/config/locales/crowdin/ko.yml
index 6dcd7435ad2..d47879081b1 100644
--- a/modules/meeting/config/locales/crowdin/ko.yml
+++ b/modules/meeting/config/locales/crowdin/ko.yml
@@ -69,11 +69,9 @@ ko:
invalid_time_format: "은(는) 유효한 시간이 아닙니다. 필요한 형식: HH:MM"
models:
recurring_meeting: "반복 미팅"
- structured_meeting: "일회성 미팅"
- meeting: "미팅"
+ meeting: "일회성 미팅"
meeting_agenda_item: "의제 항목"
meeting_agenda: "의제"
- meeting_minutes: "의사록"
meeting_section: "섹션"
activity:
filter:
@@ -213,9 +211,6 @@ ko:
new_date_time: "새로운 날짜/시간"
label_mail_all_participants: "모든 참가자에게 이메일 보내기"
types:
- classic: "클래식(지원되지 않음)"
- classic_text: "참고: 클래식 미팅은 다음 버전의 OpenProject에서 제거됩니다."
- structured: "구조화됨"
one_time: "일회성"
recurring: "반복"
recurring_text: "각 항목에 대해 다이내믹 템플릿을 사용하여 미팅 시리즈를 만듭니다."
diff --git a/modules/meeting/config/locales/crowdin/lt.yml b/modules/meeting/config/locales/crowdin/lt.yml
index 9dad0423637..d1d038394c1 100644
--- a/modules/meeting/config/locales/crowdin/lt.yml
+++ b/modules/meeting/config/locales/crowdin/lt.yml
@@ -72,11 +72,9 @@ lt:
invalid_time_format: "netinkamas laikas. Reikalingas formatas: HH:MM (pvz.: 10:30)"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Susitikimas"
+ meeting: "One-time meeting"
meeting_agenda_item: "Darbotvarkės punktas"
meeting_agenda: "Dienotvarkė"
- meeting_minutes: "Minutės"
meeting_section: "Skiltis"
activity:
filter:
@@ -225,9 +223,6 @@ lt:
new_date_time: "Nauja data/laikas"
label_mail_all_participants: "Siųsti e-laišką visiems dalyviams"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/lv.yml b/modules/meeting/config/locales/crowdin/lv.yml
index fdd5182899b..cb2822bd5a7 100644
--- a/modules/meeting/config/locales/crowdin/lv.yml
+++ b/modules/meeting/config/locales/crowdin/lv.yml
@@ -71,11 +71,9 @@ lv:
invalid_time_format: "nav derīgs laiks. Vajadzīgais formāts: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Sanāksmes"
+ meeting: "One-time meeting"
meeting_agenda_item: "Darba kārtības punkts"
meeting_agenda: "Darba kārtība"
- meeting_minutes: "Protokols"
meeting_section: "Section"
activity:
filter:
@@ -221,9 +219,6 @@ lv:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/mn.yml b/modules/meeting/config/locales/crowdin/mn.yml
index 220133df1e0..12119ef18e6 100644
--- a/modules/meeting/config/locales/crowdin/mn.yml
+++ b/modules/meeting/config/locales/crowdin/mn.yml
@@ -70,11 +70,9 @@ mn:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Meeting"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ mn:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/ms.yml b/modules/meeting/config/locales/crowdin/ms.yml
index b05e184d832..2fc75cee27c 100644
--- a/modules/meeting/config/locales/crowdin/ms.yml
+++ b/modules/meeting/config/locales/crowdin/ms.yml
@@ -69,11 +69,9 @@ ms:
invalid_time_format: "bukan masa yang sah. Format yang diperlukan: JJ:MM"
models:
recurring_meeting: "Mesyuarat berulang"
- structured_meeting: "Mesyuarat sekali"
- meeting: "Mesyuarat"
+ meeting: "One-time meeting"
meeting_agenda_item: "Item agenda"
meeting_agenda: "Agenda"
- meeting_minutes: "Minit mesyuarat"
meeting_section: "Bahagian"
activity:
filter:
@@ -213,9 +211,6 @@ ms:
new_date_time: "Tarikh/masa baharu"
label_mail_all_participants: "Hantar e-mel kepada semua peserta"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Berstruktur"
one_time: "Sekali"
recurring: "Berulang"
recurring_text: "Buat siri mesyuarat dengan templat dinamik untuk setiap kejadian."
diff --git a/modules/meeting/config/locales/crowdin/ne.yml b/modules/meeting/config/locales/crowdin/ne.yml
index 52b5257862b..3e3173f25f2 100644
--- a/modules/meeting/config/locales/crowdin/ne.yml
+++ b/modules/meeting/config/locales/crowdin/ne.yml
@@ -70,11 +70,9 @@ ne:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Meeting"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ ne:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/nl.yml b/modules/meeting/config/locales/crowdin/nl.yml
index 08e1f643738..1645de5cbb1 100644
--- a/modules/meeting/config/locales/crowdin/nl.yml
+++ b/modules/meeting/config/locales/crowdin/nl.yml
@@ -70,11 +70,9 @@ nl:
invalid_time_format: "is geen geldige tijd. Vereist formaat: UH:MM"
models:
recurring_meeting: "Terugkerende vergadering"
- structured_meeting: "Eenmalige vergadering"
- meeting: "Vergadering"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agendapunt"
meeting_agenda: "Agenda"
- meeting_minutes: "Minuten"
meeting_section: "Sectie"
activity:
filter:
@@ -217,9 +215,6 @@ nl:
new_date_time: "Nieuwe datum/tijd"
label_mail_all_participants: "E-mail verzenden naar alle deelnemers"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Gestructureerd"
one_time: "Eenmalig"
recurring: "Terugkerend"
recurring_text: "Maak vergaderreeksen met dynamische sjablonen voor elke gebeurtenis."
diff --git a/modules/meeting/config/locales/crowdin/no.yml b/modules/meeting/config/locales/crowdin/no.yml
index f8925a714ac..5ca9ee34ab5 100644
--- a/modules/meeting/config/locales/crowdin/no.yml
+++ b/modules/meeting/config/locales/crowdin/no.yml
@@ -70,11 +70,9 @@
invalid_time_format: "er ikke et gyldig tidpunkt. Formatet må være HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Møte"
+ meeting: "One-time meeting"
meeting_agenda_item: "Dagsorden element"
meeting_agenda: "Saksliste"
- meeting_minutes: "Referat"
meeting_section: "Seksjon"
activity:
filter:
@@ -217,9 +215,6 @@
new_date_time: "Ny dato/tid"
label_mail_all_participants: "Send e-post til alle deltagere"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/pl.yml b/modules/meeting/config/locales/crowdin/pl.yml
index 3eecdac15bf..00613bb73c0 100644
--- a/modules/meeting/config/locales/crowdin/pl.yml
+++ b/modules/meeting/config/locales/crowdin/pl.yml
@@ -72,11 +72,9 @@ pl:
invalid_time_format: "nie jest prawidłowy. Wymagany format to: GG:MM"
models:
recurring_meeting: "Spotkanie cykliczne"
- structured_meeting: "Spotkanie jednorazowe"
- meeting: "Spotkanie"
+ meeting: "Spotkanie jednorazowe"
meeting_agenda_item: "Punkt programu"
meeting_agenda: "Plan spotkania"
- meeting_minutes: "Protokół ze spotkania"
meeting_section: "Sekcja"
activity:
filter:
@@ -225,9 +223,6 @@ pl:
new_date_time: "Nowa data/godzina"
label_mail_all_participants: "Wyślij wiadomość e-mail do wszystkich uczestników"
types:
- classic: "Klasyczne (nieobsługiwane)"
- classic_text: "Uwaga: spotkania klasyczne zostaną usunięte w następnej wersji OpenProject."
- structured: "Zorganizowane"
one_time: "Jednorazowe"
recurring: "Cykliczne"
recurring_text: "Utwórz serię spotkań z dynamicznym szablonem dla każdego wystąpienia."
diff --git a/modules/meeting/config/locales/crowdin/pt-BR.yml b/modules/meeting/config/locales/crowdin/pt-BR.yml
index b00b25ca710..6190920d395 100644
--- a/modules/meeting/config/locales/crowdin/pt-BR.yml
+++ b/modules/meeting/config/locales/crowdin/pt-BR.yml
@@ -70,11 +70,9 @@ pt-BR:
invalid_time_format: "não é um horário válido. Formato exigido: HH:MM"
models:
recurring_meeting: "Reunião recorrente"
- structured_meeting: "Reunião única"
- meeting: "Reunião"
+ meeting: "Reunião única"
meeting_agenda_item: "Item da agenda"
meeting_agenda: "Agenda"
- meeting_minutes: "Atas"
meeting_section: "Seção"
activity:
filter:
@@ -217,9 +215,6 @@ pt-BR:
new_date_time: "Nova data/hora"
label_mail_all_participants: "Enviar e-mail para todos os participantes"
types:
- classic: "Clássica (não suportada)"
- classic_text: "Nota: As reuniões clássicas serão removidas na próxima versão do OpenProject."
- structured: "Estruturada"
one_time: "Única"
recurring: "Recorrente"
recurring_text: "Criar série de reuniões com modelo dinâmico para cada evento."
diff --git a/modules/meeting/config/locales/crowdin/pt-PT.yml b/modules/meeting/config/locales/crowdin/pt-PT.yml
index 0285df0f715..c57e66bb0db 100644
--- a/modules/meeting/config/locales/crowdin/pt-PT.yml
+++ b/modules/meeting/config/locales/crowdin/pt-PT.yml
@@ -70,11 +70,9 @@ pt-PT:
invalid_time_format: "não é uma hora válida. Formato exigido: HH:MM"
models:
recurring_meeting: "Reunião recorrente"
- structured_meeting: "Reunião única"
- meeting: "Reunião"
+ meeting: "Reunião única"
meeting_agenda_item: "Pontos da ordem de trabalhos"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutos"
meeting_section: "Secção"
activity:
filter:
@@ -217,9 +215,6 @@ pt-PT:
new_date_time: "Nova data/hora"
label_mail_all_participants: "Enviar e-mail a todos os participantes"
types:
- classic: "Clássica (não suportada)"
- classic_text: "Nota: as reuniões clássicas serão removidas na próxima versão do OpenProject."
- structured: "Estruturada"
one_time: "Única"
recurring: "Recorrente"
recurring_text: "Crie séries de reuniões com um modelo dinâmico para cada ocorrência."
diff --git a/modules/meeting/config/locales/crowdin/ro.yml b/modules/meeting/config/locales/crowdin/ro.yml
index 7a95aab0bc3..aed764e2f86 100644
--- a/modules/meeting/config/locales/crowdin/ro.yml
+++ b/modules/meeting/config/locales/crowdin/ro.yml
@@ -71,11 +71,9 @@ ro:
invalid_time_format: "nu este o oră valabilă. Format necesar: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "ID întâlnire"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agendă"
- meeting_minutes: "Minute"
meeting_section: "Secțiune"
activity:
filter:
@@ -221,9 +219,6 @@ ro:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/ru.yml b/modules/meeting/config/locales/crowdin/ru.yml
index 912433e5317..f44e03db954 100644
--- a/modules/meeting/config/locales/crowdin/ru.yml
+++ b/modules/meeting/config/locales/crowdin/ru.yml
@@ -72,11 +72,9 @@ ru:
invalid_time_format: "недопустимое время. Требуемый формат: ЧЧ:ММ"
models:
recurring_meeting: "Повторяющееся совещание"
- structured_meeting: "Однократное совещание"
- meeting: "Совещание"
+ meeting: "Однократное совещание"
meeting_agenda_item: "Пункт повестки"
meeting_agenda: "Повестка дня"
- meeting_minutes: "Протокол(-ы)"
meeting_section: "Раздел"
activity:
filter:
@@ -225,9 +223,6 @@ ru:
new_date_time: "Новые дата/время"
label_mail_all_participants: "Отправить письмо всем участникам"
types:
- classic: "Классический (не поддерживается)"
- classic_text: "Примечание: совещания классического типа будут удалены в следующей версии OpenProject."
- structured: "Структурированная"
one_time: "Однократная"
recurring: "Периодичность"
recurring_text: "Создать серию совещаний с динамическим шаблоном для каждого события."
diff --git a/modules/meeting/config/locales/crowdin/rw.yml b/modules/meeting/config/locales/crowdin/rw.yml
index 2b5e431677e..55e3d0d1ee7 100644
--- a/modules/meeting/config/locales/crowdin/rw.yml
+++ b/modules/meeting/config/locales/crowdin/rw.yml
@@ -70,11 +70,9 @@ rw:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Meeting"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ rw:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/si.yml b/modules/meeting/config/locales/crowdin/si.yml
index 53fc9f4dee1..6df003a0109 100644
--- a/modules/meeting/config/locales/crowdin/si.yml
+++ b/modules/meeting/config/locales/crowdin/si.yml
@@ -70,11 +70,9 @@ si:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Meeting"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ si:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/sk.yml b/modules/meeting/config/locales/crowdin/sk.yml
index 66f97c01a54..ad369159d1b 100644
--- a/modules/meeting/config/locales/crowdin/sk.yml
+++ b/modules/meeting/config/locales/crowdin/sk.yml
@@ -72,11 +72,9 @@ sk:
invalid_time_format: "nie je platný čas. Požadovaný formát: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Stretnutie"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Zápisnica"
meeting_section: "Section"
activity:
filter:
@@ -225,9 +223,6 @@ sk:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/sl.yml b/modules/meeting/config/locales/crowdin/sl.yml
index 38824946f69..70d68214ad5 100644
--- a/modules/meeting/config/locales/crowdin/sl.yml
+++ b/modules/meeting/config/locales/crowdin/sl.yml
@@ -72,11 +72,9 @@ sl:
invalid_time_format: "ni veljaven čas. Potreben format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Sestanek"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Dnevni red"
- meeting_minutes: "Zapisnik"
meeting_section: "Section"
activity:
filter:
@@ -225,9 +223,6 @@ sl:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/sr.yml b/modules/meeting/config/locales/crowdin/sr.yml
index 6b3ab5e994d..46ceaa82824 100644
--- a/modules/meeting/config/locales/crowdin/sr.yml
+++ b/modules/meeting/config/locales/crowdin/sr.yml
@@ -71,11 +71,9 @@ sr:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Meeting"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
filter:
@@ -221,9 +219,6 @@ sr:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/sv.yml b/modules/meeting/config/locales/crowdin/sv.yml
index 419af360a8a..210b70a0bba 100644
--- a/modules/meeting/config/locales/crowdin/sv.yml
+++ b/modules/meeting/config/locales/crowdin/sv.yml
@@ -70,11 +70,9 @@ sv:
invalid_time_format: "är inte en giltig tid. Använd formatet HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Möte"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Protokoll"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ sv:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/th.yml b/modules/meeting/config/locales/crowdin/th.yml
index 61526065a19..5e7c14493b8 100644
--- a/modules/meeting/config/locales/crowdin/th.yml
+++ b/modules/meeting/config/locales/crowdin/th.yml
@@ -69,11 +69,9 @@ th:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "ประชุม"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
filter:
@@ -213,9 +211,6 @@ th:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/tr.yml b/modules/meeting/config/locales/crowdin/tr.yml
index b778eae9028..3c245824539 100644
--- a/modules/meeting/config/locales/crowdin/tr.yml
+++ b/modules/meeting/config/locales/crowdin/tr.yml
@@ -70,11 +70,9 @@ tr:
invalid_time_format: "geçerli bir zaman değil. Gerekli format: SS:DD"
models:
recurring_meeting: "Yinelenen toplantı"
- structured_meeting: "Tek seferlik toplantı"
- meeting: "Toplantı"
+ meeting: "Tek seferlik toplantı"
meeting_agenda_item: "Gündem maddesi"
meeting_agenda: "Gündem"
- meeting_minutes: "Dakika"
meeting_section: "Bölüm"
activity:
filter:
@@ -217,9 +215,6 @@ tr:
new_date_time: "Yeni tarih/saat"
label_mail_all_participants: "Tüm katılımcılara e-posta gönder"
types:
- classic: "Klasik (desteklenmiyor)"
- classic_text: "Not: Klasik toplantılar sonraki OpenProject sürümünde kaldırılacaktır."
- structured: "Planlı"
one_time: "Tek seferlik"
recurring: "Yinelenen"
recurring_text: "Her toplantı için kullanılabilecek dinamik bir şablonla toplantı serileri oluşturun."
diff --git a/modules/meeting/config/locales/crowdin/uk.yml b/modules/meeting/config/locales/crowdin/uk.yml
index b25560464f2..6516cc103ae 100644
--- a/modules/meeting/config/locales/crowdin/uk.yml
+++ b/modules/meeting/config/locales/crowdin/uk.yml
@@ -72,11 +72,9 @@ uk:
invalid_time_format: "не є допустимим часом. Необхідний формат: HH:MM"
models:
recurring_meeting: "Повторювана нарада"
- structured_meeting: "Одноразова нарада"
- meeting: "Нарада"
+ meeting: "Одноразова нарада"
meeting_agenda_item: "Порядок денний"
meeting_agenda: "Порядок денний"
- meeting_minutes: "Хвилини"
meeting_section: "Розділ"
activity:
filter:
@@ -225,9 +223,6 @@ uk:
new_date_time: "Нова дата/час"
label_mail_all_participants: "Надіслати лист усім учасникам"
types:
- classic: "Класична (не підтримується)"
- classic_text: "Примітка: класичні наради буде вилучено в наступній версії OpenProject."
- structured: "Структурована"
one_time: "Одноразова"
recurring: "Повторювана"
recurring_text: "Створюйте серії нарад на основі динамічного шаблона для кожної події."
diff --git a/modules/meeting/config/locales/crowdin/uz.yml b/modules/meeting/config/locales/crowdin/uz.yml
index fed63d2302a..323453407e1 100644
--- a/modules/meeting/config/locales/crowdin/uz.yml
+++ b/modules/meeting/config/locales/crowdin/uz.yml
@@ -70,11 +70,9 @@ uz:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Meeting"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
filter:
@@ -217,9 +215,6 @@ uz:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/config/locales/crowdin/vi.yml b/modules/meeting/config/locales/crowdin/vi.yml
index 6f73d9214eb..2434208bc8d 100644
--- a/modules/meeting/config/locales/crowdin/vi.yml
+++ b/modules/meeting/config/locales/crowdin/vi.yml
@@ -69,11 +69,9 @@ vi:
invalid_time_format: "không phải giờ hợp lệ. Yêu cầu định dạng: HH:MM"
models:
recurring_meeting: "Họp định kỳ"
- structured_meeting: "Họp đột xuất"
- meeting: "Cuộc họp"
+ meeting: "One-time meeting"
meeting_agenda_item: "Mục chương trình nghị sự"
meeting_agenda: "Các ý chính"
- meeting_minutes: "Phút"
meeting_section: "Phần"
activity:
filter:
@@ -213,9 +211,6 @@ vi:
new_date_time: "Ngày/giờ mới"
label_mail_all_participants: "Gửi email đến tất cả người tham gia"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Đã cấu trúc"
one_time: "Một lần"
recurring: "Định kỳ"
recurring_text: "Tạo chuỗi họp với mẫu động cho mỗi lần họp."
diff --git a/modules/meeting/config/locales/crowdin/zh-CN.yml b/modules/meeting/config/locales/crowdin/zh-CN.yml
index 43d626c87f2..9d0376db212 100644
--- a/modules/meeting/config/locales/crowdin/zh-CN.yml
+++ b/modules/meeting/config/locales/crowdin/zh-CN.yml
@@ -69,11 +69,9 @@ zh-CN:
invalid_time_format: "不是有效时间。所需格式:HH:MM"
models:
recurring_meeting: "定期会议"
- structured_meeting: "动态会议"
- meeting: "会议"
+ meeting: "一次性会议"
meeting_agenda_item: "议程项目"
meeting_agenda: "议程"
- meeting_minutes: "会议记录"
meeting_section: "节"
activity:
filter:
@@ -213,9 +211,6 @@ zh-CN:
new_date_time: "新日期/时间"
label_mail_all_participants: "发送电子邮件给所有参与者"
types:
- classic: "经典(不支持)"
- classic_text: "注意:经典会议将在下一版 OpenProject 中删除。"
- structured: "结构化"
one_time: "动态"
recurring: "定期"
recurring_text: "为每次事件创建带有动态模板的会议系列。"
diff --git a/modules/meeting/config/locales/crowdin/zh-TW.yml b/modules/meeting/config/locales/crowdin/zh-TW.yml
index ac3a23dac4f..a4156e22383 100644
--- a/modules/meeting/config/locales/crowdin/zh-TW.yml
+++ b/modules/meeting/config/locales/crowdin/zh-TW.yml
@@ -69,11 +69,9 @@ zh-TW:
invalid_time_format: "不是有效時間。所需格式: HH:MM"
models:
recurring_meeting: "重複性會議"
- structured_meeting: "一次性會議"
- meeting: "會議"
+ meeting: "一次性會議"
meeting_agenda_item: "議程項目"
meeting_agenda: "會議大綱"
- meeting_minutes: "會議記錄"
meeting_section: "區段"
activity:
filter:
@@ -213,9 +211,6 @@ zh-TW:
new_date_time: "新日期/時間"
label_mail_all_participants: "發送電子郵件給所有參與者"
types:
- classic: "傳統(不支援)"
- classic_text: "注意:傳統會議將在 OpenProject 的下一個版本中刪除。"
- structured: "結構化"
one_time: "一次性"
recurring: "重覆"
recurring_text: "為每次發生的事件建立具有動態範本的會議系列。"
@@ -329,7 +324,7 @@ zh-TW:
permission_create_meeting_agendas_explanation: "允許編輯傳統會議的議程。"
permission_manage_agendas: "管理議程"
permission_manage_agendas_explanation: "允許編輯動態會議的議程項目。"
- permission_close_meeting_agendas: "結束會議大綱"
+ permission_close_meeting_agendas: "定案會議大綱"
permission_send_meeting_agendas_notification: "傳送會議大綱審閱通知"
permission_create_meeting_minutes: "管理會議記錄"
permission_send_meeting_minutes_notification: "傳送會議記錄審閱通知"
diff --git a/modules/meeting/config/locales/en.yml b/modules/meeting/config/locales/en.yml
index 1ba523ae371..0bc0310920e 100644
--- a/modules/meeting/config/locales/en.yml
+++ b/modules/meeting/config/locales/en.yml
@@ -82,11 +82,9 @@ en:
invalid_time_format: "is not a valid time. Required format: HH:MM"
models:
recurring_meeting: "Recurring meeting"
- structured_meeting: "One-time meeting"
- meeting: "Meeting"
+ meeting: "One-time meeting"
meeting_agenda_item: "Agenda item"
meeting_agenda: "Agenda"
- meeting_minutes: "Minutes"
meeting_section: "Section"
activity:
@@ -236,9 +234,6 @@ en:
new_date_time: "New date/time"
label_mail_all_participants: "Send email to all participants"
types:
- classic: "Classic (unsupported)"
- classic_text: "Note: Classic meetings will be removed in the next version of OpenProject."
- structured: "Structured"
one_time: "One-time"
recurring: "Recurring"
recurring_text: "Create meeting series with dynamic template for each occurrence."
diff --git a/modules/meeting/db/migrate/20240405131352_create_meeting_sections.rb b/modules/meeting/db/migrate/20240405131352_create_meeting_sections.rb
index 0bf6a12d535..60e5145d6ca 100644
--- a/modules/meeting/db/migrate/20240405131352_create_meeting_sections.rb
+++ b/modules/meeting/db/migrate/20240405131352_create_meeting_sections.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
class CreateMeetingSections < ActiveRecord::Migration[7.1]
def up
create_table :meeting_sections do |t|
@@ -24,7 +25,7 @@ class CreateMeetingSections < ActiveRecord::Migration[7.1]
private
def create_and_assign_default_section
- StructuredMeeting.includes(:agenda_items).find_each do |meeting|
+ Meeting.includes(:agenda_items).find_each do |meeting|
section = MeetingSection.create!(
meeting:,
title: "Untitled"
diff --git a/modules/meeting/db/migrate/20250404060850_add_author_to_outcome.rb b/modules/meeting/db/migrate/20250404060850_add_author_to_outcome.rb
new file mode 100644
index 00000000000..60900dcf601
--- /dev/null
+++ b/modules/meeting/db/migrate/20250404060850_add_author_to_outcome.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class AddAuthorToOutcome < ActiveRecord::Migration[8.0]
+ def change
+ add_reference :meeting_outcomes,
+ :author,
+ type: :bigint,
+ foreign_key: { to_table: :users },
+ null: true,
+ index: true
+ end
+end
diff --git a/modules/meeting/db/migrate/20250404061503_migrate_classic_meetings.rb b/modules/meeting/db/migrate/20250404061503_migrate_classic_meetings.rb
new file mode 100644
index 00000000000..e2e8dc18919
--- /dev/null
+++ b/modules/meeting/db/migrate/20250404061503_migrate_classic_meetings.rb
@@ -0,0 +1,82 @@
+# rubocop:disable Rails/SquishedSQLHeredocs
+# frozen_string_literal: true
+
+class MigrateClassicMeetings < ActiveRecord::Migration[8.0]
+ def up
+ # Create default sections for meetings that don't have one
+ execute <<~SQL
+ INSERT INTO meeting_sections (meeting_id, title, position, created_at, updated_at)
+ SELECT m.id, '', 1, NOW(), NOW()
+ FROM meetings m
+ LEFT JOIN meeting_sections ms ON ms.meeting_id = m.id
+ WHERE ms.id IS NULL;
+ SQL
+
+ # Migrate MeetingAgenda content to MeetingAgendaItem
+ execute <<~SQL.squish
+ INSERT INTO meeting_agenda_items (
+ meeting_id,
+ meeting_section_id,
+ author_id,
+ presenter_id,
+ title,
+ notes,
+ position,
+ created_at,
+ updated_at
+ )
+ SELECT
+ mc.meeting_id,
+ ms.id,
+ mc.author_id,
+ mc.author_id,
+ '#{I18n.t('activerecord.models.meeting_agenda')}',
+ mc.text,
+ 1,
+ mc.created_at,
+ mc.updated_at
+ FROM meeting_contents mc
+ INNER JOIN meetings m ON m.id = mc.meeting_id
+ INNER JOIN meeting_sections ms ON ms.meeting_id = m.id
+ WHERE mc.type = 'MeetingAgenda';
+ SQL
+
+ # Migrate MeetingMinutes to MeetingOutcome
+ execute <<~SQL.squish
+ INSERT INTO meeting_outcomes (
+ meeting_agenda_item_id,
+ author_id,
+ notes,
+ created_at,
+ updated_at
+ )
+ SELECT
+ mai.id,
+ mc.author_id,
+ mc.text,
+ mc.created_at,
+ mc.updated_at
+ FROM meeting_contents mc
+ INNER JOIN meetings m ON m.id = mc.meeting_id
+ INNER JOIN meeting_sections ms ON ms.meeting_id = m.id
+ INNER JOIN meeting_agenda_items mai ON mai.meeting_id = m.id
+ WHERE mc.type = 'MeetingMinutes';
+ SQL
+
+ # Close classic meetings that are in the past
+ execute <<~SQL.squish
+ UPDATE meetings
+ SET state = 5
+ WHERE type = 'Meeting'
+ AND start_time < CURRENT_TIMESTAMP
+ SQL
+
+ # Remove STI column
+ remove_column :meetings, :type
+ end
+
+ def down
+ raise ActiveRecord::IrreversibleMigration
+ end
+end
+# rubocop:enable Rails/SquishedSQLHeredocs
diff --git a/modules/meeting/lib/api/v3/attachments/attachments_by_meeting_content_api.rb b/modules/meeting/lib/api/v3/attachments/attachments_by_meeting_content_api.rb
index 4813568e815..66dc5bdbbb6 100644
--- a/modules/meeting/lib/api/v3/attachments/attachments_by_meeting_content_api.rb
+++ b/modules/meeting/lib/api/v3/attachments/attachments_by_meeting_content_api.rb
@@ -32,23 +32,18 @@ module API
module Attachments
class AttachmentsByMeetingContentAPI < ::API::OpenProjectAPI
resources :attachments do
- helpers API::V3::Attachments::AttachmentsByContainerAPI::Helpers
-
- helpers do
- def container
- meeting_content
- end
-
- def get_attachment_self_path
- api_v3_paths.attachments_by_meeting_content container.id
- end
+ get do
+ status :gone
end
- get &API::V3::Attachments::AttachmentsByContainerAPI.read
- post &API::V3::Attachments::AttachmentsByContainerAPI.create
+ post do
+ status :gone
+ end
namespace :prepare do
- post &API::V3::Attachments::AttachmentsByContainerAPI.prepare
+ post do
+ status :gone
+ end
end
end
end
diff --git a/modules/meeting/lib/api/v3/meeting_agendas/meeting_agenda_representer.rb b/modules/meeting/lib/api/v3/meeting_agendas/meeting_agenda_representer.rb
deleted file mode 100644
index f0d6571db92..00000000000
--- a/modules/meeting/lib/api/v3/meeting_agendas/meeting_agenda_representer.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-module API
- module V3
- module MeetingAgendas
- class MeetingAgendaRepresenter < API::V3::MeetingContents::MeetingContentRepresenter
- end
- end
- end
-end
diff --git a/modules/meeting/lib/api/v3/meetings/meeting_contents_api.rb b/modules/meeting/lib/api/v3/meetings/meeting_contents_api.rb
index e46c74b1180..1d74caadca2 100644
--- a/modules/meeting/lib/api/v3/meetings/meeting_contents_api.rb
+++ b/modules/meeting/lib/api/v3/meetings/meeting_contents_api.rb
@@ -32,20 +32,10 @@ module API
module Meetings
class MeetingContentsAPI < ::API::OpenProjectAPI
resources :meeting_contents do
- helpers do
- def meeting_content
- MeetingContent.find params[:id]
- end
- end
-
route_param :id do
get do
- ::API::V3::MeetingContents::MeetingContentRepresenter.new(
- meeting_content, current_user:, embed_links: true
- )
+ status :gone
end
-
- mount ::API::V3::Attachments::AttachmentsByMeetingContentAPI
end
end
end
diff --git a/modules/meeting/lib/api/v3/meetings/meeting_representer.rb b/modules/meeting/lib/api/v3/meetings/meeting_representer.rb
index e7528766f4b..21426d113e9 100644
--- a/modules/meeting/lib/api/v3/meetings/meeting_representer.rb
+++ b/modules/meeting/lib/api/v3/meetings/meeting_representer.rb
@@ -48,10 +48,6 @@ module API
lock_version.to_i
}
- property :type,
- as: :meeting_type,
- getter: ->(*) { type }
-
date_time_property :start_time
date_time_property :end_time
diff --git a/modules/meeting/lib/open_project/meeting/engine.rb b/modules/meeting/lib/open_project/meeting/engine.rb
index 25906f7bf53..a522fb332c7 100644
--- a/modules/meeting/lib/open_project/meeting/engine.rb
+++ b/modules/meeting/lib/open_project/meeting/engine.rb
@@ -189,6 +189,13 @@ module OpenProject::Meeting
patches [:Project]
patch_with_namespace :BasicData, :SettingSeeder
+ replace_principal_references "Meeting" => %i[author_id],
+ "MeetingAgenda" => %i[author_id],
+ "MeetingMinutes" => %i[author_id],
+ "MeetingAgendaItem" => %i[author_id presenter_id],
+ "MeetingParticipant" => :user_id,
+ "MeetingOutcome" => :author_id
+
extend_api_response(:v3, :work_packages, :work_package,
&::OpenProject::Meeting::Patches::API::WorkPackageRepresenter.extension)
diff --git a/modules/meeting/spec/components/meetings/delete_dialog_component_spec.rb b/modules/meeting/spec/components/meetings/delete_dialog_component_spec.rb
index dd74e691069..31b721aea03 100644
--- a/modules/meeting/spec/components/meetings/delete_dialog_component_spec.rb
+++ b/modules/meeting/spec/components/meetings/delete_dialog_component_spec.rb
@@ -69,7 +69,7 @@ RSpec.describe Meetings::DeleteDialogComponent, type: :component do
context "with an associated recurring/templated meeting" do
let(:series) { build_stubbed(:recurring_meeting) }
- let(:meeting) { build_stubbed(:structured_meeting_template, recurring_meeting: series) }
+ let(:meeting) { build_stubbed(:meeting_template, recurring_meeting: series) }
it "shows a heading" do
expect(subject).to have_text "Cancel this meeting occurrence?"
diff --git a/modules/meeting/spec/components/meetings/row_component_spec.rb b/modules/meeting/spec/components/meetings/row_component_spec.rb
index 5fb0fd9088c..fc39200c080 100644
--- a/modules/meeting/spec/components/meetings/row_component_spec.rb
+++ b/modules/meeting/spec/components/meetings/row_component_spec.rb
@@ -61,7 +61,7 @@ RSpec.describe Meetings::RowComponent, type: :component do
context "with an associated recurring/templated meeting" do
let(:series) { build_stubbed(:recurring_meeting, project:) }
- let(:meeting) { build_stubbed(:structured_meeting_template, recurring_meeting: series, project:) }
+ let(:meeting) { build_stubbed(:meeting_template, recurring_meeting: series, project:) }
it "shows default menu items" do
expect(subject).to have_link "View meeting series"
@@ -99,7 +99,7 @@ RSpec.describe Meetings::RowComponent, type: :component do
context "with an associated recurring/templated meeting" do
let(:series) { build_stubbed(:recurring_meeting, project:) }
- let(:meeting) { build_stubbed(:structured_meeting_template, recurring_meeting: series, project:) }
+ let(:meeting) { build_stubbed(:meeting_template, recurring_meeting: series, project:) }
context "without a current project" do
it "shows delete menu item with a back url" do
diff --git a/modules/meeting/spec/components/meetings/side_panel/details_component_spec.rb b/modules/meeting/spec/components/meetings/side_panel/details_component_spec.rb
index 709db948fd1..72fef088563 100644
--- a/modules/meeting/spec/components/meetings/side_panel/details_component_spec.rb
+++ b/modules/meeting/spec/components/meetings/side_panel/details_component_spec.rb
@@ -49,7 +49,7 @@ RSpec.describe Meetings::SidePanel::DetailsComponent, type: :component do
frequency: "working_days")
end
let(:meeting) do
- build_stubbed(:structured_meeting_template,
+ build_stubbed(:meeting_template,
recurring_meeting: series)
end
@@ -65,7 +65,7 @@ RSpec.describe Meetings::SidePanel::DetailsComponent, type: :component do
frequency: "weekly")
end
let(:meeting) do
- build_stubbed(:structured_meeting_template,
+ build_stubbed(:meeting_template,
recurring_meeting: series)
end
diff --git a/modules/meeting/spec/components/recurring_meetings/delete_dialog_component_spec.rb b/modules/meeting/spec/components/recurring_meetings/delete_dialog_component_spec.rb
index 20614f6c262..a0d2ffb046b 100644
--- a/modules/meeting/spec/components/recurring_meetings/delete_dialog_component_spec.rb
+++ b/modules/meeting/spec/components/recurring_meetings/delete_dialog_component_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe RecurringMeetings::DeleteDialogComponent, type: :component do
let(:project) { build_stubbed(:project) }
let(:recurring_meeting) { build_stubbed(:recurring_meeting, project:, end_after: :iterations, iterations: 6) }
- let(:meeting) { build_stubbed(:structured_meeting_template, recurring_meeting:) }
+ let(:meeting) { build_stubbed(:meeting_template, recurring_meeting:) }
let(:user) { build_stubbed(:user) }
subject do
diff --git a/modules/meeting/spec/components/recurring_meetings/delete_scheduled_dialog_component_spec.rb b/modules/meeting/spec/components/recurring_meetings/delete_scheduled_dialog_component_spec.rb
index 1f47a657909..e595609bc07 100644
--- a/modules/meeting/spec/components/recurring_meetings/delete_scheduled_dialog_component_spec.rb
+++ b/modules/meeting/spec/components/recurring_meetings/delete_scheduled_dialog_component_spec.rb
@@ -37,7 +37,7 @@ RSpec.describe RecurringMeetings::DeleteScheduledDialogComponent, type: :compone
let(:recurring_meeting) { build_stubbed(:recurring_meeting, project:, end_after: :iterations, iterations: 6) }
let(:start_time) { 1.day.from_now }
let(:scheduled_meeting) { recurring_meeting.scheduled_meetings.find_or_initialize_by(start_time: start_time) }
- let(:meeting) { build_stubbed(:structured_meeting_template, recurring_meeting:) }
+ let(:meeting) { build_stubbed(:meeting_template, recurring_meeting:) }
let(:user) { build_stubbed(:user) }
subject do
diff --git a/modules/meeting/spec/contracts/meeting_agenda_items/create_contract_spec.rb b/modules/meeting/spec/contracts/meeting_agenda_items/create_contract_spec.rb
index e09213d85bc..cb5006e0feb 100644
--- a/modules/meeting/spec/contracts/meeting_agenda_items/create_contract_spec.rb
+++ b/modules/meeting/spec/contracts/meeting_agenda_items/create_contract_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe MeetingAgendaItems::CreateContract do
include_context "ModelContract shared context"
shared_let(:project) { create(:project) }
- let(:meeting) { create(:structured_meeting, project:) }
+ let(:meeting) { create(:meeting, project:) }
let(:item) { build(:meeting_agenda_item, meeting:) }
let(:contract) { described_class.new(item, user) }
diff --git a/modules/meeting/spec/contracts/meeting_agenda_items/delete_contract_spec.rb b/modules/meeting/spec/contracts/meeting_agenda_items/delete_contract_spec.rb
index 5afda8bd64e..ebe061d8aa5 100644
--- a/modules/meeting/spec/contracts/meeting_agenda_items/delete_contract_spec.rb
+++ b/modules/meeting/spec/contracts/meeting_agenda_items/delete_contract_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe MeetingAgendaItems::DeleteContract do
include_context "ModelContract shared context"
shared_let(:project) { create(:project) }
- shared_let(:meeting) { create(:structured_meeting, project:) }
+ shared_let(:meeting) { create(:meeting, project:) }
shared_let(:item) { create(:meeting_agenda_item, meeting:) }
let(:contract) { described_class.new(item, user) }
diff --git a/modules/meeting/spec/contracts/meeting_agenda_items/update_contract_spec.rb b/modules/meeting/spec/contracts/meeting_agenda_items/update_contract_spec.rb
index fec471e6c51..43a85865509 100644
--- a/modules/meeting/spec/contracts/meeting_agenda_items/update_contract_spec.rb
+++ b/modules/meeting/spec/contracts/meeting_agenda_items/update_contract_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe MeetingAgendaItems::UpdateContract do
include_context "ModelContract shared context"
shared_let(:project) { create(:project) }
- shared_let(:meeting) { create(:structured_meeting, project:) }
+ shared_let(:meeting) { create(:meeting, project:) }
shared_let(:item) { create(:meeting_agenda_item, meeting:) }
let(:contract) { described_class.new(item, user) }
diff --git a/modules/meeting/spec/contracts/meeting_outcomes/create_contract_spec.rb b/modules/meeting/spec/contracts/meeting_outcomes/create_contract_spec.rb
index c85321449e9..e07e30c5243 100644
--- a/modules/meeting/spec/contracts/meeting_outcomes/create_contract_spec.rb
+++ b/modules/meeting/spec/contracts/meeting_outcomes/create_contract_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe MeetingOutcomes::CreateContract do
include_context "ModelContract shared context"
shared_let(:project) { create(:project) }
- let(:meeting) { create(:structured_meeting, project:) }
+ let(:meeting) { create(:meeting, project:) }
let(:meeting_agenda_item) { create(:meeting_agenda_item, meeting:) }
let(:outcome) { build(:meeting_outcome, meeting_agenda_item:) }
let(:contract) { described_class.new(outcome, user) }
diff --git a/modules/meeting/spec/contracts/meeting_outcomes/delete_contract_spec.rb b/modules/meeting/spec/contracts/meeting_outcomes/delete_contract_spec.rb
index 999ce1800de..bcc473f8c3b 100644
--- a/modules/meeting/spec/contracts/meeting_outcomes/delete_contract_spec.rb
+++ b/modules/meeting/spec/contracts/meeting_outcomes/delete_contract_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe MeetingOutcomes::DeleteContract do
include_context "ModelContract shared context"
shared_let(:project) { create(:project) }
- shared_let(:meeting) { create(:structured_meeting, project:) }
+ shared_let(:meeting) { create(:meeting, project:) }
let(:meeting_agenda_item) { create(:meeting_agenda_item, meeting:) }
let(:outcome) { build_stubbed(:meeting_outcome, meeting_agenda_item:) }
let(:contract) { described_class.new(outcome, user) }
diff --git a/modules/meeting/spec/contracts/meeting_outcomes/update_contract_spec.rb b/modules/meeting/spec/contracts/meeting_outcomes/update_contract_spec.rb
index 0b6fd928cc8..a0c61195a8e 100644
--- a/modules/meeting/spec/contracts/meeting_outcomes/update_contract_spec.rb
+++ b/modules/meeting/spec/contracts/meeting_outcomes/update_contract_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe MeetingOutcomes::UpdateContract do
include_context "ModelContract shared context"
shared_let(:project) { create(:project) }
- let(:meeting) { create(:structured_meeting, project:) }
+ let(:meeting) { create(:meeting, project:) }
let(:meeting_agenda_item) { create(:meeting_agenda_item, meeting:) }
let(:outcome) { build(:meeting_outcome, meeting_agenda_item:) }
let(:contract) { described_class.new(outcome, user) }
diff --git a/modules/meeting/spec/contracts/meeting_sections/create_contract_spec.rb b/modules/meeting/spec/contracts/meeting_sections/create_contract_spec.rb
index a702e623cff..e088c011516 100644
--- a/modules/meeting/spec/contracts/meeting_sections/create_contract_spec.rb
+++ b/modules/meeting/spec/contracts/meeting_sections/create_contract_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe MeetingSections::CreateContract do
include_context "ModelContract shared context"
shared_let(:project) { create(:project) }
- let(:meeting) { create(:structured_meeting, project:) }
+ let(:meeting) { create(:meeting, project:) }
let(:section) { build(:meeting_section, meeting:) }
let(:contract) { described_class.new(section, user) }
diff --git a/modules/meeting/spec/contracts/meeting_sections/delete_contract_spec.rb b/modules/meeting/spec/contracts/meeting_sections/delete_contract_spec.rb
index c5c836c6de0..dfdcbcefa74 100644
--- a/modules/meeting/spec/contracts/meeting_sections/delete_contract_spec.rb
+++ b/modules/meeting/spec/contracts/meeting_sections/delete_contract_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe MeetingSections::DeleteContract do
include_context "ModelContract shared context"
shared_let(:project) { create(:project) }
- shared_let(:meeting) { create(:structured_meeting, project:) }
+ shared_let(:meeting) { create(:meeting, project:) }
let(:section) { create(:meeting_section, meeting:) }
let(:contract) { described_class.new(section, user) }
diff --git a/modules/meeting/spec/contracts/meeting_sections/update_contract_spec.rb b/modules/meeting/spec/contracts/meeting_sections/update_contract_spec.rb
index 5bbac6a403c..84a3653fbea 100644
--- a/modules/meeting/spec/contracts/meeting_sections/update_contract_spec.rb
+++ b/modules/meeting/spec/contracts/meeting_sections/update_contract_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe MeetingSections::UpdateContract do
include_context "ModelContract shared context"
shared_let(:project) { create(:project) }
- shared_let(:meeting) { create(:structured_meeting, project:) }
+ shared_let(:meeting) { create(:meeting, project:) }
shared_let(:section) { create(:meeting_section, meeting:) }
let(:contract) { described_class.new(section, user) }
diff --git a/modules/meeting/spec/contracts/meetings/create_contract_spec.rb b/modules/meeting/spec/contracts/meetings/create_contract_spec.rb
index 6360f5a767b..be7a26635ae 100644
--- a/modules/meeting/spec/contracts/meetings/create_contract_spec.rb
+++ b/modules/meeting/spec/contracts/meetings/create_contract_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe Meetings::CreateContract do
include_context "ModelContract shared context"
shared_let(:project) { create(:project) }
- let(:meeting) { build(:structured_meeting, project:) }
+ let(:meeting) { build(:meeting, project:) }
let(:contract) { described_class.new(meeting, user) }
context "with permission" do
diff --git a/modules/meeting/spec/contracts/meetings/update_contract_spec.rb b/modules/meeting/spec/contracts/meetings/update_contract_spec.rb
index 850cf721181..29d95655bfd 100644
--- a/modules/meeting/spec/contracts/meetings/update_contract_spec.rb
+++ b/modules/meeting/spec/contracts/meetings/update_contract_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe Meetings::UpdateContract do
include_context "ModelContract shared context"
shared_let(:project) { create(:project) }
- shared_let(:meeting) { create(:structured_meeting, project:) }
+ shared_let(:meeting) { create(:meeting, project:) }
let(:contract) { described_class.new(meeting, user) }
context "with permission" do
diff --git a/modules/meeting/spec/controllers/meetings_controller_spec.rb b/modules/meeting/spec/controllers/meetings_controller_spec.rb
deleted file mode 100644
index dd3c1878ae7..00000000000
--- a/modules/meeting/spec/controllers/meetings_controller_spec.rb
+++ /dev/null
@@ -1,221 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-require "#{File.dirname(__FILE__)}/../spec_helper"
-
-RSpec.describe MeetingsController do
- shared_let(:user) { create(:admin) }
- shared_let(:project) { create(:project) }
- shared_let(:other_project) { create(:project) }
-
- current_user { user }
-
- describe "GET" do
- describe "index" do
- let(:meetings) do
- [
- create(:meeting, project:),
- create(:meeting, author: user, project:),
- create(:meeting, author: user, project: other_project)
- ]
- end
- end
-
- describe "show" do
- let(:meeting) { create(:meeting, project:, agenda: nil) }
-
- describe "html" do
- before do
- get "show", params: { id: meeting.id }
- end
-
- it { expect(response).to redirect_to(project_meeting_path(project, meeting)) }
- it { expect(assigns(:meeting)).to eql meeting }
- end
- end
-
- describe "new" do
- let(:meeting) { Meeting.new(project:) }
-
- before do
- allow(Project).to receive(:find).and_return(project)
- allow(Meeting).to receive(:new).and_return(meeting)
- end
-
- shared_examples_for "new action" do |response_type:|
- describe response_type do
- context "when requesting the page without a project id" do
- before do
- get "new"
- end
-
- it { expect(response).to be_successful }
- it { expect(assigns(:meeting)).to eql meeting }
- it { expect(assigns(:project)).to be_nil }
- end
-
- context "when requesting the page with a project id" do
- before do
- get "new", params: { project_id: project.id }
- end
-
- it { expect(response).to be_successful }
- it { expect(assigns(:meeting)).to eql meeting }
- it { expect(assigns(:project)).to eql project }
- end
- end
- end
-
- it_behaves_like "new action", response_type: "html"
- it_behaves_like "new action", response_type: "turbo_stream"
- end
-
- describe "edit" do
- let(:meeting) { create(:meeting, project:) }
-
- describe "html" do
- before do
- get "edit", params: { project_id: meeting.project_id, id: meeting.id }
- end
-
- it { expect(response).to be_successful }
- it { expect(assigns(:meeting)).to eql meeting }
- end
- end
- end
-
- describe "POST" do
- describe "create" do
- render_views
-
- let(:base_params) do
- {
- project_id: project&.id,
- meeting: meeting_params
- }
- end
-
- let(:base_meeting_params) do
- {
- title: "Foobar",
- duration: "1.0",
- start_date: "2015-06-01",
- start_time_hour: "10:00"
- }
- end
-
- let(:params) { base_params }
- let(:meeting_params) { base_meeting_params }
-
- before do
- post :create,
- params:
- end
-
- context "with a project_id" do
- context "and an invalid start_date with start_time_hour" do
- let(:meeting_params) do
- base_meeting_params.merge(start_date: "-")
- end
-
- it "renders an error" do
- expect(response).to have_http_status :unprocessable_entity
- expect(response).to render_template :new
- expect(response.body)
- .to have_text("Date #{I18n.t('activerecord.errors.messages.not_an_iso_date')}")
- end
- end
-
- context "and an invalid start_time_hour with start_date" do
- let(:meeting_params) do
- base_meeting_params.merge(start_time_hour: "-")
- end
-
- it "renders an error" do
- expect(response).to have_http_status :unprocessable_entity
- expect(response).to render_template :new
- expect(response.body)
- .to have_text("Start time #{I18n.t('activerecord.errors.messages.invalid_time_format')}")
- end
- end
- end
-
- context "with a nil project_id" do
- let(:project) { nil }
-
- it "renders an error" do
- expect(response).to have_http_status :unprocessable_entity
- expect(response).to render_template :new
- expect(response.body)
- .to have_text("Project #{I18n.t('activerecord.errors.messages.blank')}")
- end
- end
-
- context "without a project_id" do
- let(:params) { base_params.except(:project_id) }
- let(:project) { nil }
-
- it "renders an error" do
- expect(response).to have_http_status :unprocessable_entity
- expect(response).to render_template :new
- expect(response.body)
- .to have_text("Project #{I18n.t('activerecord.errors.messages.blank')}")
- end
- end
- end
- end
-
- describe "notify" do
- let!(:meeting) { create(:meeting) }
- let!(:participant) { create(:meeting_participant, meeting:, attended: true) }
-
- it "produces a background job for notification" do
- post :notify, params: { project_id: meeting.project_id, id: meeting.id }
-
- perform_enqueued_jobs
- expect(ActionMailer::Base.deliveries.count).to eq(1)
- end
-
- context "with an error during deliver" do
- before do
- allow(MeetingMailer).to receive(:invited).and_raise(Net::SMTPError)
- end
-
- it "produces a flash message containing the mail addresses raising the error" do
- expect { post :notify, params: { project_id: meeting.project_id, id: meeting.id } }.not_to raise_error
- meeting.participants.each do |participant|
- expect(flash[:error]).to include(participant.name)
- end
-
- perform_enqueued_jobs
- expect(ActionMailer::Base.deliveries.count).to eq(0)
- end
- end
- end
-end
diff --git a/modules/meeting/spec/factories/meeting_agenda_factory.rb b/modules/meeting/spec/factories/meeting_agenda_factory.rb
deleted file mode 100644
index 22490f8aa58..00000000000
--- a/modules/meeting/spec/factories/meeting_agenda_factory.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-FactoryBot.define do
- factory :meeting_agenda do |_a|
- meeting
- end
-end
diff --git a/modules/meeting/spec/factories/meeting_agenda_item_factory.rb b/modules/meeting/spec/factories/meeting_agenda_item_factory.rb
index de44aec2961..f475af5f455 100644
--- a/modules/meeting/spec/factories/meeting_agenda_item_factory.rb
+++ b/modules/meeting/spec/factories/meeting_agenda_item_factory.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -29,7 +30,7 @@
FactoryBot.define do
factory :meeting_agenda_item do |m|
- meeting factory: :structured_meeting
+ meeting factory: :meeting
work_package { nil }
author factory: :user
duration_in_minutes { 10 }
diff --git a/modules/meeting/spec/factories/meeting_content_journal_factory.rb b/modules/meeting/spec/factories/meeting_content_journal_factory.rb
deleted file mode 100644
index 93f50409fe1..00000000000
--- a/modules/meeting/spec/factories/meeting_content_journal_factory.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-FactoryBot.define do
- factory :journal_meeting_content_journal, class: "Journal::MeetingContentJournal" do
- end
-end
diff --git a/modules/meeting/spec/factories/meeting_factory.rb b/modules/meeting/spec/factories/meeting_factory.rb
index d807b97958f..085f174cee1 100644
--- a/modules/meeting/spec/factories/meeting_factory.rb
+++ b/modules/meeting/spec/factories/meeting_factory.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -47,12 +48,8 @@ FactoryBot.define do
meeting.project = evaluator.project if evaluator.project
end
- factory :structured_meeting, class: "StructuredMeeting" do |structured_meeting|
- structured_meeting.sequence(:title) { |n| "Structured meeting #{n}" }
- end
-
- factory :structured_meeting_template, class: "StructuredMeeting" do |structured_meeting|
- structured_meeting.sequence(:title) { |n| "Structured meeting template #{n}" }
+ factory :meeting_template do |meeting|
+ meeting.sequence(:title) { |n| "Meeting template #{n}" }
template { true }
recurring_meeting
diff --git a/modules/meeting/spec/factories/meeting_minutes_factory.rb b/modules/meeting/spec/factories/meeting_minutes_factory.rb
deleted file mode 100644
index c9682b447bc..00000000000
--- a/modules/meeting/spec/factories/meeting_minutes_factory.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-FactoryBot.define do
- factory :meeting_minutes do |_m|
- meeting
- end
-end
diff --git a/modules/meeting/spec/factories/meeting_section_factory.rb b/modules/meeting/spec/factories/meeting_section_factory.rb
index 79bf3e3b30f..3c53cb8ec43 100644
--- a/modules/meeting/spec/factories/meeting_section_factory.rb
+++ b/modules/meeting/spec/factories/meeting_section_factory.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -29,7 +30,7 @@
FactoryBot.define do
factory :meeting_section do |m|
- meeting factory: :structured_meeting
+ meeting factory: :meeting
m.sequence(:title) { |n| "Section #{n}" }
end
diff --git a/modules/meeting/spec/factories/recurring_meeting_factory.rb b/modules/meeting/spec/factories/recurring_meeting_factory.rb
index 1bafc93d571..94025c9446a 100644
--- a/modules/meeting/spec/factories/recurring_meeting_factory.rb
+++ b/modules/meeting/spec/factories/recurring_meeting_factory.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -48,7 +49,7 @@ FactoryBot.define do
recurring_meeting.project = project
# create template
- template = create(:structured_meeting_template,
+ template = create(:meeting_template,
:author_participates,
author: recurring_meeting.author,
recurring_meeting:,
diff --git a/modules/meeting/spec/features/meetings_activity_spec.rb b/modules/meeting/spec/features/meetings_activity_spec.rb
index e2f099644fa..d7ea85968f5 100644
--- a/modules/meeting/spec/features/meetings_activity_spec.rb
+++ b/modules/meeting/spec/features/meetings_activity_spec.rb
@@ -34,8 +34,6 @@ RSpec.describe "Meetings", :js do
let(:user) { create(:admin) }
let!(:meeting) { create(:meeting, project:, title: "Awesome meeting!") }
- let!(:agenda) { create(:meeting_agenda, meeting:, text: "foo") }
- let!(:minutes) { create(:meeting_minutes, meeting:, text: "minutes") }
before do
login_as(user)
@@ -47,9 +45,6 @@ RSpec.describe "Meetings", :js do
check "Meetings"
click_on "Apply"
-
- expect(page).to have_css(".op-activity-list--item-title", text: "Minutes: Awesome meeting!")
- expect(page).to have_css(".op-activity-list--item-title", text: "Agenda: Awesome meeting!")
expect(page).to have_css(".op-activity-list--item-title", text: "Meeting: Awesome meeting!")
end
end
diff --git a/modules/meeting/spec/features/meetings_attachments_spec.rb b/modules/meeting/spec/features/meetings_attachments_spec.rb
deleted file mode 100644
index 18289cbbe69..00000000000
--- a/modules/meeting/spec/features/meetings_attachments_spec.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-# frozen_string_literal: true
-require "spec_helper"
-require "features/page_objects/notification"
-
-RSpec.describe "Add an attachment to a meeting (agenda)", :js, :selenium do
- let(:role) do
- create(:project_role, permissions: %i[view_meetings edit_meetings create_meeting_agendas])
- end
-
- let(:dev) do
- create(:user, member_with_roles: { project => role })
- end
-
- let(:project) { create(:project) }
-
- let(:meeting) do
- create(
- :meeting,
- project:,
- title: "Versammlung",
- agenda: create(:meeting_agenda, text: "Versammlung")
- )
- end
-
- let(:attachments) { Components::Attachments.new }
- let(:image_fixture) { UploadedFile.load_from("spec/fixtures/files/image.png") }
- let(:editor) { Components::WysiwygEditor.new }
- let(:attachments_list) { Components::AttachmentsList.new }
-
- before do
- login_as(dev)
-
- visit "/meetings/#{meeting.id}"
-
- within "#tab-content-agenda .toolbar" do
- click_on "Edit"
- end
- end
-
- describe "wysiwyg editor" do
- context "if on an existing page" do
- it "can upload an image via drag & drop" do
- find(".ck-content")
-
- editor.expect_button "Upload image from computer"
-
- editor.drag_attachment image_fixture.path, "Some image caption"
-
- click_on "Save"
-
- content = find_test_selector("op-meeting--meeting_agenda")
-
- expect(content).to have_css("img")
- expect(content).to have_content("Some image caption")
- end
- end
- end
-
- describe "attachment dropzone" do
- it "can upload an image via attaching and drag & drop" do
- editor.wait_until_loaded
- attachments_list.wait_until_visible
-
- ##
- # Attach file manually
- editor.attachments_list.expect_empty
- attachments.attach_file_on_input(image_fixture.path)
- editor.wait_until_upload_progress_toaster_cleared
- editor.attachments_list.expect_attached("image.png")
-
- ##
- # and via drag & drop
- editor.attachments_list.drag_enter
- editor.attachments_list.drop(image_fixture)
- editor.wait_until_upload_progress_toaster_cleared
- editor.attachments_list.expect_attached("image.png", count: 2)
- end
- end
-end
diff --git a/modules/meeting/spec/features/meetings_close_spec.rb b/modules/meeting/spec/features/meetings_close_spec.rb
deleted file mode 100644
index 534a51c779c..00000000000
--- a/modules/meeting/spec/features/meetings_close_spec.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-require "spec_helper"
-
-RSpec.describe "Meetings close" do
- let(:project) { create(:project, enabled_module_names: %w[meetings]) }
- let(:user) do
- create(:user,
- member_with_permissions: { project => permissions })
- end
- let(:other_user) do
- create(:user,
- member_with_permissions: { project => permissions })
- end
-
- let!(:meeting) { create(:meeting, :author_participates, project:, title: "Own awesome meeting!", author: user) }
- let!(:meeting_agenda) { create(:meeting_agenda, meeting:, text: "asdf") }
-
- before do
- login_as(user)
- end
-
- context "with permission to close meetings",
- :js do
- let(:permissions) { %i[view_meetings close_meeting_agendas] }
-
- it "can delete own and other`s meetings" do
- visit project_meetings_path(project)
-
- click_on meeting.title
-
- # Go to minutes, expect uneditable
- find(".op-tab-row--link", text: "MINUTES").click
- wait_for_network_idle
- expect(page).to have_css(".button", text: "Close the agenda to begin the Minutes")
-
- # Close the meeting
- find(".op-tab-row--link", text: "AGENDA").click
- wait_for_network_idle
-
- accept_confirm do
- find(".button", text: "Close").click
- end
-
- # Expect to be on minutes
- expect(page).to have_css(".op-tab-row--link_selected", text: "MINUTES")
-
- # Copies the text
- expect(page).to have_css("#tab-content-minutes", text: "asdf")
-
- # Go back to agenda, expect we can open it again
- find(".op-tab-row--link", text: "AGENDA").click
- accept_confirm do
- find(".button", text: "Open").click
- end
- expect(page).to have_css(".button", text: "Close")
- end
- end
-
- context "without permission to close meetings" do
- let(:permissions) { %i[view_meetings] }
-
- it "cannot delete own and other`s meetings" do
- visit project_meetings_path(project)
-
- expect(page)
- .to have_no_link "Close"
- end
- end
-end
diff --git a/modules/meeting/spec/features/meetings_copy_spec.rb b/modules/meeting/spec/features/meetings_copy_spec.rb
deleted file mode 100644
index 6d4183a628a..00000000000
--- a/modules/meeting/spec/features/meetings_copy_spec.rb
+++ /dev/null
@@ -1,133 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-require "spec_helper"
-
-RSpec.describe "Meetings copy", :js do
- shared_let(:project) { create(:project, enabled_module_names: %w[meetings]) }
- shared_let(:permissions) { %i[view_meetings create_meetings] }
- shared_let(:user) do
- create(:user,
- member_with_permissions: { project => permissions }).tap do |u|
- u.pref[:time_zone] = "Etc/UTC"
-
- u.save!
- end
- end
- shared_let(:other_user) do
- create(:user,
- member_with_permissions: { project => permissions })
- end
-
- shared_let(:start_time) { Time.current.next_day.at_noon }
- shared_let(:duration) { 1.5 }
- shared_let(:agenda_text) { "We will talk" }
- shared_let(:meeting) do
- create(:meeting,
- :author_participates,
- author: user,
- project:,
- title: "Awesome meeting!",
- location: "Meeting room",
- duration:,
- start_time:).tap do |m|
- create(:meeting_agenda, meeting: m, text: agenda_text)
- create(:meeting_participant, :attendee, meeting: m, user: other_user)
- end
- end
-
- shared_let(:twelve_hour_format) { "%I:%M %p" }
- shared_let(:copied_meeting_time_heading) do
- date = (start_time + 1.day).strftime("%m/%d/%Y")
- start_of_meeting = start_time.strftime(twelve_hour_format)
- end_of_meeting = (start_time + meeting.duration.hours).strftime(twelve_hour_format)
-
- "Start time: #{date} #{start_of_meeting} - #{end_of_meeting} UTC+00:00"
- end
-
- before do
- login_as user
- end
-
- it "copying a meeting" do
- visit project_meetings_path(project)
-
- click_on meeting.title
-
- find_test_selector("meetings-more-dropdown-menu").click
- page.within(".menu-drop-down-container") do
- click_on "Copy"
- end
-
- expect(page)
- .to have_field "Title", with: meeting.title
- expect(page)
- .to have_field "Location", with: meeting.location
- expect(page)
- .to have_field "Duration", with: meeting.duration
- expect(page)
- .to have_field "Start date", with: (start_time + 1.day).strftime("%Y-%m-%d")
- expect(page)
- .to have_field "Time", with: start_time.strftime("%H:%M")
-
- click_on "Create"
-
- # Be on the new meeting's page with copied over attributes
- expect(page).to have_no_current_path meeting_path(meeting.id)
-
- expect(page)
- .to have_content("Added by #{user.name}")
- expect(page)
- .to have_content("Meeting: #{meeting.title}")
- expect(page)
- .to have_content(copied_meeting_time_heading)
- expect(page)
- .to have_content("Location: #{meeting.location}")
-
- # Copies the invitees
- expect(page)
- .to have_content "Invitees: #{other_user.name}"
-
- # Does not copy the attendees
- expect(page)
- .to have_no_content "Attendees: #{other_user.name}"
- expect(page)
- .to have_content "Attendees:"
-
- # Copies the agenda
- click_on "Agenda"
- expect(page)
- .to have_content agenda_text
-
- # Adds an entry to the history
- click_on "History"
- expect(page)
- .to have_content("Copied from Meeting ##{meeting.id}")
- end
-end
diff --git a/modules/meeting/spec/features/meetings_delete_spec.rb b/modules/meeting/spec/features/meetings_delete_spec.rb
deleted file mode 100644
index 17943bd6c6d..00000000000
--- a/modules/meeting/spec/features/meetings_delete_spec.rb
+++ /dev/null
@@ -1,101 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-require "spec_helper"
-
-RSpec.describe "Meetings deletion" do
- let(:project) { create(:project, enabled_module_names: %w[meetings]) }
- let(:user) do
- create(:user,
- member_with_permissions: { project => permissions })
- end
- let(:other_user) do
- create(:user,
- member_with_permissions: { project => permissions })
- end
-
- let!(:meeting) { create(:meeting, project:, title: "Own awesome meeting!", author: user) }
- let!(:other_meeting) { create(:meeting, project:, title: "Other awesome meeting!", author: other_user) }
-
- let(:index_path) { project_meetings_path(project) }
-
- before do
- create(:meeting_participant, :invitee, user:, meeting:)
- create(:meeting_participant, :invitee, user:, meeting: other_meeting)
-
- login_as(user)
- end
-
- context "with permission to delete meetings", :js do
- let(:permissions) { %i[view_meetings delete_meetings] }
-
- it "can delete own and other's meetings" do
- visit index_path
-
- click_on meeting.title
- accept_confirm do
- find_test_selector("meetings-more-dropdown-menu").click
- click_on "Delete"
- end
-
- expect(page)
- .to have_current_path index_path
-
- click_on other_meeting.title
- accept_confirm do
- find_test_selector("meetings-more-dropdown-menu").click
- click_on "Delete"
- end
-
- expect(page)
- .to have_content(I18n.t("meeting.blankslate.title"))
-
- expect(page)
- .to have_current_path index_path
- end
- end
-
- context "without permission to delete meetings" do
- let(:permissions) { %i[view_meetings] }
-
- it "cannot delete own and other's meetings" do
- visit index_path
-
- click_on meeting.title
- expect(page)
- .to have_no_link "Delete"
-
- visit index_path
-
- click_on other_meeting.title
- expect(page)
- .to have_no_link "Delete"
- end
- end
-end
diff --git a/modules/meeting/spec/features/meetings_new_spec.rb b/modules/meeting/spec/features/meetings_new_spec.rb
deleted file mode 100644
index fd6393051d7..00000000000
--- a/modules/meeting/spec/features/meetings_new_spec.rb
+++ /dev/null
@@ -1,349 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-require "spec_helper"
-
-require_relative "../support/pages/meetings/index"
-
-RSpec.describe "Meetings new", :js do
- shared_let(:project) { create(:project, enabled_module_names: %w[meetings]) }
- shared_let(:admin) { create(:admin) }
- let(:time_zone) { "Etc/UTC" }
- let(:user) do
- create(:user,
- lastname: "First",
- member_with_permissions: { project => permissions }).tap do |u|
- u.pref[:time_zone] = time_zone
-
- u.save!
- end
- end
- let(:other_user) do
- create(:user,
- lastname: "Second",
- member_with_permissions: { project => permissions })
- end
- let(:permissions) { %i[view_meetings create_meetings] }
- let(:current_user) { user }
-
- before do
- login_as current_user
- end
-
- context "when creating a meeting from the global page" do
- before do
- other_user
- project
- end
-
- let(:index_page) { Pages::Meetings::Index.new(project: nil) }
- let(:new_page) { Pages::Meetings::New.new(nil) }
-
- context "with permission to create meetings" do
- it "does not render menus" do
- new_page.visit!
- new_page.expect_no_main_menu
- end
-
- describe "clicking on the create new meeting button" do
- it "navigates to the global create form" do
- index_page.visit!
- index_page.click_create_new
- expect(page).to have_current_path(new_page.path)
- end
- end
-
- ["CET", "UTC", "", "Pacific Time (US & Canada)"].each do |zone|
- let(:time_zone) { zone }
-
- it "allows creating a project and handles errors in time zone #{zone}" do
- new_page.visit!
-
- expect_angular_frontend_initialized # Wait for project dropdown to be ready
-
- new_page.set_title "Some title"
- new_page.set_project project
-
- new_page.set_start_date "2013-03-28"
- new_page.set_start_time "13:30"
- new_page.set_duration "1.5"
- new_page.invite(other_user)
-
- show_page = new_page.click_create
-
- expect_flash(message: "Successful creation.")
-
- show_page.expect_invited(user, other_user)
-
- show_page.expect_date_time "03/28/2013 01:30 PM - 03:00 PM"
- end
- end
-
- context "without a title set" do
- before do
- new_page.visit!
-
- # Wait for project dropdown to be initialized
- expect_angular_frontend_initialized
-
- new_page.set_project project
-
- new_page.set_start_date "2013-03-28"
- new_page.set_start_time "13:30"
- new_page.set_duration "1.5"
- new_page.invite(other_user)
- end
-
- it "renders a validation error" do
- expect do
- new_page.click_create
- end.not_to change(Query, :count)
-
- # HTML required attribute validation error
- expect(page).to have_current_path(new_page.path)
- end
- end
-
- context "without a project set" do
- before do
- new_page.visit!
- new_page.set_title "Some title"
- new_page.set_start_date "2013-03-28"
- new_page.set_start_time "13:30"
- new_page.set_duration "1.5"
- end
-
- it "renders a validation error" do
- new_page.click_create
-
- expect_flash(type: :error,
- message: "#{Project.model_name.human} #{I18n.t('activerecord.errors.messages.blank')}")
-
- new_page.expect_project_dropdown
- end
- end
- end
-
- context "without permission to create meetings" do
- let(:permissions) { %i[view_meetings] }
-
- it "shows no edit link" do
- index_page.visit!
-
- index_page.expect_no_create_new_button
- end
- end
-
- context "as an admin" do
- let(:current_user) { admin }
-
- it "allows creating meeting in a project without members" do
- new_page.visit!
-
- expect_angular_frontend_initialized # Wait for project dropdown to be ready
-
- new_page.set_title "Some title"
-
- new_page.set_project project
-
- wait_for_network_idle # Wait for participant section to be fetched
-
- show_page = new_page.click_create
-
- expect_flash(message: "Successful creation.")
-
- # Not sure if that is then intended behaviour but that is what is currently programmed
- show_page.expect_invited(admin)
- end
-
- context "without a project set" do
- before do
- new_page.visit!
- new_page.set_title "Some title"
- end
-
- it "renders a validation error" do
- new_page.click_create
-
- expect(page).to have_text "#{Project.model_name.human} #{I18n.t('activerecord.errors.messages.blank')}"
- new_page.expect_project_dropdown
- end
- end
-
- context "without a title set" do
- before do
- new_page.visit!
-
- # Wait for project dropdown to be initialized
- expect_angular_frontend_initialized
-
- new_page.set_project project
- end
-
- it "renders a validation error" do
- expect do
- new_page.click_create
- end.not_to change(Query, :count)
-
- # HTML required attribute validation error
- expect(page).to have_current_path(new_page.path)
- end
- end
- end
- end
-
- context "when creating a meeting from the project-specific page" do
- let(:index_page) { Pages::Meetings::Index.new(project:) }
- let(:new_page) { Pages::Meetings::New.new(project) }
-
- context "with permission to create meetings" do
- before do
- other_user
- end
-
- describe "clicking on the create new meeting button" do
- it "navigates to the project-specific create form" do
- index_page.visit!
- index_page.click_create_new
- expect(page).to have_current_path(new_page.path)
- end
- end
-
- ["CET", "UTC", "", "Pacific Time (US & Canada)"].each do |zone|
- let(:time_zone) { zone }
-
- it "allows creating a project and handles errors in time zone #{zone}" do
- new_page.visit!
-
- new_page.set_title "Some title"
- new_page.set_start_date "2013-03-28"
- new_page.set_start_time "13:30"
- new_page.set_duration "1.5"
- new_page.invite(other_user)
-
- show_page = new_page.click_create
-
- expect_flash(message: "Successful creation.")
-
- show_page.expect_invited(user, other_user)
-
- show_page.expect_date_time "03/28/2013 01:30 PM - 03:00 PM"
- end
- end
-
- context "without a title set" do
- before do
- new_page.visit!
- new_page.set_start_date "2013-03-28"
- new_page.set_start_time "13:30"
- new_page.set_duration "1.5"
- new_page.invite(other_user)
- end
-
- it "renders a validation error" do
- expect do
- new_page.click_create
- end.not_to change(Query, :count)
-
- # HTML required attribute validation error
- expect(page).to have_current_path(new_page.path)
- end
- end
- end
-
- context "without permission to create meetings" do
- let(:permissions) { %i[view_meetings] }
-
- it "shows no edit link" do
- index_page.visit!
-
- index_page.expect_no_create_new_button
- end
- end
-
- context "as an admin" do
- let(:current_user) { admin }
- let(:field) do
- TextEditorField.new(page,
- "",
- selector: test_selector("op-meeting--meeting_agenda"))
- end
-
- it "allows creating meeting in a project without members" do
- new_page.visit!
-
- new_page.set_title "Some title"
-
- show_page = new_page.click_create
-
- expect_flash(message: "Successful creation.")
-
- # Not sure if that is then intended behaviour but that is what is currently programmed
- show_page.expect_invited(admin)
- end
-
- context "without a title set" do
- before do
- new_page.visit!
- end
-
- it "renders a validation error" do
- expect do
- new_page.click_create
- end.not_to change(Query, :count)
-
- # HTML required attribute validation error
- expect(page).to have_current_path(new_page.path)
- end
- end
-
- it "can save the meeting agenda via cmd+Enter" do
- new_page.visit!
-
- new_page.set_title "Some title"
-
- new_page.click_create
-
- expect_flash(message: "Successful creation.")
-
- meeting = Meeting.last
-
- field.set_value("My new meeting text")
-
- field.submit_by_enter
-
- expect_flash(message: "Successful update")
-
- meeting.reload
-
- expect(meeting.agenda.text).to eq "My new meeting text"
- end
- end
- end
-end
diff --git a/modules/meeting/spec/features/meetings_participants_spec.rb b/modules/meeting/spec/features/meetings_participants_spec.rb
deleted file mode 100644
index 16ec0cd6d2d..00000000000
--- a/modules/meeting/spec/features/meetings_participants_spec.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-require "spec_helper"
-
-require_relative "../support/pages/meetings/edit"
-
-RSpec.describe "Meetings participants" do
- let(:project) { create(:project, enabled_module_names: %w[meetings]) }
- let!(:user) do
- create(:user,
- firstname: "Current",
- member_with_permissions: { project => %i[view_meetings edit_meetings] })
- end
- let!(:viewer_user) do
- create(:user,
- firstname: "Viewer",
- member_with_permissions: { project => %i[view_meetings] })
- end
- let!(:non_viewer_user) do
- create(:user,
- firstname: "Nonviewer",
- member_with_permissions: { project => %i[] })
- end
- let(:edit_page) { Pages::Meetings::Edit.new(meeting) }
- let!(:meeting) { create(:meeting, project:, title: "Awesome meeting!") }
-
- before do
- login_as(user)
- end
-
- it "allows setting members to participants which are allowed to view the meeting" do
- edit_page.visit!
-
- edit_page.expect_available_participant(user)
- edit_page.expect_available_participant(viewer_user)
-
- edit_page.expect_not_available_participant(non_viewer_user)
-
- edit_page.invite(viewer_user)
- show_page = edit_page.click_save
- expect_flash(message: "Successful update")
-
- show_page.expect_invited(viewer_user)
-
- show_page.click_edit
-
- edit_page.uninvite(viewer_user)
- show_page = edit_page.click_save
-
- expect_flash(message: "Successful update")
-
- show_page.expect_uninvited(viewer_user)
- end
-
- context "with an invalid user reference" do
- let(:show_page) { Pages::Meetings::Show.new(meeting) }
- let(:meeting_participant) { create(:meeting_participant, user: viewer_user, meeting:) }
-
- before do
- meeting_participant.update_column(:user_id, 12341234)
- end
-
- it "still allows to view the meeting" do
- show_page.visit!
-
- show_page.expect_invited meeting.author
- show_page.expect_uninvited viewer_user
- end
- end
-end
diff --git a/modules/meeting/spec/features/meetings_search_spec.rb b/modules/meeting/spec/features/meetings_search_spec.rb
index 48a9188823d..a8f96b18584 100644
--- a/modules/meeting/spec/features/meetings_search_spec.rb
+++ b/modules/meeting/spec/features/meetings_search_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -35,7 +36,7 @@ RSpec.describe "Meeting search", :js do
let(:role) { create(:project_role, permissions: %i(view_meetings view_work_packages)) }
let(:user) { create(:user, member_with_roles: { project => role }) }
- let!(:meeting) { create(:structured_meeting, project:) }
+ let!(:meeting) { create(:meeting, project:) }
let!(:agenda_item) { create(:meeting_agenda_item, meeting:) }
before do
diff --git a/modules/meeting/spec/features/meetings_show_spec.rb b/modules/meeting/spec/features/meetings_show_spec.rb
deleted file mode 100644
index 93ec40796b8..00000000000
--- a/modules/meeting/spec/features/meetings_show_spec.rb
+++ /dev/null
@@ -1,183 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-require "spec_helper"
-require_relative "../support/pages/meetings/show"
-
-RSpec.describe "Meetings", :js do
- let(:project) { create(:project, enabled_module_names: %w[meetings]) }
- let(:role) { create(:project_role, permissions:) }
- let(:user) do
- create(:user,
- member_with_roles: { project => role })
- end
-
- let!(:meeting) { create(:meeting, project:, title: "Awesome meeting!") }
- let(:show_page) { Pages::Meetings::Show.new(meeting) }
-
- current_user { user }
-
- describe "navigate to meeting page" do
- before do
- create(:meeting_participant, :invitee, user:, meeting:)
- end
-
- let(:permissions) { %i[view_meetings] }
-
- it "can visit the meeting" do
- visit meetings_path(project)
-
- find("div.title a", text: "Awesome meeting!", wait: 10).click
- expect(page).to have_css("h2", text: "Meeting: Awesome meeting!")
-
- expect(page).to have_test_selector("op-meeting--meeting_agenda",
- text: "There is currently nothing to display")
- end
-
- context "with a location" do
- context "as a valid url" do
- it "renders a link to the meeting location" do
- show_page.visit!
-
- show_page.expect_link_to_location(meeting.location)
- end
- end
-
- context "as an invalid url" do
- before do
- meeting.update!(location: "badurl")
- end
-
- it "renders the meeting location as plaintext" do
- show_page.visit!
-
- show_page.expect_plaintext_location(meeting.location)
- end
- end
- end
-
- context "with an open agenda" do
- let!(:agenda) { create(:meeting_agenda, meeting:, text: "foo") }
- let(:agenda_update) { create(:meeting_agenda, meeting:, text: "bla") }
-
- it "shows the agenda" do
- visit meeting_path(meeting)
- expect(page).to have_test_selector("op-meeting--meeting_agenda",
- text: "foo")
-
- # May not edit
- expect(page).to have_no_css(".button--edit-agenda")
- expect(page).not_to have_test_selector("op-meeting--meeting_agenda",
- text: "Edit")
- end
-
- it "can view history" do
- agenda_update
-
- visit meeting_path(meeting)
-
- click_on "History"
-
- find_by_id("version-1").click
- expect(page).to have_test_selector("op-meeting--meeting_agenda", text: "foo")
- end
-
- context "and edit permissions" do
- let(:permissions) { %i[view_meetings create_meeting_agendas] }
- let(:field) do
- TextEditorField.new(page,
- "",
- selector: test_selector("op-meeting--meeting_agenda"))
- end
-
- it "can edit the agenda" do
- visit meeting_path(meeting)
-
- find(".toolbar-item", text: "Edit").click
-
- field.expect_value("foo")
-
- field.set_value("My new meeting text")
-
- field.submit_by_enter
-
- expect_and_dismiss_flash(message: "Successful update")
-
- meeting.reload
-
- expect(meeting.agenda.text).to eq "My new meeting text"
- end
- end
-
- context "and edit minutes permissions" do
- let(:permissions) { %i[view_meetings create_meeting_minutes] }
-
- it "can not edit the minutes" do
- visit meeting_path(meeting)
- click_on "Minutes"
- expect(page).not_to have_test_selector("op-meeting--meeting_minutes", text: "Edit")
- expect(page).to have_test_selector("op-meeting--meeting_minutes",
- text: "There is currently nothing to display")
- end
- end
- end
-
- context "with a locked agenda" do
- let!(:agenda) { create(:meeting_agenda, meeting:, text: "foo", locked: true) }
-
- it "shows the minutes when visiting" do
- visit meeting_path(meeting)
- expect(page).to have_no_css("h2", text: "Agenda")
- expect(page).to have_no_css("#meeting_minutes_text")
- expect(page).to have_css("h2", text: "Minutes")
- end
-
- context "and edit permissions" do
- let(:permissions) { %i[view_meetings create_meeting_minutes] }
- let(:field) do
- TextEditorField.new(page,
- "",
- selector: test_selector("op-meeting--meeting_minutes"))
- end
-
- it "can edit the minutes" do
- visit meeting_path(meeting)
-
- field.set_value("This is what we talked about")
-
- click_on "Save"
-
- expect(page)
- .to have_css(".op-uc-container",
- text: "This is what we talked about")
- end
- end
- end
- end
-end
diff --git a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_create_spec.rb b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_create_spec.rb
index 721149f006c..77d87002176 100644
--- a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_create_spec.rb
+++ b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_create_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -29,8 +30,7 @@
require "spec_helper"
-require_relative "../../support/pages/meetings/new"
-require_relative "../../support/pages/structured_meeting/show"
+require_relative "../../support/pages/meetings/show"
require_relative "../../support/pages/recurring_meeting/show"
require_relative "../../support/pages/meetings/index"
@@ -61,7 +61,7 @@ RSpec.describe "Recurring meetings creation",
let(:current_user) { user }
let(:meeting) { RecurringMeeting.last }
let(:show_page) { Pages::RecurringMeeting::Show.new(meeting) }
- let(:template_page) { Pages::StructuredMeeting::Show.new(meeting.template) }
+ let(:template_page) { Pages::Meetings::Show.new(meeting.template) }
let(:meetings_page) { Pages::Meetings::Index.new(project:) }
before do
diff --git a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_crud_spec.rb b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_crud_spec.rb
index aed1150fd41..362a047985a 100644
--- a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_crud_spec.rb
+++ b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_crud_spec.rb
@@ -30,8 +30,7 @@
require "spec_helper"
-require_relative "../../support/pages/meetings/new"
-require_relative "../../support/pages/structured_meeting/show"
+require_relative "../../support/pages/meetings/show"
require_relative "../../support/pages/recurring_meeting/show"
require_relative "../../support/pages/meetings/index"
diff --git a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_end_series_spec.rb b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_end_series_spec.rb
index a54d08eb525..ca45180dc36 100644
--- a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_end_series_spec.rb
+++ b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_end_series_spec.rb
@@ -30,8 +30,7 @@
require "spec_helper"
-require_relative "../../support/pages/meetings/new"
-require_relative "../../support/pages/structured_meeting/show"
+require_relative "../../support/pages/meetings/show"
require_relative "../../support/pages/recurring_meeting/show"
require_relative "../../support/pages/meetings/index"
diff --git a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_global_create_spec.rb b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_global_create_spec.rb
index 795a56f15c0..cc9d629044f 100644
--- a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_global_create_spec.rb
+++ b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_global_create_spec.rb
@@ -29,8 +29,7 @@
require "spec_helper"
-require_relative "../../support/pages/meetings/new"
-require_relative "../../support/pages/structured_meeting/show"
+require_relative "../../support/pages/meetings/show"
require_relative "../../support/pages/recurring_meeting/show"
require_relative "../../support/pages/meetings/index"
diff --git a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_global_crud_spec.rb b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_global_crud_spec.rb
index da7583bf956..6f03deb0517 100644
--- a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_global_crud_spec.rb
+++ b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_global_crud_spec.rb
@@ -30,8 +30,7 @@
require "spec_helper"
-require_relative "../../support/pages/meetings/new"
-require_relative "../../support/pages/structured_meeting/show"
+require_relative "../../support/pages/meetings/show"
require_relative "../../support/pages/recurring_meeting/show"
require_relative "../../support/pages/meetings/index"
diff --git a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_move_to_next_spec.rb b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_move_to_next_spec.rb
index 860a5808317..80e4c362586 100644
--- a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_move_to_next_spec.rb
+++ b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_move_to_next_spec.rb
@@ -30,8 +30,7 @@
require "spec_helper"
-require_relative "../../support/pages/meetings/new"
-require_relative "../../support/pages/structured_meeting/show"
+require_relative "../../support/pages/meetings/show"
require_relative "../../support/pages/recurring_meeting/show"
require_relative "../../support/pages/meetings/index"
@@ -60,8 +59,8 @@ RSpec.describe "Recurring meetings move to next meeting", :js do
end_after: "never",
author: user_with_manage_permissions
end
- shared_let(:structured_meeting) do
- create :structured_meeting,
+ shared_let(:meeting) do
+ create :meeting,
project:,
start_time: DateTime.parse("2025-01-28T10:30:00Z"),
duration: 1,
@@ -75,10 +74,8 @@ RSpec.describe "Recurring meetings move to next meeting", :js do
series.meetings.not_templated.first
end
- let!(:agenda_item) { create(:meeting_agenda_item, meeting: structured_meeting, title: "Test notes") }
- let!(:series_agenda_item) { create(:meeting_agenda_item, meeting: recurring_meeting, title: "Test notes") }
-
- let(:meeting_page) { Pages::StructuredMeeting::Show.new(meeting) }
+ let!(:agenda_item) { create(:meeting_agenda_item, meeting:, title: "Test notes") }
+ let(:meeting_page) { Pages::Meetings::Show.new(meeting) }
before do
login_as current_user
@@ -94,12 +91,13 @@ RSpec.describe "Recurring meetings move to next meeting", :js do
it "shows the move to next meeting option" do
meeting_page.expect_agenda_item(title: "Test notes")
- meeting_page.expect_agenda_action_menu(series_agenda_item)
accept_confirm do
- meeting_page.select_action(series_agenda_item, "Move to next meeting")
+ meeting_page.select_action(agenda_item, "Move to next meeting")
end
+ expect_and_dismiss_flash(message: "Agenda item moved to the next meeting")
+
meeting_page.expect_no_agenda_item(title: "Test notes")
end
end
@@ -115,10 +113,9 @@ RSpec.describe "Recurring meetings move to next meeting", :js do
it "shows the move to next meeting option" do
meeting_page.expect_agenda_item(title: "Test notes")
- meeting_page.expect_agenda_action_menu(series_agenda_item)
accept_confirm do
- meeting_page.select_action(series_agenda_item, "Move to next meeting")
+ meeting_page.select_action(agenda_item, "Move to next meeting")
end
expect(page).to have_text "Unable to move to the next meeting since it has been cancelled."
@@ -136,8 +133,7 @@ RSpec.describe "Recurring meetings move to next meeting", :js do
end
end
- context "when viewing a structured meeting" do
- let(:meeting) { structured_meeting }
+ context "when viewing a one-time meeting" do
let(:current_user) { user_with_manage_permissions }
it "does not show the move to next meeting option" do
diff --git a/modules/meeting/spec/features/structured_meetings/attachment_upload_spec.rb b/modules/meeting/spec/features/structured_meetings/attachment_upload_spec.rb
index 8e2e41f26c7..5ff6d968bfd 100644
--- a/modules/meeting/spec/features/structured_meetings/attachment_upload_spec.rb
+++ b/modules/meeting/spec/features/structured_meetings/attachment_upload_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -29,7 +30,7 @@
require "spec_helper"
require "features/page_objects/notification"
-require_relative "../../support/pages/structured_meeting/show"
+require_relative "../../support/pages/meetings/show"
RSpec.describe "Upload attachment to meetings", :js do
let(:user) do
@@ -43,8 +44,8 @@ RSpec.describe "Upload attachment to meetings", :js do
let(:wiki_page_content) { project.wiki.pages.first.text }
let(:attachment_list) { Components::AttachmentsList.new("#content") }
- let(:meeting) { create(:structured_meeting, project:) }
- let(:show_page) { Pages::StructuredMeeting::Show.new(meeting) }
+ let(:meeting) { create(:meeting, project:) }
+ let(:show_page) { Pages::Meetings::Show.new(meeting) }
before do
login_as(user)
diff --git a/modules/meeting/spec/features/structured_meetings/history_spec.rb b/modules/meeting/spec/features/structured_meetings/history_spec.rb
index feb6a9d28f8..78d9296e73d 100644
--- a/modules/meeting/spec/features/structured_meetings/history_spec.rb
+++ b/modules/meeting/spec/features/structured_meetings/history_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -29,9 +30,8 @@
require "spec_helper"
-require_relative "../../support/pages/meetings/new"
-require_relative "../../support/pages/structured_meeting/show"
-require_relative "../../support/pages/structured_meeting/history"
+require_relative "../../support/pages/meetings/show"
+require_relative "../../support/pages/meetings/history"
RSpec.describe "history",
:js do
@@ -72,7 +72,7 @@ RSpec.describe "history",
end
shared_let(:meeting) do
User.execute_as(user) do
- create(:structured_meeting,
+ create(:meeting,
project:,
start_time: DateTime.parse("2024-03-28T13:30:00Z"),
title: "Some title",
@@ -82,8 +82,8 @@ RSpec.describe "history",
end
end
- let(:show_page) { Pages::StructuredMeeting::Show.new(meeting) }
- let(:history_page) { Pages::StructuredMeeting::History.new(meeting) }
+ let(:show_page) { Pages::Meetings::Show.new(meeting) }
+ let(:history_page) { Pages::Meeting::History.new(meeting) }
let(:editor) { Components::WysiwygEditor.new "#content", "opce-ckeditor-augmented-textarea" }
it "allows browsing the history", with_settings: { journal_aggregation_time_minutes: 0 } do
diff --git a/modules/meeting/spec/features/structured_meetings/meeting_outcomes/meeting_outcomes_crud_spec.rb b/modules/meeting/spec/features/structured_meetings/meeting_outcomes/meeting_outcomes_crud_spec.rb
index 5c11ad33be1..0f5eee55cce 100644
--- a/modules/meeting/spec/features/structured_meetings/meeting_outcomes/meeting_outcomes_crud_spec.rb
+++ b/modules/meeting/spec/features/structured_meetings/meeting_outcomes/meeting_outcomes_crud_spec.rb
@@ -30,7 +30,7 @@
require "spec_helper"
-require_relative "../../../support/pages/structured_meeting/show"
+require_relative "../../../support/pages/meetings/show"
RSpec.describe "Meeting Outcomes CRUD", :js do
shared_let(:project) { create(:project, enabled_module_names: %w[meetings]) }
@@ -46,7 +46,7 @@ RSpec.describe "Meeting Outcomes CRUD", :js do
member_with_permissions: { project => %i[view_meetings manage_agendas close_meeting_agendas] }
end
shared_let(:meeting) do
- create :structured_meeting,
+ create :meeting,
project:,
start_time: "2024-12-31T13:30:00Z",
duration: 1.5,
@@ -59,7 +59,7 @@ RSpec.describe "Meeting Outcomes CRUD", :js do
let(:current_user) { user }
let(:state) { :in_progress }
- let(:show_page) { Pages::StructuredMeeting::Show.new(meeting) }
+ let(:show_page) { Pages::Meetings::Show.new(meeting) }
let(:field) do
TextEditorField.new(page, "Outcome", selector: test_selector("meeting-outcome-input"))
end
diff --git a/modules/meeting/spec/features/structured_meetings/mobile_structure_meeting_spec.rb b/modules/meeting/spec/features/structured_meetings/mobile_structure_meeting_spec.rb
index 997873fbc0a..8a758188e16 100644
--- a/modules/meeting/spec/features/structured_meetings/mobile_structure_meeting_spec.rb
+++ b/modules/meeting/spec/features/structured_meetings/mobile_structure_meeting_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -29,9 +30,9 @@
require "spec_helper"
-require_relative "../../support/pages/structured_meeting//mobile/show"
+require_relative "../../support/pages/meetings/mobile/show"
-RSpec.describe "Structured meetings CRUD",
+RSpec.describe "Meetings CRUD",
:js do
include Components::Autocompleter::NgSelectAutocompleteHelpers
@@ -57,14 +58,14 @@ RSpec.describe "Structured meetings CRUD",
end
shared_let(:meeting) do
- create(:structured_meeting,
+ create(:meeting,
:author_participates,
project:,
author: user)
end
let(:current_user) { user }
- let(:show_page) { Pages::StructuredMeeting::Mobile::Show.new(StructuredMeeting.last) }
+ let(:show_page) { Pages::Meetings::Mobile::Show.new(Meeting.last) }
include_context "with mobile screen size"
@@ -73,7 +74,7 @@ RSpec.describe "Structured meetings CRUD",
show_page.visit!
end
- it "can edit participants of a structured meeting" do
+ it "can edit participants of a meeting" do
expect(page).to have_current_path(show_page.path)
show_page.expect_participants(count: 1)
diff --git a/modules/meeting/spec/features/structured_meetings/structured_meeting_crud_spec.rb b/modules/meeting/spec/features/structured_meetings/structured_meeting_crud_spec.rb
index f9ba5e27b9f..2046909f019 100644
--- a/modules/meeting/spec/features/structured_meetings/structured_meeting_crud_spec.rb
+++ b/modules/meeting/spec/features/structured_meetings/structured_meeting_crud_spec.rb
@@ -30,11 +30,10 @@
require "spec_helper"
-require_relative "../../support/pages/meetings/new"
-require_relative "../../support/pages/structured_meeting/show"
+require_relative "../../support/pages/meetings/show"
require_relative "../../support/pages/meetings/index"
-RSpec.describe "Structured meetings CRUD",
+RSpec.describe "Meetings CRUD",
:js do
include Components::Autocompleter::NgSelectAutocompleteHelpers
@@ -63,9 +62,8 @@ RSpec.describe "Structured meetings CRUD",
end
let(:current_user) { user }
- let(:new_page) { Pages::Meetings::New.new(project) }
- let(:meeting) { StructuredMeeting.last }
- let(:show_page) { Pages::StructuredMeeting::Show.new(meeting) }
+ let(:meeting) { Meeting.last }
+ let(:show_page) { Pages::Meetings::Show.new(meeting) }
let(:meetings_page) { Pages::Meetings::Index.new(project:) }
before do |test|
@@ -88,7 +86,7 @@ RSpec.describe "Structured meetings CRUD",
meetings_page.click_create
end
- it "can create a structured meeting and add agenda items" do
+ it "can create a meeting and add agenda items" do
expect_and_dismiss_flash(type: :success, message: "Successful creation")
# Does not send invitation mails by default
@@ -312,7 +310,7 @@ RSpec.describe "Structured meetings CRUD",
fill_in "Title", with: "Some title"
click_on "Create meeting"
- new_meeting = StructuredMeeting.last
+ new_meeting = Meeting.last
expect(page).to have_current_path "/projects/#{project.identifier}/meetings/#{new_meeting.id}"
# check for copied agenda items
@@ -330,13 +328,13 @@ RSpec.describe "Structured meetings CRUD",
end
context "with a work package reference to another" do
- let!(:meeting) { create(:structured_meeting, project:, author: current_user) }
+ let!(:meeting) { create(:meeting, project:, author: current_user) }
let!(:other_project) { create(:project) }
let!(:other_wp) { create(:work_package, project: other_project, author: current_user, subject: "Private task") }
let!(:role) { create(:project_role, permissions: %w[view_work_packages]) }
let!(:membership) { create(:member, principal: user, project: other_project, roles: [role]) }
let!(:agenda_item) { create(:wp_meeting_agenda_item, meeting:, author: current_user, work_package: other_wp) }
- let(:show_page) { Pages::StructuredMeeting::Show.new(meeting) }
+ let(:show_page) { Pages::Meetings::Show.new(meeting) }
it "shows correctly for author, but returns an unresolved reference for the second user" do
show_page.visit!
@@ -352,8 +350,8 @@ RSpec.describe "Structured meetings CRUD",
end
context "with sections" do
- let!(:meeting) { create(:structured_meeting, project:, author: current_user) }
- let(:show_page) { Pages::StructuredMeeting::Show.new(meeting) }
+ let!(:meeting) { create(:meeting, project:, author: current_user) }
+ let(:show_page) { Pages::Meetings::Show.new(meeting) }
context "when starting with empty sections" do
it "can add, edit and delete sections" do
diff --git a/modules/meeting/spec/features/structured_meetings/structured_meeting_delete_spec.rb b/modules/meeting/spec/features/structured_meetings/structured_meeting_delete_spec.rb
index fc8a25344d4..6779b75c08d 100644
--- a/modules/meeting/spec/features/structured_meetings/structured_meeting_delete_spec.rb
+++ b/modules/meeting/spec/features/structured_meetings/structured_meeting_delete_spec.rb
@@ -30,11 +30,10 @@
require "spec_helper"
-require_relative "../../support/pages/meetings/new"
-require_relative "../../support/pages/structured_meeting/show"
+require_relative "../../support/pages/meetings/show"
require_relative "../../support/pages/meetings/index"
-RSpec.describe "Structured meetings deletion",
+RSpec.describe "Meetings deletion",
:js do
include Components::Autocompleter::NgSelectAutocompleteHelpers
diff --git a/modules/meeting/spec/features/structured_meetings/structured_meeting_global_crud_spec.rb b/modules/meeting/spec/features/structured_meetings/structured_meeting_global_crud_spec.rb
index 64cb70ac8e2..78866d68202 100644
--- a/modules/meeting/spec/features/structured_meetings/structured_meeting_global_crud_spec.rb
+++ b/modules/meeting/spec/features/structured_meetings/structured_meeting_global_crud_spec.rb
@@ -30,11 +30,10 @@
require "spec_helper"
-require_relative "../../support/pages/meetings/new"
-require_relative "../../support/pages/structured_meeting/show"
+require_relative "../../support/pages/meetings/show"
require_relative "../../support/pages/meetings/index"
-RSpec.describe "Structured meetings global CRUD", :js do
+RSpec.describe "Meetings global CRUD", :js do
include Components::Autocompleter::NgSelectAutocompleteHelpers
shared_let(:project) { create(:project, enabled_module_names: %w[meetings work_package_tracking]) }
@@ -62,9 +61,8 @@ RSpec.describe "Structured meetings global CRUD", :js do
end
let(:current_user) { user }
- let(:new_page) { Pages::Meetings::New.new(project) }
- let(:meeting) { StructuredMeeting.last }
- let(:show_page) { Pages::StructuredMeeting::Show.new(meeting) }
+ let(:meeting) { Meeting.last }
+ let(:show_page) { Pages::Meetings::Show.new(meeting) }
let(:meetings_page) { Pages::Meetings::Index.new(project: nil) }
before do
diff --git a/modules/meeting/spec/features/structured_meetings/structured_meeting_participant_spec.rb b/modules/meeting/spec/features/structured_meetings/structured_meeting_participant_spec.rb
index 6df140abe18..86091b124c2 100644
--- a/modules/meeting/spec/features/structured_meetings/structured_meeting_participant_spec.rb
+++ b/modules/meeting/spec/features/structured_meetings/structured_meeting_participant_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -29,9 +30,9 @@
require "spec_helper"
-require_relative "../../support/pages/structured_meeting//mobile/show"
+require_relative "../../support/pages/meetings/mobile/show"
-RSpec.describe "Structured meetings participants",
+RSpec.describe "Meetings participants",
:js do
include Components::Autocompleter::NgSelectAutocompleteHelpers
@@ -57,21 +58,21 @@ RSpec.describe "Structured meetings participants",
end
shared_let(:meeting) do
- create(:structured_meeting,
+ create(:meeting,
:author_participates,
project:,
author: user)
end
let(:current_user) { user }
- let(:show_page) { Pages::StructuredMeeting::Show.new(StructuredMeeting.last) }
+ let(:show_page) { Pages::Meetings::Show.new(Meeting.last) }
before do
login_as current_user
show_page.visit!
end
- it "can edit participants of a structured meeting" do
+ it "can edit participants of a meeting" do
expect(page).to have_current_path(show_page.path)
show_page.open_participant_form
diff --git a/modules/meeting/spec/features/structured_meetings/structured_meeting_update_flash_spec.rb b/modules/meeting/spec/features/structured_meetings/structured_meeting_update_flash_spec.rb
index ed037ac0320..2db7b6b2951 100644
--- a/modules/meeting/spec/features/structured_meetings/structured_meeting_update_flash_spec.rb
+++ b/modules/meeting/spec/features/structured_meetings/structured_meeting_update_flash_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -29,10 +30,9 @@
require "spec_helper"
-require_relative "../../support/pages/meetings/new"
-require_relative "../../support/pages/structured_meeting/show"
+require_relative "../../support/pages/meetings/show"
-RSpec.describe "Structured meetings CRUD",
+RSpec.describe "Meetings CRUD",
:js,
:selenium do
include Components::Autocompleter::NgSelectAutocompleteHelpers
@@ -41,9 +41,8 @@ RSpec.describe "Structured meetings CRUD",
shared_let(:user) { create(:admin) }
current_user { user }
- let(:new_page) { Pages::Meetings::New.new(project) }
- let(:meeting) { create(:structured_meeting, project:, author: current_user) }
- let(:show_page) { Pages::StructuredMeeting::Show.new(meeting) }
+ let(:meeting) { create(:meeting, project:, author: current_user) }
+ let(:show_page) { Pages::Meetings::Show.new(meeting) }
describe "meeting update flash" do
before do
@@ -134,7 +133,7 @@ RSpec.describe "Structured meetings CRUD",
## Edit meeting details
within_window(first_window) do
find_test_selector("edit-meeting-details-button").click
- fill_in "structured_meeting_duration", with: "2.5"
+ fill_in "meeting_duration", with: "2.5"
click_link_or_button "Save"
# Expect updated duration
diff --git a/modules/meeting/spec/features/structured_meetings/turbo_links_spec.rb b/modules/meeting/spec/features/structured_meetings/turbo_links_spec.rb
index 935958f11a5..fa027164a9c 100644
--- a/modules/meeting/spec/features/structured_meetings/turbo_links_spec.rb
+++ b/modules/meeting/spec/features/structured_meetings/turbo_links_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -29,10 +30,9 @@
require "spec_helper"
-require_relative "../../support/pages/meetings/new"
-require_relative "../../support/pages/structured_meeting/show"
+require_relative "../../support/pages/meetings/show"
-RSpec.describe "Structured meetings links caught by turbo",
+RSpec.describe "Meetings links caught by turbo",
:js do
include Rails.application.routes.url_helpers
@@ -47,8 +47,8 @@ RSpec.describe "Structured meetings links caught by turbo",
u.save!
end
end
- shared_let(:meeting1) { create(:structured_meeting, title: "First meeting", project:) }
- shared_let(:meeting2) { create(:structured_meeting, title: "Other meeting", project:) }
+ shared_let(:meeting1) { create(:meeting, title: "First meeting", project:) }
+ shared_let(:meeting2) { create(:meeting, title: "Other meeting", project:) }
let(:notes) do
<<~NOTES
@@ -56,7 +56,7 @@ RSpec.describe "Structured meetings links caught by turbo",
NOTES
end
let!(:agenda_item) { create(:meeting_agenda_item, meeting: meeting1, notes:) }
- let(:show_page) { Pages::StructuredMeeting::Show.new(meeting1) }
+ let(:show_page) { Pages::Meetings::Show.new(meeting1) }
before do
login_as user
diff --git a/modules/meeting/spec/features/structured_meetings/work_package_meetings_tab_spec.rb b/modules/meeting/spec/features/structured_meetings/work_package_meetings_tab_spec.rb
index 8ce9d2e8deb..510735987ee 100644
--- a/modules/meeting/spec/features/structured_meetings/work_package_meetings_tab_spec.rb
+++ b/modules/meeting/spec/features/structured_meetings/work_package_meetings_tab_spec.rb
@@ -30,7 +30,7 @@
require "spec_helper"
require_relative "../../support/pages/work_package_meetings_tab"
-require_relative "../../support/pages/structured_meeting/show"
+require_relative "../../support/pages/meetings/show"
RSpec.describe "Open the Meetings tab",
:js do
@@ -98,8 +98,8 @@ RSpec.describe "Open the Meetings tab",
context "when the user has the permission to see the tab, but the work package is linked in two projects" do
let(:other_project) { create(:project, enabled_module_names: %w[meetings]) }
- let!(:visible_meeting) { create(:structured_meeting, project:) }
- let!(:invisible_meeting) { create(:structured_meeting, project: other_project) }
+ let!(:visible_meeting) { create(:meeting, project:) }
+ let!(:invisible_meeting) { create(:meeting, project: other_project) }
let!(:meeting_agenda_item_of_visible_meeting) do
create(:meeting_agenda_item, meeting: visible_meeting, work_package:, notes: "Public note!")
@@ -131,7 +131,7 @@ RSpec.describe "Open the Meetings tab",
end
context "with another past meeting" do
- let!(:past_meeting) { create(:structured_meeting, project:, start_time: 1.week.ago) }
+ let!(:past_meeting) { create(:meeting, project:, start_time: 1.week.ago) }
let!(:past_agenda_item) do
create(:meeting_agenda_item, meeting: past_meeting, work_package:, notes: "Public note!")
@@ -225,8 +225,8 @@ RSpec.describe "Open the Meetings tab",
end
context "when the work_package is already referenced in upcoming meetings" do
- let!(:first_meeting) { create(:structured_meeting, project:) }
- let!(:second_meeting) { create(:structured_meeting, project:) }
+ let!(:first_meeting) { create(:meeting, project:) }
+ let!(:second_meeting) { create(:meeting, project:) }
let!(:first_meeting_agenda_item_of_first_meeting) do
create(:meeting_agenda_item, meeting: first_meeting, work_package:, notes: "A very important note in first meeting!")
@@ -267,7 +267,7 @@ RSpec.describe "Open the Meetings tab",
end
context "when the work_package is referenced and has an outcome" do
- let!(:meeting) { create(:structured_meeting, project:) }
+ let!(:meeting) { create(:meeting, project:) }
let!(:meeting_agenda_item) do
create(:meeting_agenda_item, meeting:, work_package:, notes: "A very important note in first meeting!")
@@ -291,8 +291,8 @@ RSpec.describe "Open the Meetings tab",
end
context "when the work_package was already referenced in past meetings" do
- let!(:first_past_meeting) { create(:structured_meeting, project:, start_time: Date.yesterday - 11.hours) }
- let!(:second_past_meeting) { create(:structured_meeting, project:, start_time: Date.yesterday - 10.hours) }
+ let!(:first_past_meeting) { create(:meeting, project:, start_time: Date.yesterday - 11.hours) }
+ let!(:second_past_meeting) { create(:meeting, project:, start_time: Date.yesterday - 10.hours) }
let!(:first_meeting_agenda_item_of_first_past_meeting) do
create(:meeting_agenda_item, meeting: first_past_meeting, work_package:, notes: "A very important note in first meeting!")
@@ -347,15 +347,15 @@ RSpec.describe "Open the Meetings tab",
end
context "when open, upcoming meetings are visible for the user" do
- shared_let(:past_meeting) { create(:structured_meeting, project:, start_time: Date.yesterday - 10.hours) }
- shared_let(:first_upcoming_meeting) { create(:structured_meeting, project:) }
- shared_let(:second_upcoming_meeting) { create(:structured_meeting, project:) }
- shared_let(:closed_upcoming_meeting) { create(:structured_meeting, project:, state: :closed) }
+ shared_let(:past_meeting) { create(:meeting, project:, start_time: Date.yesterday - 10.hours) }
+ shared_let(:first_upcoming_meeting) { create(:meeting, project:) }
+ shared_let(:second_upcoming_meeting) { create(:meeting, project:) }
+ shared_let(:closed_upcoming_meeting) { create(:meeting, project:, state: :closed) }
shared_let(:ongoing_meeting) do
- create(:structured_meeting, title: "Ongoing", project:, start_time: 1.hour.ago, duration: 4.0)
+ create(:meeting, title: "Ongoing", project:, start_time: 1.hour.ago, duration: 4.0)
end
- let(:meeting_page) { Pages::StructuredMeeting::Show.new(first_upcoming_meeting) }
+ let(:meeting_page) { Pages::Meetings::Show.new(first_upcoming_meeting) }
it "enables the user to add the work package to multiple open, upcoming meetings" do
work_package_page.visit!
diff --git a/modules/meeting/spec/mailers/meeting_mailer_spec.rb b/modules/meeting/spec/mailers/meeting_mailer_spec.rb
index 7a5c058144b..e17ea369ceb 100644
--- a/modules/meeting/spec/mailers/meeting_mailer_spec.rb
+++ b/modules/meeting/spec/mailers/meeting_mailer_spec.rb
@@ -45,9 +45,6 @@ RSpec.describe MeetingMailer do
author:,
project:)
end
- let(:meeting_agenda) do
- create(:meeting_agenda, meeting:)
- end
let(:tokyo_offset) { "UTC#{ActiveSupport::TimeZone['Asia/Tokyo'].now.formatted_offset}" }
let(:berlin_offset) { "UTC#{ActiveSupport::TimeZone['Europe/Berlin'].now.formatted_offset}" }
diff --git a/modules/meeting/spec/models/meeting_acts_as_journalized_spec.rb b/modules/meeting/spec/models/meeting_acts_as_journalized_spec.rb
index 6f834a756e5..5d57eda6008 100644
--- a/modules/meeting/spec/models/meeting_acts_as_journalized_spec.rb
+++ b/modules/meeting/spec/models/meeting_acts_as_journalized_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -35,7 +36,7 @@ RSpec.describe Meeting do
let!(:meeting) do
User.execute_as current_user do
- create(:structured_meeting, author: user)
+ create(:meeting, author: user)
end
end
diff --git a/modules/meeting/spec/models/meeting_agenda_item_spec.rb b/modules/meeting/spec/models/meeting_agenda_item_spec.rb
index 00e6651aebc..78183c4cfeb 100644
--- a/modules/meeting/spec/models/meeting_agenda_item_spec.rb
+++ b/modules/meeting/spec/models/meeting_agenda_item_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -31,7 +32,7 @@ require_relative "../spec_helper"
RSpec.describe MeetingAgendaItem do
let(:meeting_attributes) { {} }
- let(:meeting) { build_stubbed(:structured_meeting, **meeting_attributes) }
+ let(:meeting) { build_stubbed(:meeting, **meeting_attributes) }
let(:attributes) { {} }
let(:meeting_agenda_item) { described_class.new(meeting:, **attributes) }
diff --git a/modules/meeting/spec/models/meeting_section_spec.rb b/modules/meeting/spec/models/meeting_section_spec.rb
index e34aaddd831..5c3cea18ee0 100644
--- a/modules/meeting/spec/models/meeting_section_spec.rb
+++ b/modules/meeting/spec/models/meeting_section_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -31,7 +32,7 @@ require_relative "../spec_helper"
RSpec.describe MeetingSection do
let(:meeting_attributes) { {} }
- let(:meeting) { build_stubbed(:structured_meeting, **meeting_attributes) }
+ let(:meeting) { build_stubbed(:meeting, **meeting_attributes) }
let(:attributes) { {} }
let(:meeting_section) { described_class.new(meeting:, **attributes) }
@@ -73,7 +74,7 @@ RSpec.describe MeetingSection do
describe "#agenda_items_sum_duration_in_minutes" do
subject { meeting_section.agenda_items_sum_duration_in_minutes }
- let(:meeting) { create(:structured_meeting, **meeting_attributes) }
+ let(:meeting) { create(:meeting, **meeting_attributes) }
let(:meeting_section) { create(:meeting_section, meeting:) }
context "when there are no agenda items" do
diff --git a/modules/meeting/spec/models/meeting_spec.rb b/modules/meeting/spec/models/meeting_spec.rb
index 8e45a28c4eb..c2ebc8d7b30 100644
--- a/modules/meeting/spec/models/meeting_spec.rb
+++ b/modules/meeting/spec/models/meeting_spec.rb
@@ -32,12 +32,9 @@ require_relative "../spec_helper"
RSpec.describe Meeting do
shared_let (:user1) { create(:user) }
shared_let (:user2) { create(:user) }
+
let(:project) { create(:project, members: project_members) }
let(:meeting) { create(:meeting, :author_participates, project:, author: user1) }
- let(:agenda) do
- meeting.create_agenda text: "Meeting Agenda text"
- meeting.reload_agenda # avoiding stale object errors
- end
let(:project_members) { {} }
let(:role) { create(:project_role, permissions: [:view_meetings]) }
@@ -152,22 +149,6 @@ RSpec.describe Meeting do
it { expect(meeting.watchers.collect(&:user)).to contain_exactly(user1, user2) }
end
- describe "#close_agenda_and_copy_to_minutes" do
- before do
- agenda # creating it
-
- meeting.close_agenda_and_copy_to_minutes!
- end
-
- it "creates a meeting with the agenda's text" do
- expect(meeting.minutes.text).to eq(meeting.agenda.text)
- end
-
- it "closes the agenda" do
- expect(meeting.agenda).to be_locked
- end
- end
-
describe "Timezones" do
shared_examples "uses that zone" do |zone|
it do
@@ -203,10 +184,6 @@ RSpec.describe Meeting do
it "uses the :view_meetings permission" do
expect(described_class.acts_as_watchable_permission).to eq(:view_meetings)
end
-
- it "uses the :view_meetings permission in STI classes" do
- expect(StructuredMeeting.acts_as_watchable_permission).to eq(:view_meetings)
- end
end
describe "duration" do
diff --git a/modules/meeting/spec/requests/api/v3/attachments/meeting_agenda_spec.rb b/modules/meeting/spec/requests/api/v3/attachments/meeting_agenda_spec.rb
deleted file mode 100644
index 32cce374d07..00000000000
--- a/modules/meeting/spec/requests/api/v3/attachments/meeting_agenda_spec.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-require "spec_helper"
-require "requests/api/v3/attachments/attachment_resource_shared_examples"
-
-RSpec.describe "meeting agenda attachments" do
- it_behaves_like "an APIv3 attachment resource" do
- let(:attachment_type) { :meeting_content }
-
- let(:create_permission) { :create_meetings }
- let(:read_permission) { :view_meetings }
- let(:update_permission) { :edit_meetings }
-
- let(:meeting_content) { create(:meeting_agenda, meeting:) }
- let(:meeting) { create(:meeting, project:) }
- end
-end
diff --git a/modules/meeting/spec/requests/meetings_index_spec.rb b/modules/meeting/spec/requests/meetings_index_spec.rb
index ed4786a3072..cfedbd2032a 100644
--- a/modules/meeting/spec/requests/meetings_index_spec.rb
+++ b/modules/meeting/spec/requests/meetings_index_spec.rb
@@ -37,7 +37,7 @@ RSpec.describe "Meeting index",
shared_let(:user) { create(:user, member_with_permissions: { project => %i[view_meetings] }) }
shared_let(:past) do
- create(:structured_meeting,
+ create(:meeting,
:author_participates,
title: "an earlier meeting",
start_time: DateTime.parse("2025-01-29T06:00:00Z"),
@@ -46,7 +46,7 @@ RSpec.describe "Meeting index",
end
shared_let(:today) do
- create(:structured_meeting,
+ create(:meeting,
:author_participates,
title: "meeting starting soon",
start_time: DateTime.parse("2025-01-29T10:00:00Z"),
@@ -55,7 +55,7 @@ RSpec.describe "Meeting index",
end
shared_let(:tomorrow) do
- create(:structured_meeting,
+ create(:meeting,
:author_participates,
title: "meeting starting tomorrow",
start_time: DateTime.parse("2025-01-30T10:00:00Z"),
@@ -64,7 +64,7 @@ RSpec.describe "Meeting index",
end
shared_let(:saturday) do
- create(:structured_meeting,
+ create(:meeting,
:author_participates,
title: "weekend meeting on saturday",
start_time: DateTime.parse("2025-02-01T10:00:00Z"),
@@ -73,7 +73,7 @@ RSpec.describe "Meeting index",
end
shared_let(:sunday) do
- create(:structured_meeting,
+ create(:meeting,
:author_participates,
title: "weekend meeting on sunday",
start_time: DateTime.parse("2025-02-02T10:00:00Z"),
@@ -82,7 +82,7 @@ RSpec.describe "Meeting index",
end
shared_let(:next_monday) do
- create(:structured_meeting,
+ create(:meeting,
:author_participates,
title: "meeting on next monday",
start_time: DateTime.parse("2025-02-03T10:00:00Z"),
@@ -91,7 +91,7 @@ RSpec.describe "Meeting index",
end
shared_let(:next_friday) do
- create(:structured_meeting,
+ create(:meeting,
:author_participates,
title: "meeting on next friday",
start_time: DateTime.parse("2025-02-07T10:00:00Z"),
diff --git a/modules/meeting/spec/requests/meetings_spec.rb b/modules/meeting/spec/requests/meetings_spec.rb
index 1fd3a5a4a15..f748a749818 100644
--- a/modules/meeting/spec/requests/meetings_spec.rb
+++ b/modules/meeting/spec/requests/meetings_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -34,7 +35,7 @@ RSpec.describe "Meeting requests",
type: :rails_request do
shared_let(:project) { create(:project, enabled_module_names: %i[meetings]) }
shared_let(:user) { create(:user, member_with_permissions: { project => %i[view_meetings create_meetings edit_meetings] }) }
- shared_let(:meeting) { create(:structured_meeting, project:, author: user) }
+ shared_let(:meeting) { create(:meeting, project:, author: user) }
before do
meeting.participants.delete_all
@@ -51,55 +52,6 @@ RSpec.describe "Meeting requests",
end
end
- describe "update with a new particpant" do
- let(:other_user) { create(:user, member_with_permissions: { project => %i[view_meetings] }) }
- let(:params) do
- {
- send_notifications:,
- title: "Updated meeting",
- meeting: {
- participants_attributes: [
- { user_id: user.id, invited: true },
- { user_id: other_user.id, invited: true }
- ]
- }
-
- }
- end
-
- subject do
- meeting.reload
- end
-
- context "with send_notifications" do
- let(:send_notifications) { "1" }
-
- it "sends an invitation mail to the invited users" do
- patch(project_meeting_path(project, meeting), params:)
-
- expect(subject.participants.count).to eq(2)
-
- perform_enqueued_jobs
-
- expect(ActionMailer::Base.deliveries.size).to eq(2)
- end
- end
-
- context "with send_notifications false" do
- let(:send_notifications) { "0" }
-
- it "sends an invitation mail to the invited users" do
- patch(project_meeting_path(project, meeting), params:)
-
- expect(subject.participants.count).to eq(2)
-
- perform_enqueued_jobs
-
- expect(ActionMailer::Base.deliveries.size).to eq(0)
- end
- end
- end
-
describe "copy" do
let(:base_params) do
{
@@ -107,7 +59,7 @@ RSpec.describe "Meeting requests",
project_id: project.id,
meeting: {
title: "Copied meeting",
- type: "StructuredMeeting"
+ type: "Meeting"
}
}
end
diff --git a/modules/meeting/spec/requests/recurring_meetings/recurring_meetings_end_series_spec.rb b/modules/meeting/spec/requests/recurring_meetings/recurring_meetings_end_series_spec.rb
index 206f0d2afe5..cc0775a0233 100644
--- a/modules/meeting/spec/requests/recurring_meetings/recurring_meetings_end_series_spec.rb
+++ b/modules/meeting/spec/requests/recurring_meetings/recurring_meetings_end_series_spec.rb
@@ -62,7 +62,7 @@ RSpec.describe "Recurring meetings complete template",
end
context "when past occurrence is already created" do
- let!(:meeting) { create(:structured_meeting, recurring_meeting:, start_time: recurring_meeting.start_time) }
+ let!(:meeting) { create(:meeting, recurring_meeting:, start_time: recurring_meeting.start_time) }
let!(:schedule) do
create :scheduled_meeting,
meeting:,
@@ -81,7 +81,7 @@ RSpec.describe "Recurring meetings complete template",
end
context "when start_time < current time" do
- let!(:meeting) { create(:structured_meeting, recurring_meeting:, start_time: recurring_meeting.start_time) }
+ let!(:meeting) { create(:meeting, recurring_meeting:, start_time: recurring_meeting.start_time) }
let!(:schedule) do
create :scheduled_meeting,
meeting:,
diff --git a/modules/meeting/spec/requests/recurring_meetings/recurring_meetings_show_spec.rb b/modules/meeting/spec/requests/recurring_meetings/recurring_meetings_show_spec.rb
index 10c351e0acb..6e7fc910753 100644
--- a/modules/meeting/spec/requests/recurring_meetings/recurring_meetings_show_spec.rb
+++ b/modules/meeting/spec/requests/recurring_meetings/recurring_meetings_show_spec.rb
@@ -94,7 +94,7 @@ RSpec.describe "Recurring meetings show",
end
describe "past quick filter" do
- let!(:past_instance) { create(:structured_meeting, recurring_meeting:, start_time: 1.day.ago + 10.hours) }
+ let!(:past_instance) { create(:meeting, recurring_meeting:, start_time: 1.day.ago + 10.hours) }
let!(:past_schedule) do
create :scheduled_meeting,
meeting: past_instance,
@@ -131,7 +131,7 @@ RSpec.describe "Recurring meetings show",
describe "upcoming tab" do
let!(:upcoming_open_meeting) do
- create(:structured_meeting, recurring_meeting:, start_time: Time.zone.today + 1.day + 10.hours, state: :open)
+ create(:meeting, recurring_meeting:, start_time: Time.zone.today + 1.day + 10.hours, state: :open)
end
let!(:open_meeting) do
create :scheduled_meeting,
@@ -179,7 +179,7 @@ RSpec.describe "Recurring meetings show",
end
let!(:ongoing_meeting) do
- create(:structured_meeting, recurring_meeting:, start_time: Time.zone.today + 10.hours, state: :open)
+ create(:meeting, recurring_meeting:, start_time: Time.zone.today + 10.hours, state: :open)
end
let!(:ongoing_schedule) do
create :scheduled_meeting,
@@ -210,7 +210,7 @@ RSpec.describe "Recurring meetings show",
describe "upcoming quick filter" do
context "with a rescheduled meeting" do
let!(:rescheduled_instance) do
- create :structured_meeting,
+ create :meeting,
recurring_meeting:,
start_time: Time.zone.today + 2.days + 10.hours
end
@@ -242,7 +242,7 @@ RSpec.describe "Recurring meetings show",
iterations: 2
end
let!(:first_instance) do
- create :structured_meeting,
+ create :meeting,
recurring_meeting:,
start_time: Time.zone.today + 2.days + 10.hours,
state: :open
@@ -254,7 +254,7 @@ RSpec.describe "Recurring meetings show",
start_time: Time.zone.today + 2.days + 10.hours
end
let!(:second_instance) do
- create :structured_meeting,
+ create :meeting,
recurring_meeting:,
start_time: Time.zone.today + 3.days + 10.hours,
state: :open
@@ -323,7 +323,7 @@ RSpec.describe "Recurring meetings show",
iterations: 5
end
let!(:ongoing_instance) do
- create :structured_meeting,
+ create :meeting,
recurring_meeting:,
start_time: Time.zone.today + 10.hours - 10.minutes
end
diff --git a/modules/meeting/spec/requests/recurring_meetings/recurring_meetings_template_completed_spec.rb b/modules/meeting/spec/requests/recurring_meetings/recurring_meetings_template_completed_spec.rb
index f01a2603d7a..2f3794e7ed5 100644
--- a/modules/meeting/spec/requests/recurring_meetings/recurring_meetings_template_completed_spec.rb
+++ b/modules/meeting/spec/requests/recurring_meetings/recurring_meetings_template_completed_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -81,7 +82,7 @@ RSpec.describe "Recurring meetings complete template",
end
context "when first occurrence is already created" do
- let!(:meeting) { create(:structured_meeting, recurring_meeting:, start_time: recurring_meeting.start_time) }
+ let!(:meeting) { create(:meeting, recurring_meeting:, start_time: recurring_meeting.start_time) }
let!(:schedule) do
create :scheduled_meeting,
meeting:,
diff --git a/modules/meeting/spec/routing/previews_routing_spec.rb b/modules/meeting/spec/routing/previews_routing_spec.rb
deleted file mode 100644
index 3c8fedad40e..00000000000
--- a/modules/meeting/spec/routing/previews_routing_spec.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-require "spec_helper"
-
-RSpec.describe "preview" do
- it "connects POST /meetings/:meeting_id/agenda/preview to meeting_agendas#preview" do
- expect(post("/meetings/1/agenda/preview")).to route_to(controller: "meeting_agendas",
- meeting_id: "1",
- action: "preview")
- end
-
- it "connects POST /meetings/:meeting_id/agenda/preview to meeting_minutes#preview" do
- expect(post("/meetings/1/minutes/preview")).to route_to(controller: "meeting_minutes",
- meeting_id: "1",
- action: "preview")
- end
-end
diff --git a/modules/meeting/spec/seeders/demo_data/meeting_agenda_items_seeder_spec.rb b/modules/meeting/spec/seeders/demo_data/meeting_agenda_items_seeder_spec.rb
index 85d788aebda..b6f67607449 100644
--- a/modules/meeting/spec/seeders/demo_data/meeting_agenda_items_seeder_spec.rb
+++ b/modules/meeting/spec/seeders/demo_data/meeting_agenda_items_seeder_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -34,7 +35,7 @@ RSpec.describe Meetings::DemoData::MeetingAgendaItemsSeeder do
shared_let(:alice) { create(:user, firstname: "Alice") }
shared_let(:bob) { create(:user, firstname: "Bob") }
- shared_let(:meeting) { create(:structured_meeting, title: "Weekly meeting") }
+ shared_let(:meeting) { create(:meeting, title: "Weekly meeting") }
shared_let(:work_package) { create(:work_package, subject: "Some important task") }
subject(:seeder) { described_class.new("_project", seed_data) }
diff --git a/modules/meeting/spec/services/meeting_contents/update_service_spec.rb b/modules/meeting/spec/services/meeting_contents/update_service_spec.rb
deleted file mode 100644
index 296e8a4a960..00000000000
--- a/modules/meeting/spec/services/meeting_contents/update_service_spec.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-require "spec_helper"
-require "services/base_services/behaves_like_update_service"
-
-RSpec.describe MeetingContents::UpdateService, type: :model do
- it_behaves_like "BaseServices update service" do
- let(:factory) { :meeting_agenda }
- end
-end
diff --git a/modules/meeting/spec/services/meetings/copy_service_integration_spec.rb b/modules/meeting/spec/services/meetings/copy_service_integration_spec.rb
index 00bddb71226..d71b3a9d7f5 100644
--- a/modules/meeting/spec/services/meetings/copy_service_integration_spec.rb
+++ b/modules/meeting/spec/services/meetings/copy_service_integration_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe Meetings::CopyService, "integration", type: :model do
shared_let(:user) do
create(:user, member_with_permissions: { project => %i(view_meetings create_meetings) })
end
- shared_let(:meeting) { create(:structured_meeting, project:, start_time: Time.parse("2013-03-27T15:35:00Z")) }
+ shared_let(:meeting) { create(:meeting, project:, start_time: Time.parse("2013-03-27T15:35:00Z")) }
let(:instance) { described_class.new(model: meeting, user:) }
let(:attributes) { {} }
diff --git a/modules/meeting/spec/services/meetings/update_service_integration_spec.rb b/modules/meeting/spec/services/meetings/update_service_integration_spec.rb
index 2197c9f486c..23d13048196 100644
--- a/modules/meeting/spec/services/meetings/update_service_integration_spec.rb
+++ b/modules/meeting/spec/services/meetings/update_service_integration_spec.rb
@@ -48,7 +48,7 @@ RSpec.describe Meetings::UpdateService, "integration", type: :model do
context "when meeting is in a series and scheduled to the future" do
shared_let(:recurring_meeting, refind: true) { create(:recurring_meeting, project:, frequency: "daily") }
shared_let(:meeting, refind: true) do
- create(:structured_meeting,
+ create(:meeting,
recurring_meeting:,
project:,
start_time: Time.zone.today + 2.days + 10.hours)
@@ -140,7 +140,7 @@ RSpec.describe Meetings::UpdateService, "integration", type: :model do
context "when previous schedule exists tomorrow at 10:00" do
shared_let(:previous_meeting) do
- create(:structured_meeting, recurring_meeting:, project:, start_time: Time.zone.tomorrow + 10.hours)
+ create(:meeting, recurring_meeting:, project:, start_time: Time.zone.tomorrow + 10.hours)
end
shared_let(:previous_schedule) do
create(:scheduled_meeting,
diff --git a/modules/meeting/spec/contracts/meeting_contents/update_contract_spec.rb b/modules/meeting/spec/services/principals/delete_job_integration_spec.rb
similarity index 63%
rename from modules/meeting/spec/contracts/meeting_contents/update_contract_spec.rb
rename to modules/meeting/spec/services/principals/delete_job_integration_spec.rb
index 0bfbaaa38fd..83e2911de42 100644
--- a/modules/meeting/spec/contracts/meeting_contents/update_contract_spec.rb
+++ b/modules/meeting/spec/services/principals/delete_job_integration_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -28,29 +29,28 @@
#++
require "spec_helper"
-require "contracts/shared/model_contract_shared_context"
-RSpec.describe MeetingContents::UpdateContract do
- include_context "ModelContract shared context"
- let(:agenda) { build_stubbed(:meeting_agenda) }
- let(:current_user) { build_stubbed(:user) }
- let(:contract) { described_class.new(agenda, current_user) }
+RSpec.describe Principals::DeleteJob, "Meetings", type: :model do
+ subject(:job) { described_class.perform_now(principal) }
- context "when not editable" do
- before do
- allow(agenda).to receive(:editable?).and_return false
- end
-
- it_behaves_like "contract is invalid", base: :error_readonly
+ shared_let(:deleted_user) do
+ create(:deleted_user)
+ end
+ let(:principal) do
+ create(:user)
end
- context "when editable" do
- before do
- allow(agenda).to receive(:editable?).and_return true
+ context "with a meeting" do
+ let!(:meeting) { create(:meeting, author: principal) }
+ let!(:meeting_agenda_item) { create(:meeting_agenda_item, presenter: principal) }
+ let!(:meeting_outcome) { create(:meeting_outcome, meeting_agenda_item:, author: principal) }
+
+ it "rewrites the references" do
+ job
+
+ expect(meeting.reload.author).to eq deleted_user
+ expect(meeting_agenda_item.reload.presenter).to eq deleted_user
+ expect(meeting_outcome.reload.author).to eq deleted_user
end
-
- it_behaves_like "contract is valid"
end
-
- include_examples "contract reuses the model errors"
end
diff --git a/modules/meeting/spec/services/principals/replace_references_service_call_integration_spec.rb b/modules/meeting/spec/services/principals/replace_references_service_call_integration_spec.rb
index a3156bf03e5..456596b5dbd 100644
--- a/modules/meeting/spec/services/principals/replace_references_service_call_integration_spec.rb
+++ b/modules/meeting/spec/services/principals/replace_references_service_call_integration_spec.rb
@@ -53,6 +53,12 @@ RSpec.describe Principals::ReplaceReferencesService, "#call", type: :model do
:presenter_id
end
+ context "with MeetingOutcome" do
+ it_behaves_like "rewritten record",
+ :meeting_outcome,
+ :author_id
+ end
+
context "with Journal::MeetingAgendaItemJournal" do
it_behaves_like "rewritten record",
:journal_meeting_agenda_item_journal,
diff --git a/modules/meeting/spec/services/recurring_meetings/create_service_integration_spec.rb b/modules/meeting/spec/services/recurring_meetings/create_service_integration_spec.rb
index 714c8aea39a..d9707c8db01 100644
--- a/modules/meeting/spec/services/recurring_meetings/create_service_integration_spec.rb
+++ b/modules/meeting/spec/services/recurring_meetings/create_service_integration_spec.rb
@@ -47,7 +47,7 @@ RSpec.describe RecurringMeetings::CreateService, "integration", type: :model do
expect(service_result).to be_success
expect(series).to be_persisted
- expect(series.template).to be_a(StructuredMeeting)
+ expect(series.template).to be_a(Meeting)
expect(series.template).to be_template
expect(series.meetings.count).to eq(1)
@@ -84,10 +84,10 @@ RSpec.describe RecurringMeetings::CreateService, "integration", type: :model do
it_behaves_like "creates the series"
context "when the template cannot be saved" do
- let(:template) { StructuredMeeting.new }
+ let(:template) { Meeting.new }
before do
- allow(StructuredMeeting).to receive(:new).and_return(template)
+ allow(Meeting).to receive(:new).and_return(template)
allow(template).to receive(:save).and_return(false)
end
diff --git a/modules/meeting/spec/support/pages/structured_meeting/history.rb b/modules/meeting/spec/support/pages/meetings/history.rb
similarity index 98%
rename from modules/meeting/spec/support/pages/structured_meeting/history.rb
rename to modules/meeting/spec/support/pages/meetings/history.rb
index c47ceff18c6..01aafdb3d27 100644
--- a/modules/meeting/spec/support/pages/structured_meeting/history.rb
+++ b/modules/meeting/spec/support/pages/meetings/history.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -27,7 +28,7 @@
# See COPYRIGHT and LICENSE files for more details.
#++
-module Pages::StructuredMeeting
+module Pages::Meeting
class History < ::Pages::Page
def initialize(meeting)
super()
diff --git a/modules/meeting/spec/support/pages/meetings/index.rb b/modules/meeting/spec/support/pages/meetings/index.rb
index ecd4830e110..e737a97258c 100644
--- a/modules/meeting/spec/support/pages/meetings/index.rb
+++ b/modules/meeting/spec/support/pages/meetings/index.rb
@@ -28,8 +28,6 @@
# See COPYRIGHT and LICENSE files for more details.
#++
-require_relative "new"
-
module Pages::Meetings
class Index < Pages::Page
include Components::Common::Filters
@@ -43,13 +41,6 @@ module Pages::Meetings
self.project = project
end
- def click_create_new
- click_on("add-meeting-button")
- click_on("Classic")
-
- New.new(project)
- end
-
def set_title(text)
fill_in "Title", with: text
end
@@ -88,16 +79,7 @@ module Pages::Meetings
def click_create
click_on "Create meeting"
-
wait_for_network_idle
-
- meeting = Meeting.last
-
- if meeting
- Pages::Meetings::Show.new(meeting)
- else
- self
- end
end
def expect_no_main_menu
@@ -115,7 +97,7 @@ module Pages::Meetings
def expect_create_new_types
click_on("add-meeting-button")
- expect(page).to have_link("Classic")
+ expect(page).to have_link("Recurring")
expect(page).to have_link("One-time")
end
diff --git a/modules/meeting/spec/support/pages/structured_meeting/mobile/show.rb b/modules/meeting/spec/support/pages/meetings/mobile/show.rb
similarity index 95%
rename from modules/meeting/spec/support/pages/structured_meeting/mobile/show.rb
rename to modules/meeting/spec/support/pages/meetings/mobile/show.rb
index a149a2d0a1a..0b81b0f9323 100644
--- a/modules/meeting/spec/support/pages/structured_meeting/mobile/show.rb
+++ b/modules/meeting/spec/support/pages/meetings/mobile/show.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -29,8 +30,8 @@
require_relative "../show"
-module Pages::StructuredMeeting::Mobile
- class Show < ::Pages::StructuredMeeting::Show
+module Pages::Meetings::Mobile
+ class Show < ::Pages::Meetings::Show
def expect_participants(count: 1)
within(meeting_details_container) do
expect(page).to have_text(Meeting.human_attribute_name(:participant, count:))
diff --git a/modules/meeting/spec/support/pages/meetings/new.rb b/modules/meeting/spec/support/pages/meetings/new.rb
deleted file mode 100644
index fed8cb47804..00000000000
--- a/modules/meeting/spec/support/pages/meetings/new.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-# frozen_string_literal: true
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-require_relative "base"
-require_relative "show"
-
-module Pages::Meetings
- class New < Base
- include Components::Autocompleter::NgSelectAutocompleteHelpers
-
- def expect_no_main_menu
- expect(page).to have_no_css "#main-menu"
- end
-
- def click_create
- click_on "Create"
-
- wait_for_network_idle
-
- meeting = Meeting.last
-
- if meeting
- Pages::Meetings::Show.new(meeting)
- else
- self
- end
- end
-
- def set_type(type)
- choose type, match: :first
- end
-
- def set_title(text)
- fill_in "Title", with: text
- end
-
- def expect_project_dropdown
- find "[data-test-selector='project_id']"
- end
-
- def set_project(project)
- select_autocomplete find("[data-test-selector='project_id']"),
- query: project.name,
- results_selector: "body"
- end
-
- def set_start_date(date)
- if using_cuprite?
- fill_in "Start date", with: date
- else
- fill_in "Start date", with: date, fill_options: { clear: :backspace }
- end
- end
-
- def set_start_time(time)
- input = page.find_by_id("meeting-form-start-time")
- page.execute_script("arguments[0].value = arguments[1]", input.native, time)
- end
-
- def set_duration(duration)
- fill_in "Duration", with: duration
- end
-
- def invite(user)
- check "#{user.name} invited"
- end
-
- def path
- polymorphic_path([:new, project, :meeting])
- end
- end
-end
diff --git a/modules/meeting/spec/support/pages/meetings/show.rb b/modules/meeting/spec/support/pages/meetings/show.rb
index 1c50f32f3a8..3e0239e2ac5 100644
--- a/modules/meeting/spec/support/pages/meetings/show.rb
+++ b/modules/meeting/spec/support/pages/meetings/show.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -31,10 +32,13 @@ require_relative "base"
module Pages::Meetings
class Show < Base
+ include ::Components::Autocompleter::NgSelectAutocompleteHelpers
attr_accessor :meeting
def initialize(meeting)
self.meeting = meeting
+
+ super(meeting.project)
end
def expect_no_invited
@@ -83,18 +87,359 @@ module Pages::Meetings
end
end
- def meeting_details_container
- find(".meeting.details")
- end
-
- def click_edit
- within ".meeting--main-toolbar .toolbar-items" do
- click_on "Edit"
- end
- end
-
def path
project_meeting_path(meeting.project, meeting)
end
+
+ def expect_empty
+ expect(page).to have_no_css('[id^="meeting-agenda-items-item-component"]')
+ end
+
+ def trigger_dropdown_menu_item(name)
+ click_link_or_button "op-meetings-header-action-trigger"
+ click_link_or_button name
+ end
+
+ def trigger_change_poll
+ script = <<~JS
+ var target = document.querySelector('#content-wrapper');
+ var controller = window.Stimulus.getControllerForElementAndIdentifier(target, 'poll-for-changes')
+ controller.triggerTurboStream();
+ JS
+
+ page.execute_script(script)
+ end
+
+ def add_agenda_item(type: MeetingAgendaItem, save: true, &)
+ page.within("#meeting-agenda-items-new-button-component") do
+ click_on I18n.t(:button_add)
+ click_on type.model_name.human
+ end
+
+ in_agenda_form do
+ yield
+ click_on("Save") if save
+ end
+ end
+
+ def expect_modal(...)
+ expect(page).to have_modal(...)
+ end
+
+ def expect_no_add_form
+ expect(page).not_to have_test_selector("#meeting-agenda-items-form-component")
+ end
+
+ def add_agenda_item_to_section(section:, type: MeetingAgendaItem, save: true, &)
+ select_section_action(section, type.model_name.human)
+
+ within("#meeting-sections-show-component-#{section.id}") do
+ in_agenda_form do
+ yield
+ click_on("Save") if save
+ end
+ end
+ end
+
+ def cancel_edit_form(item)
+ in_edit_form(item) do
+ click_on I18n.t(:button_cancel)
+ expect(page).to have_no_link I18n.t(:button_cancel)
+ end
+ end
+
+ def in_edit_form(item, &)
+ page.within("#meeting-agenda-items-item-component-#{item.id}", &)
+ end
+
+ def in_agenda_form(&)
+ page.within("#meeting-agenda-items-form-component", &)
+ end
+
+ def assert_agenda_order!(*titles)
+ retry_block do
+ found = page.all(:test_id, "op-meeting-agenda-title").map(&:text)
+ raise "Expected order of agenda items #{titles.inspect}, but found #{found.inspect}" if titles != found
+ end
+ end
+
+ def remove_agenda_item(item)
+ accept_confirm(I18n.t("text_are_you_sure")) do
+ action = item.work_package ? I18n.t(:label_agenda_item_remove) : I18n.t(:button_delete)
+ select_action(item, action)
+ end
+
+ title = item.work_package ? item.work_package.subject : item.title
+ expect_no_agenda_item(title:)
+ end
+
+ def expect_agenda_item(title:)
+ expect(page).to have_test_selector("op-meeting-agenda-title", text: title)
+ end
+
+ def expect_agenda_item_in_section(title:, section:)
+ within("#meeting-sections-show-component-#{section.id}") do
+ expect_agenda_item(title:)
+ end
+ end
+
+ def expect_agenda_link(item)
+ if item.is_a?(WorkPackage)
+ expect(page).to have_css("[id^='meeting-agenda-items-item-component-']", text: item.subject)
+ else
+ expect(page).to have_css("#meeting-agenda-items-item-component-#{item.id}", text: item.work_package.subject)
+ end
+ end
+
+ def expect_agenda_author(name)
+ expect(page).to have_test_selector("op-principal", text: name)
+ end
+
+ def expect_undisclosed_agenda_link(item)
+ expect(page).to have_css("#meeting-agenda-items-item-component-#{item.id}",
+ text: I18n.t(:label_agenda_item_undisclosed_wp, id: item.work_package_id))
+ end
+
+ def expect_no_agenda_item(title:)
+ expect(page).not_to have_test_selector("op-meeting-agenda-title", text: title)
+ end
+
+ def expect_agenda_action_menu(item)
+ expect(page)
+ .to have_css("#meeting-agenda-items-item-component-#{item.id} #{test_selector('op-meeting-agenda-actions')}")
+ end
+
+ def expect_no_agenda_action_menu(item)
+ expect(page)
+ .to have_no_css("#meeting-agenda-items-item-component-#{item.id} #{test_selector('op-meeting-agenda-actions')}")
+ end
+
+ def select_action(item, action)
+ open_menu(item) do
+ click_on action
+ end
+ end
+
+ def open_menu(item, &)
+ retry_block do
+ page.within("#meeting-agenda-items-item-component-#{item.id}") do
+ page.find_test_selector("op-meeting-agenda-actions").click
+ end
+ page.find(".Overlay")
+ end
+
+ page.within(".Overlay", &)
+ end
+
+ def select_outcome_action(action)
+ retry_block do
+ page.find_test_selector("op-meeting-outcome-actions").click
+ page.find(".Overlay")
+ end
+
+ page.within(".Overlay") do
+ click_on action
+ end
+ end
+
+ def expect_no_outcome_actions
+ expect(page).to have_no_css("op-meeting-outcome-actions")
+ end
+
+ def expect_no_outcome_action(item)
+ retry_block do
+ page.within("#meeting-agenda-items-item-component-#{item.id}") do
+ page.find_test_selector("op-meeting-agenda-actions").trigger("click")
+ end
+ page.find(".Overlay")
+ end
+
+ page.within(".Overlay") do
+ expect(page).to have_no_text("Add outcome")
+ end
+ end
+
+ def select_section_action(section, action)
+ retry_block do
+ click_on_section_menu(section)
+ page.find(".Overlay")
+ end
+
+ page.within(".Overlay") do
+ click_on action
+ end
+ end
+
+ def click_on_section_menu(section)
+ page.within_test_selector("meeting-section-header-container-#{section.id}") do
+ page.find_test_selector("meeting-section-action-menu").click
+ end
+ end
+
+ def in_outcome_component(item, &)
+ page.within("#meeting-agenda-items-outcomes-base-component-#{item.id}", &)
+ end
+
+ def add_outcome(item, &)
+ page.within("#meeting-agenda-items-outcomes-base-component-#{item.id}") do
+ click_link_or_button "Outcome"
+ end
+ expect_outcome_form(item)
+ page.within("#meeting-agenda-items-outcomes-input-component-#{item.id}", &)
+ end
+
+ def add_outcome_from_menu(item, &)
+ select_action item, "Add outcome"
+ expect_outcome_form(item)
+ page.within("#meeting-agenda-items-outcomes-input-component-#{item.id}", &)
+ end
+
+ def expect_outcome_form(item)
+ expect(page)
+ .to have_css("#meeting-agenda-items-outcomes-input-component-#{item.id}")
+ end
+
+ def expect_outcome(text)
+ expect(page).to have_css("#meeting-agenda-items-outcomes-show-notes-component", text:)
+ end
+
+ def expect_no_outcome(text)
+ expect(page).to have_no_css("#meeting-agenda-items-outcomes-show-notes-component", text:)
+ end
+
+ def expect_no_outcome_button
+ expect(page).to have_no_css("op-meeting-outcome--button")
+ end
+
+ def edit_agenda_item(item, &)
+ select_action item, "Edit"
+ expect_item_edit_form(item)
+ page.within("#meeting-agenda-items-form-component-#{item.id}", &)
+ end
+
+ def expect_item_edit_form(item, visible: true)
+ expect(page)
+ .to have_conditional_selector(
+ visible,
+ "#meeting-agenda-items-form-component-#{item.id}"
+ )
+ end
+
+ def expect_item_edit_title(item, value)
+ page.within("#meeting-agenda-items-form-component-#{item.id}") do
+ find_field("Title", with: value)
+ end
+ end
+
+ def expect_item_edit_field_error(item, text)
+ page.within("#meeting-agenda-items-form-component-#{item.id}") do
+ expect(page).to have_css(".FormControl-inlineValidation", text:)
+ end
+ end
+
+ def clear_item_edit_work_package_title
+ ng_select_clear page.find(".op-meeting-agenda-item-form--title")
+ expect(page).to have_css(".ng-input ", value: nil)
+ end
+
+ def open_participant_form
+ page.find_test_selector("manage-participants-button").click
+ expect_modal("Participants")
+ end
+
+ def in_participant_form(&)
+ page.within_modal("Participants", &)
+ end
+
+ def expect_participant(participant, invited: false, attended: false, editable: true)
+ expect(page).to have_text(participant.name)
+ expect(page).to have_field(id: "checkbox_invited_#{participant.id}", checked: invited, disabled: !editable)
+ expect(page).to have_field(id: "checkbox_attended_#{participant.id}", checked: attended, disabled: !editable)
+ end
+
+ def expect_participant_invited(participant, invited: true)
+ expect(page).to have_text(participant.name)
+ expect(page).to have_field(id: "checkbox_invited_#{participant.id}", checked: invited)
+ end
+
+ def invite_participant(participant)
+ id = "checkbox_invited_#{participant.id}"
+ retry_block do
+ check(id:)
+ raise "Expected #{participant.id} to be invited now" unless page.has_checked_field?(id:)
+ end
+ end
+
+ def expect_available_participants(count:)
+ expect(page).to have_link(class: "op-principal--name", count:)
+ end
+
+ def close_meeting
+ retry_block do
+ click_on("Open")
+ page.find(".Overlay")
+ end
+
+ page.within(".Overlay") do
+ click_on("Closed")
+ end
+ expect(page).to have_link("Reopen meeting")
+ end
+
+ def reopen_meeting
+ click_on("Reopen meeting")
+ expect(page).to have_link("Start meeting")
+ end
+
+ def close_dialog
+ click_on(class: "Overlay-closeButton")
+ end
+
+ def meeting_details_container
+ find_by_id("meetings-side-panel-details-component")
+ end
+
+ def in_latest_section_form(&)
+ page.within(all(".op-meeting-section-container").last, &)
+ end
+
+ def add_section(&)
+ retry_block do
+ page.within("#meeting-agenda-items-new-button-component") do
+ click_on I18n.t(:button_add)
+ click_on "Section"
+ # wait for the disabled button, indicating the turbo streams are applied
+ expect(page).to have_css("#meeting-agenda-items-new-button-component button[disabled='disabled']")
+ end
+ end
+
+ in_latest_section_form(&)
+ end
+
+ def expect_section(title:)
+ expect(page).to have_css(".op-meeting-section-container", text: title)
+ end
+
+ def expect_no_section(title:)
+ expect(page).to have_no_css(".op-meeting-section-container", text: title)
+ end
+
+ def expect_section_duration(section:, duration_text:)
+ page.within_test_selector("meeting-section-header-container-#{section.id}") do
+ expect(page).to have_text(duration_text)
+ end
+ end
+
+ def edit_section(section, &)
+ select_section_action(section, "Edit")
+
+ page.within_test_selector("meeting-section-header-container-#{section.id}", &)
+ end
+
+ def remove_section(section)
+ accept_confirm do
+ select_section_action(section, "Delete")
+ end
+ end
end
end
diff --git a/modules/meeting/spec/support/pages/structured_meeting/show.rb b/modules/meeting/spec/support/pages/structured_meeting/show.rb
deleted file mode 100644
index dbbbe0ef758..00000000000
--- a/modules/meeting/spec/support/pages/structured_meeting/show.rb
+++ /dev/null
@@ -1,388 +0,0 @@
-# frozen_string_literal: true
-
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-require_relative "../meetings/show"
-
-module Pages::StructuredMeeting
- class Show < ::Pages::Meetings::Show
- include ::Components::Autocompleter::NgSelectAutocompleteHelpers
-
- def expect_empty
- expect(page).to have_no_css('[id^="meeting-agenda-items-item-component"]')
- end
-
- def trigger_dropdown_menu_item(name)
- click_link_or_button "op-meetings-header-action-trigger"
- click_link_or_button name
- end
-
- def trigger_change_poll
- script = <<~JS
- var target = document.querySelector('#content-wrapper');
- var controller = window.Stimulus.getControllerForElementAndIdentifier(target, 'poll-for-changes')
- controller.triggerTurboStream();
- JS
-
- page.execute_script(script)
- end
-
- def add_agenda_item(type: MeetingAgendaItem, save: true, &)
- page.within("#meeting-agenda-items-new-button-component") do
- click_on I18n.t(:button_add)
- click_on type.model_name.human
- end
-
- in_agenda_form do
- yield
- click_on("Save") if save
- end
- end
-
- def expect_modal(...)
- expect(page).to have_modal(...)
- end
-
- def expect_no_add_form
- expect(page).not_to have_test_selector("#meeting-agenda-items-form-component")
- end
-
- def add_agenda_item_to_section(section:, type: MeetingAgendaItem, save: true, &)
- select_section_action(section, type.model_name.human)
-
- within("#meeting-sections-show-component-#{section.id}") do
- in_agenda_form do
- yield
- click_on("Save") if save
- end
- end
- end
-
- def cancel_edit_form(item)
- in_edit_form(item) do
- click_on I18n.t(:button_cancel)
- expect(page).to have_no_link I18n.t(:button_cancel)
- end
- end
-
- def in_edit_form(item, &)
- page.within("#meeting-agenda-items-item-component-#{item.id}", &)
- end
-
- def in_agenda_form(&)
- page.within("#meeting-agenda-items-form-component", &)
- end
-
- def assert_agenda_order!(*titles)
- retry_block do
- found = page.all(:test_id, "op-meeting-agenda-title").map(&:text)
- raise "Expected order of agenda items #{titles.inspect}, but found #{found.inspect}" if titles != found
- end
- end
-
- def remove_agenda_item(item)
- accept_confirm(I18n.t("text_are_you_sure")) do
- action = item.work_package ? I18n.t(:label_agenda_item_remove) : I18n.t(:button_delete)
- select_action(item, action)
- end
-
- title = item.work_package ? item.work_package.subject : item.title
- expect_no_agenda_item(title:)
- end
-
- def expect_agenda_item(title:)
- expect(page).to have_test_selector("op-meeting-agenda-title", text: title)
- end
-
- def expect_agenda_item_in_section(title:, section:)
- within("#meeting-sections-show-component-#{section.id}") do
- expect_agenda_item(title:)
- end
- end
-
- def expect_agenda_link(item)
- if item.is_a?(WorkPackage)
- expect(page).to have_css("[id^='meeting-agenda-items-item-component-']", text: item.subject)
- else
- expect(page).to have_css("#meeting-agenda-items-item-component-#{item.id}", text: item.work_package.subject)
- end
- end
-
- def expect_agenda_author(name)
- expect(page).to have_test_selector("op-principal", text: name)
- end
-
- def expect_undisclosed_agenda_link(item)
- expect(page).to have_css("#meeting-agenda-items-item-component-#{item.id}",
- text: I18n.t(:label_agenda_item_undisclosed_wp, id: item.work_package_id))
- end
-
- def expect_no_agenda_item(title:)
- expect(page).not_to have_test_selector("op-meeting-agenda-title", text: title)
- end
-
- def expect_agenda_action_menu(item)
- expect(page)
- .to have_css("#meeting-agenda-items-item-component-#{item.id} #{test_selector('op-meeting-agenda-actions')}")
- end
-
- def expect_no_agenda_action_menu(item)
- expect(page)
- .to have_no_css("#meeting-agenda-items-item-component-#{item.id} #{test_selector('op-meeting-agenda-actions')}")
- end
-
- def select_action(item, action)
- open_menu(item) do
- click_on action
- end
- end
-
- def open_menu(item, &)
- retry_block do
- page.within("#meeting-agenda-items-item-component-#{item.id}") do
- page.find_test_selector("op-meeting-agenda-actions").click
- end
- page.find(".Overlay")
- end
-
- page.within(".Overlay", &)
- end
-
- def select_outcome_action(action)
- retry_block do
- page.find_test_selector("op-meeting-outcome-actions").click
- page.find(".Overlay")
- end
-
- page.within(".Overlay") do
- click_on action
- end
- end
-
- def expect_no_outcome_actions
- expect(page).to have_no_css("op-meeting-outcome-actions")
- end
-
- def expect_no_outcome_action(item)
- retry_block do
- page.within("#meeting-agenda-items-item-component-#{item.id}") do
- page.find_test_selector("op-meeting-agenda-actions").trigger("click")
- end
- page.find(".Overlay")
- end
-
- page.within(".Overlay") do
- expect(page).to have_no_text("Add outcome")
- end
- end
-
- def select_section_action(section, action)
- retry_block do
- click_on_section_menu(section)
- page.find(".Overlay")
- end
-
- page.within(".Overlay") do
- click_on action
- end
- end
-
- def click_on_section_menu(section)
- page.within_test_selector("meeting-section-header-container-#{section.id}") do
- page.find_test_selector("meeting-section-action-menu").click
- end
- end
-
- def in_outcome_component(item, &)
- page.within("#meeting-agenda-items-outcomes-base-component-#{item.id}", &)
- end
-
- def add_outcome(item, &)
- page.within("#meeting-agenda-items-outcomes-base-component-#{item.id}") do
- click_link_or_button "Outcome"
- end
- expect_outcome_form(item)
- page.within("#meeting-agenda-items-outcomes-input-component-#{item.id}", &)
- end
-
- def add_outcome_from_menu(item, &)
- select_action item, "Add outcome"
- expect_outcome_form(item)
- page.within("#meeting-agenda-items-outcomes-input-component-#{item.id}", &)
- end
-
- def expect_outcome_form(item)
- expect(page)
- .to have_css("#meeting-agenda-items-outcomes-input-component-#{item.id}")
- end
-
- def expect_outcome(text)
- expect(page).to have_css("#meeting-agenda-items-outcomes-show-notes-component", text:)
- end
-
- def expect_no_outcome(text)
- expect(page).to have_no_css("#meeting-agenda-items-outcomes-show-notes-component", text:)
- end
-
- def expect_no_outcome_button
- expect(page).to have_no_css("op-meeting-outcome--button")
- end
-
- def edit_agenda_item(item, &)
- select_action item, "Edit"
- expect_item_edit_form(item)
- page.within("#meeting-agenda-items-form-component-#{item.id}", &)
- end
-
- def expect_item_edit_form(item, visible: true)
- expect(page)
- .to have_conditional_selector(
- visible,
- "#meeting-agenda-items-form-component-#{item.id}"
- )
- end
-
- def expect_item_edit_title(item, value)
- page.within("#meeting-agenda-items-form-component-#{item.id}") do
- find_field("Title", with: value)
- end
- end
-
- def expect_item_edit_field_error(item, text)
- page.within("#meeting-agenda-items-form-component-#{item.id}") do
- expect(page).to have_css(".FormControl-inlineValidation", text:)
- end
- end
-
- def clear_item_edit_work_package_title
- ng_select_clear page.find(".op-meeting-agenda-item-form--title")
- expect(page).to have_css(".ng-input ", value: nil)
- end
-
- def open_participant_form
- page.find_test_selector("manage-participants-button").click
- expect_modal("Participants")
- end
-
- def in_participant_form(&)
- page.within_modal("Participants", &)
- end
-
- def expect_participant(participant, invited: false, attended: false, editable: true)
- expect(page).to have_text(participant.name)
- expect(page).to have_field(id: "checkbox_invited_#{participant.id}", checked: invited, disabled: !editable)
- expect(page).to have_field(id: "checkbox_attended_#{participant.id}", checked: attended, disabled: !editable)
- end
-
- def expect_participant_invited(participant, invited: true)
- expect(page).to have_text(participant.name)
- expect(page).to have_field(id: "checkbox_invited_#{participant.id}", checked: invited)
- end
-
- def invite_participant(participant)
- id = "checkbox_invited_#{participant.id}"
- retry_block do
- check(id:)
- raise "Expected #{participant.id} to be invited now" unless page.has_checked_field?(id:)
- end
- end
-
- def expect_available_participants(count:)
- expect(page).to have_link(class: "op-principal--name", count:)
- end
-
- def close_meeting
- retry_block do
- click_on("Open")
- page.find(".Overlay")
- end
-
- page.within(".Overlay") do
- click_on("Closed")
- end
- expect(page).to have_link("Reopen meeting")
- end
-
- def reopen_meeting
- click_on("Reopen meeting")
- expect(page).to have_link("Start meeting")
- end
-
- def close_dialog
- click_on(class: "Overlay-closeButton")
- end
-
- def meeting_details_container
- find_by_id("meetings-side-panel-details-component")
- end
-
- def in_latest_section_form(&)
- page.within(all(".op-meeting-section-container").last, &)
- end
-
- def add_section(&)
- retry_block do
- page.within("#meeting-agenda-items-new-button-component") do
- click_on I18n.t(:button_add)
- click_on "Section"
- # wait for the disabled button, indicating the turbo streams are applied
- expect(page).to have_css("#meeting-agenda-items-new-button-component button[disabled='disabled']")
- end
- end
-
- in_latest_section_form(&)
- end
-
- def expect_section(title:)
- expect(page).to have_css(".op-meeting-section-container", text: title)
- end
-
- def expect_no_section(title:)
- expect(page).to have_no_css(".op-meeting-section-container", text: title)
- end
-
- def expect_section_duration(section:, duration_text:)
- page.within_test_selector("meeting-section-header-container-#{section.id}") do
- expect(page).to have_text(duration_text)
- end
- end
-
- def edit_section(section, &)
- select_section_action(section, "Edit")
-
- page.within_test_selector("meeting-section-header-container-#{section.id}", &)
- end
-
- def remove_section(section)
- accept_confirm do
- select_section_action(section, "Delete")
- end
- end
- end
-end
diff --git a/modules/meeting/spec/workers/recurring_meetings/init_next_occurrence_job_spec.rb b/modules/meeting/spec/workers/recurring_meetings/init_next_occurrence_job_spec.rb
index 698b77c1668..59a08813cae 100644
--- a/modules/meeting/spec/workers/recurring_meetings/init_next_occurrence_job_spec.rb
+++ b/modules/meeting/spec/workers/recurring_meetings/init_next_occurrence_job_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -46,7 +47,7 @@ RSpec.describe RecurringMeetings::InitNextOccurrenceJob, type: :model do
subject { described_class.perform_now(series, scheduled_time) }
it "schedules the first occurrence" do
- expect { subject }.to change(StructuredMeeting, :count).by(1)
+ expect { subject }.to change(Meeting, :count).by(1)
expect(subject).to be_success
created_meeting = subject.result
@@ -62,7 +63,7 @@ RSpec.describe RecurringMeetings::InitNextOccurrenceJob, type: :model do
end
it "does not instantiate anything, but schedules the next job" do
- expect { subject }.not_to change(StructuredMeeting, :count)
+ expect { subject }.not_to change(Meeting, :count)
expect(subject).to be_nil
expect(described_class)
@@ -73,7 +74,7 @@ RSpec.describe RecurringMeetings::InitNextOccurrenceJob, type: :model do
context "when next occurrence is already instantiated" do
let!(:instance) do
- create(:structured_meeting,
+ create(:meeting,
recurring_meeting: series,
start_time: Time.zone.tomorrow + 10.hours)
end
@@ -88,7 +89,7 @@ RSpec.describe RecurringMeetings::InitNextOccurrenceJob, type: :model do
let(:next_occurrence) { Time.zone.tomorrow + 1.day + 10.hours }
it "does not instantiate anything, but schedules the next job" do
- expect { subject }.not_to change(StructuredMeeting, :count)
+ expect { subject }.not_to change(Meeting, :count)
expect(subject).to be_nil
expect(described_class)
@@ -99,7 +100,7 @@ RSpec.describe RecurringMeetings::InitNextOccurrenceJob, type: :model do
context "when next occurrence is already instantiated, and moved" do
let!(:instance) do
- create(:structured_meeting,
+ create(:meeting,
recurring_meeting: series,
start_time: Time.zone.tomorrow + 1.day + 10.hours)
end
@@ -114,7 +115,7 @@ RSpec.describe RecurringMeetings::InitNextOccurrenceJob, type: :model do
let(:next_occurrence) { Time.zone.tomorrow + 1.day + 10.hours }
it "does not instantiate anything, but schedules the next one" do
- expect { subject }.not_to change(StructuredMeeting, :count)
+ expect { subject }.not_to change(Meeting, :count)
expect(subject).to be_nil
expect(described_class)
.to have_been_enqueued.with(series, next_occurrence)
@@ -124,7 +125,7 @@ RSpec.describe RecurringMeetings::InitNextOccurrenceJob, type: :model do
context "when later occurrence is already instantiated" do
let!(:instance) do
- create(:structured_meeting,
+ create(:meeting,
recurring_meeting: series,
start_time: Time.zone.tomorrow + 1.day + 10.hours)
end
@@ -139,7 +140,7 @@ RSpec.describe RecurringMeetings::InitNextOccurrenceJob, type: :model do
let(:next_occurrence) { Time.zone.tomorrow + 1.day + 10.hours }
it "schedules the one for tomorrow" do
- expect { subject }.to change(StructuredMeeting, :count).by(1)
+ expect { subject }.to change(Meeting, :count).by(1)
expect(subject).to be_success
created_meeting = subject.result
diff --git a/modules/openid_connect/app/controllers/openid_connect/providers_controller.rb b/modules/openid_connect/app/controllers/openid_connect/providers_controller.rb
index a4aadf58f8b..88d6c36c5f8 100644
--- a/modules/openid_connect/app/controllers/openid_connect/providers_controller.rb
+++ b/modules/openid_connect/app/controllers/openid_connect/providers_controller.rb
@@ -36,7 +36,7 @@ module OpenIDConnect
menu_item :plugin_openid_connect
before_action :require_admin
- before_action :check_ee
+ before_action :check_ee, except: %i[index]
before_action :find_provider, only: %i[edit show update confirm_destroy destroy]
before_action :set_edit_state, only: %i[create edit update]
@@ -123,16 +123,11 @@ module OpenIDConnect
private
def check_ee
- unless EnterpriseToken.allows_to?(:sso_auth_providers)
- render template: "/openid_connect/providers/upsale"
- false
- end
+ redirect_to action: :index unless EnterpriseToken.allows_to?(:sso_auth_providers)
end
def find_provider
@provider = OpenIDConnect::Provider.find(params[:id])
- rescue ActiveRecord::RecordNotFound
- render_404
end
def default_breadcrumb; end
diff --git a/modules/openid_connect/app/views/openid_connect/providers/index.html.erb b/modules/openid_connect/app/views/openid_connect/providers/index.html.erb
index e912d3269d4..33ae7abd753 100644
--- a/modules/openid_connect/app/views/openid_connect/providers/index.html.erb
+++ b/modules/openid_connect/app/views/openid_connect/providers/index.html.erb
@@ -21,6 +21,7 @@
) do |menu|
menu.with_show_button(
scheme: :primary,
+ disabled: !EnterpriseToken.allows_to?(:sso_auth_providers),
aria: { label: I18n.t("openid_connect.providers.label_add_new") }
) do |button|
button.with_leading_visual_icon(icon: :plus)
@@ -39,4 +40,5 @@
end
%>
+<%= render EnterpriseEdition::BannerComponent.new(:sso_auth_providers, i18n_scope: "openid_connect.providers.upsale") %>
<%= render ::OpenIDConnect::Providers::TableComponent.new(rows: @providers) %>
diff --git a/modules/openid_connect/app/views/openid_connect/providers/upsale.html.erb b/modules/openid_connect/app/views/openid_connect/providers/upsale.html.erb
deleted file mode 100644
index 6b6063a75b6..00000000000
--- a/modules/openid_connect/app/views/openid_connect/providers/upsale.html.erb
+++ /dev/null
@@ -1,9 +0,0 @@
-<% html_title(t(:label_administration), t("openid_connect.providers.plural")) -%>
-
-<%= render template: "common/upsale",
- locals: {
- feature_title: t("openid_connect.providers.plural"),
- feature_description: t("openid_connect.providers.upsale.description"),
- feature_reference: "enterprise-openid-connect",
- feature_image: "enterprise/open-id-providers.jpg"
- } %>
diff --git a/modules/openid_connect/config/locales/crowdin/af.yml b/modules/openid_connect/config/locales/crowdin/af.yml
index b161587c33e..3f8e6e7873c 100644
--- a/modules/openid_connect/config/locales/crowdin/af.yml
+++ b/modules/openid_connect/config/locales/crowdin/af.yml
@@ -92,6 +92,7 @@ af:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/ar.yml b/modules/openid_connect/config/locales/crowdin/ar.yml
index 568250486a9..882e3e06d57 100644
--- a/modules/openid_connect/config/locales/crowdin/ar.yml
+++ b/modules/openid_connect/config/locales/crowdin/ar.yml
@@ -92,6 +92,7 @@ ar:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: إضافة مزود OpenID جديد
label_edit: تعديل موفر OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/az.yml b/modules/openid_connect/config/locales/crowdin/az.yml
index 5d52883594f..280764a568f 100644
--- a/modules/openid_connect/config/locales/crowdin/az.yml
+++ b/modules/openid_connect/config/locales/crowdin/az.yml
@@ -92,6 +92,7 @@ az:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/be.yml b/modules/openid_connect/config/locales/crowdin/be.yml
index 98a664732bb..4c9196c327a 100644
--- a/modules/openid_connect/config/locales/crowdin/be.yml
+++ b/modules/openid_connect/config/locales/crowdin/be.yml
@@ -92,6 +92,7 @@ be:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/bg.yml b/modules/openid_connect/config/locales/crowdin/bg.yml
index 3c236401f6e..39b8edbd0f4 100644
--- a/modules/openid_connect/config/locales/crowdin/bg.yml
+++ b/modules/openid_connect/config/locales/crowdin/bg.yml
@@ -92,6 +92,7 @@ bg:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Добавяне на нов OpenID доставчик
label_edit: Редактиране на OpenID доставчика %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/ca.yml b/modules/openid_connect/config/locales/crowdin/ca.yml
index de128f75ca6..d50f6e5185f 100644
--- a/modules/openid_connect/config/locales/crowdin/ca.yml
+++ b/modules/openid_connect/config/locales/crowdin/ca.yml
@@ -92,6 +92,7 @@ ca:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Afegeix un proveïdor d'OpenID
label_edit: Edita el proveïdor d'OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/ckb-IR.yml b/modules/openid_connect/config/locales/crowdin/ckb-IR.yml
index e052c58f964..1d78f8ebb1f 100644
--- a/modules/openid_connect/config/locales/crowdin/ckb-IR.yml
+++ b/modules/openid_connect/config/locales/crowdin/ckb-IR.yml
@@ -92,6 +92,7 @@ ckb-IR:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/cs.yml b/modules/openid_connect/config/locales/crowdin/cs.yml
index a9117b5b380..84b50a1ab49 100644
--- a/modules/openid_connect/config/locales/crowdin/cs.yml
+++ b/modules/openid_connect/config/locales/crowdin/cs.yml
@@ -92,6 +92,7 @@ cs:
custom:
name: Vlastní
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Připojit OpenProject k poskytovateli OpenID připojení identity
label_add_new: Přidat nového poskytovatele OpenID
label_edit: Upravit poskytovatele OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/da.yml b/modules/openid_connect/config/locales/crowdin/da.yml
index 633d28bb36b..6484b3b0036 100644
--- a/modules/openid_connect/config/locales/crowdin/da.yml
+++ b/modules/openid_connect/config/locales/crowdin/da.yml
@@ -92,6 +92,7 @@ da:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/de.yml b/modules/openid_connect/config/locales/crowdin/de.yml
index ce881348b12..0f5c0fd41fc 100644
--- a/modules/openid_connect/config/locales/crowdin/de.yml
+++ b/modules/openid_connect/config/locales/crowdin/de.yml
@@ -92,6 +92,7 @@ de:
custom:
name: Benutzerdefiniert
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: OpenProject mit einem OpenID Connect SSO-Anbieter verbinden
label_add_new: Neuen OpenID-Anbieter hinzufügen
label_edit: OpenID-Provider %{name} bearbeiten
diff --git a/modules/openid_connect/config/locales/crowdin/el.yml b/modules/openid_connect/config/locales/crowdin/el.yml
index f50ad0aa2e3..2e54c262bd5 100644
--- a/modules/openid_connect/config/locales/crowdin/el.yml
+++ b/modules/openid_connect/config/locales/crowdin/el.yml
@@ -92,6 +92,7 @@ el:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Προσθήκη νέου παρόχου OpenID
label_edit: Επεξεργασία παρόχου OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/eo.yml b/modules/openid_connect/config/locales/crowdin/eo.yml
index a74f2d4950c..a10251070ea 100644
--- a/modules/openid_connect/config/locales/crowdin/eo.yml
+++ b/modules/openid_connect/config/locales/crowdin/eo.yml
@@ -92,6 +92,7 @@ eo:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/es.yml b/modules/openid_connect/config/locales/crowdin/es.yml
index a13592e204b..87ffa8f3ffe 100644
--- a/modules/openid_connect/config/locales/crowdin/es.yml
+++ b/modules/openid_connect/config/locales/crowdin/es.yml
@@ -92,6 +92,7 @@ es:
custom:
name: Personalizado
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Conectar OpenProject a un proveedor de identidad OpenID Connect
label_add_new: Agregar nuevo proveedor de OpenID
label_edit: Editar el proveedor de OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/et.yml b/modules/openid_connect/config/locales/crowdin/et.yml
index ab61a976b02..252ba07fe9d 100644
--- a/modules/openid_connect/config/locales/crowdin/et.yml
+++ b/modules/openid_connect/config/locales/crowdin/et.yml
@@ -92,6 +92,7 @@ et:
custom:
name: Kohandatud
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/eu.yml b/modules/openid_connect/config/locales/crowdin/eu.yml
index 42ef7366e07..842e25768bf 100644
--- a/modules/openid_connect/config/locales/crowdin/eu.yml
+++ b/modules/openid_connect/config/locales/crowdin/eu.yml
@@ -92,6 +92,7 @@ eu:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/fa.yml b/modules/openid_connect/config/locales/crowdin/fa.yml
index 7ef18e612a9..2da44818e32 100644
--- a/modules/openid_connect/config/locales/crowdin/fa.yml
+++ b/modules/openid_connect/config/locales/crowdin/fa.yml
@@ -92,6 +92,7 @@ fa:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/fi.yml b/modules/openid_connect/config/locales/crowdin/fi.yml
index 13597410e5c..ce996ef0f01 100644
--- a/modules/openid_connect/config/locales/crowdin/fi.yml
+++ b/modules/openid_connect/config/locales/crowdin/fi.yml
@@ -92,6 +92,7 @@ fi:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/fil.yml b/modules/openid_connect/config/locales/crowdin/fil.yml
index cc07c5fa911..0210762ebf0 100644
--- a/modules/openid_connect/config/locales/crowdin/fil.yml
+++ b/modules/openid_connect/config/locales/crowdin/fil.yml
@@ -92,6 +92,7 @@ fil:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/fr.yml b/modules/openid_connect/config/locales/crowdin/fr.yml
index fdebf25ca4f..001a1a97b66 100644
--- a/modules/openid_connect/config/locales/crowdin/fr.yml
+++ b/modules/openid_connect/config/locales/crowdin/fr.yml
@@ -92,6 +92,7 @@ fr:
custom:
name: Personnalisé
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connecter OpenProject à un fournisseur d'identité OpenID Connect
label_add_new: Ajouter un nouveau fournisseur OpenID
label_edit: Modifier le fournisseur OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/he.yml b/modules/openid_connect/config/locales/crowdin/he.yml
index 261a7ca24d6..1de4c6ffed3 100644
--- a/modules/openid_connect/config/locales/crowdin/he.yml
+++ b/modules/openid_connect/config/locales/crowdin/he.yml
@@ -92,6 +92,7 @@ he:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/hi.yml b/modules/openid_connect/config/locales/crowdin/hi.yml
index b6ae65eea6f..f34f9b0f129 100644
--- a/modules/openid_connect/config/locales/crowdin/hi.yml
+++ b/modules/openid_connect/config/locales/crowdin/hi.yml
@@ -92,6 +92,7 @@ hi:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/hr.yml b/modules/openid_connect/config/locales/crowdin/hr.yml
index cf312943608..70d4a59cd5b 100644
--- a/modules/openid_connect/config/locales/crowdin/hr.yml
+++ b/modules/openid_connect/config/locales/crowdin/hr.yml
@@ -92,6 +92,7 @@ hr:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/hu.yml b/modules/openid_connect/config/locales/crowdin/hu.yml
index 2f3ba184fb5..0aa9cc02a3b 100644
--- a/modules/openid_connect/config/locales/crowdin/hu.yml
+++ b/modules/openid_connect/config/locales/crowdin/hu.yml
@@ -92,6 +92,7 @@ hu:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: OpenID szolgáltató hozzáadása
label_edit: OpenID szolgáltató %{name} szerkesztése
diff --git a/modules/openid_connect/config/locales/crowdin/id.yml b/modules/openid_connect/config/locales/crowdin/id.yml
index fa74a9908f0..e83802001e8 100644
--- a/modules/openid_connect/config/locales/crowdin/id.yml
+++ b/modules/openid_connect/config/locales/crowdin/id.yml
@@ -92,6 +92,7 @@ id:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Tambahkan penyedia OpenID baru
label_edit: Edit penyedia OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/it.yml b/modules/openid_connect/config/locales/crowdin/it.yml
index aff4d8b4c55..ff223cb5516 100644
--- a/modules/openid_connect/config/locales/crowdin/it.yml
+++ b/modules/openid_connect/config/locales/crowdin/it.yml
@@ -92,6 +92,7 @@ it:
custom:
name: Personalizzato
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connetti OpenProject a un fornitore di identità OpenID connect
label_add_new: Aggiungi un nuovo provider OpenID
label_edit: Modifica provider OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/ja.yml b/modules/openid_connect/config/locales/crowdin/ja.yml
index a732620e3d9..ea1cb9db368 100644
--- a/modules/openid_connect/config/locales/crowdin/ja.yml
+++ b/modules/openid_connect/config/locales/crowdin/ja.yml
@@ -92,6 +92,7 @@ ja:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: 新しい OpenID プロバイダーを追加
label_edit: OpenID プロバイダー %{name} を編集
diff --git a/modules/openid_connect/config/locales/crowdin/ka.yml b/modules/openid_connect/config/locales/crowdin/ka.yml
index aac0dd5e001..610bc0bd86f 100644
--- a/modules/openid_connect/config/locales/crowdin/ka.yml
+++ b/modules/openid_connect/config/locales/crowdin/ka.yml
@@ -92,6 +92,7 @@ ka:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/kk.yml b/modules/openid_connect/config/locales/crowdin/kk.yml
index 1541745c73a..d7f65270c98 100644
--- a/modules/openid_connect/config/locales/crowdin/kk.yml
+++ b/modules/openid_connect/config/locales/crowdin/kk.yml
@@ -92,6 +92,7 @@ kk:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/ko.yml b/modules/openid_connect/config/locales/crowdin/ko.yml
index b55c2f6698b..3adcc86fc55 100644
--- a/modules/openid_connect/config/locales/crowdin/ko.yml
+++ b/modules/openid_connect/config/locales/crowdin/ko.yml
@@ -92,6 +92,7 @@ ko:
custom:
name: 사용자 지정
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: OpenID Connect ID 공급자에 OpenProject 연결
label_add_new: 새로운 OpenID 공급자 추가
label_edit: OpenID 공급자 %{name} 편집
diff --git a/modules/openid_connect/config/locales/crowdin/lt.yml b/modules/openid_connect/config/locales/crowdin/lt.yml
index 6953579d5ff..048c479a0ef 100644
--- a/modules/openid_connect/config/locales/crowdin/lt.yml
+++ b/modules/openid_connect/config/locales/crowdin/lt.yml
@@ -92,6 +92,7 @@ lt:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Pridėti naują OpenID tiekėją
label_edit: Keisti OpenID tiekėją %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/lv.yml b/modules/openid_connect/config/locales/crowdin/lv.yml
index cfc8976b9a6..76fc2f6f77e 100644
--- a/modules/openid_connect/config/locales/crowdin/lv.yml
+++ b/modules/openid_connect/config/locales/crowdin/lv.yml
@@ -92,6 +92,7 @@ lv:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/mn.yml b/modules/openid_connect/config/locales/crowdin/mn.yml
index 4a1de552294..fa15043ee20 100644
--- a/modules/openid_connect/config/locales/crowdin/mn.yml
+++ b/modules/openid_connect/config/locales/crowdin/mn.yml
@@ -92,6 +92,7 @@ mn:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/ms.yml b/modules/openid_connect/config/locales/crowdin/ms.yml
index 38ca0fe23f8..8a0e7b6701e 100644
--- a/modules/openid_connect/config/locales/crowdin/ms.yml
+++ b/modules/openid_connect/config/locales/crowdin/ms.yml
@@ -92,6 +92,7 @@ ms:
custom:
name: Adat
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Sambungkan OpenProject kepada pembekal identiti sambungan OpenID
label_add_new: Tambah penyedia OpenID baharu
label_edit: Edit penyedia OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/ne.yml b/modules/openid_connect/config/locales/crowdin/ne.yml
index acd30aa9918..41cb3c16aef 100644
--- a/modules/openid_connect/config/locales/crowdin/ne.yml
+++ b/modules/openid_connect/config/locales/crowdin/ne.yml
@@ -92,6 +92,7 @@ ne:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/nl.yml b/modules/openid_connect/config/locales/crowdin/nl.yml
index 632a5b3f7cd..b368398834e 100644
--- a/modules/openid_connect/config/locales/crowdin/nl.yml
+++ b/modules/openid_connect/config/locales/crowdin/nl.yml
@@ -92,6 +92,7 @@ nl:
custom:
name: Aangepast
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: OpenProject verbinden met een OpenID connect identity provider
label_add_new: Voeg een nieuwe OpenID provider toe
label_edit: OpenID provider %{name} bewerken
diff --git a/modules/openid_connect/config/locales/crowdin/no.yml b/modules/openid_connect/config/locales/crowdin/no.yml
index ee45b8e5cac..92be6b2600e 100644
--- a/modules/openid_connect/config/locales/crowdin/no.yml
+++ b/modules/openid_connect/config/locales/crowdin/no.yml
@@ -92,6 +92,7 @@
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Legg til ny OpenID-leverandør
label_edit: Rediger OpenID-leverandør %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/pl.yml b/modules/openid_connect/config/locales/crowdin/pl.yml
index cd9a8dde0c6..b8224f59695 100644
--- a/modules/openid_connect/config/locales/crowdin/pl.yml
+++ b/modules/openid_connect/config/locales/crowdin/pl.yml
@@ -92,6 +92,7 @@ pl:
custom:
name: Niestandardowy
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Połącz OpenProject z dostawcą tożsamości OpenID Connect
label_add_new: Dodaj nowego dostawcę OpenID
label_edit: Edytuj dostawcę OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/pt-BR.yml b/modules/openid_connect/config/locales/crowdin/pt-BR.yml
index e9521ee3c09..76d3c4dffed 100644
--- a/modules/openid_connect/config/locales/crowdin/pt-BR.yml
+++ b/modules/openid_connect/config/locales/crowdin/pt-BR.yml
@@ -92,6 +92,7 @@ pt-BR:
custom:
name: Personalizar
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Conecte o OpenProject a um provedor de identidade OpenID Connect
label_add_new: Adicionar um novo provedor OpenID
label_edit: Editar provedor OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/pt-PT.yml b/modules/openid_connect/config/locales/crowdin/pt-PT.yml
index 7a7f038a9fb..f01989bb22b 100644
--- a/modules/openid_connect/config/locales/crowdin/pt-PT.yml
+++ b/modules/openid_connect/config/locales/crowdin/pt-PT.yml
@@ -92,6 +92,7 @@ pt-PT:
custom:
name: Personalizado
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Associar o OpenProject a um fornecedor de identidade OpenID Connect
label_add_new: Adicionar um novo fornecedor OpenID
label_edit: Editar fornecedor de OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/ro.yml b/modules/openid_connect/config/locales/crowdin/ro.yml
index 1ae193110c2..cdf2d963f76 100644
--- a/modules/openid_connect/config/locales/crowdin/ro.yml
+++ b/modules/openid_connect/config/locales/crowdin/ro.yml
@@ -92,6 +92,7 @@ ro:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Adaugă stare nouă
label_edit: Editați furnizorul OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/ru.yml b/modules/openid_connect/config/locales/crowdin/ru.yml
index 80ac36ac536..382c97c942a 100644
--- a/modules/openid_connect/config/locales/crowdin/ru.yml
+++ b/modules/openid_connect/config/locales/crowdin/ru.yml
@@ -92,6 +92,7 @@ ru:
custom:
name: Пользовательский
upsale:
+ title: "Single Sign-On (SSO) с подключением OpenID"
description: Подключить OpenProject к провайдеру идентификации OpenID connect
label_add_new: Добавить нового провайдера OpenID
label_edit: Редактировать провайдера OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/rw.yml b/modules/openid_connect/config/locales/crowdin/rw.yml
index 06419d2835e..6846f5c5b38 100644
--- a/modules/openid_connect/config/locales/crowdin/rw.yml
+++ b/modules/openid_connect/config/locales/crowdin/rw.yml
@@ -92,6 +92,7 @@ rw:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/si.yml b/modules/openid_connect/config/locales/crowdin/si.yml
index c9b38005b3e..26c318fb2b8 100644
--- a/modules/openid_connect/config/locales/crowdin/si.yml
+++ b/modules/openid_connect/config/locales/crowdin/si.yml
@@ -92,6 +92,7 @@ si:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/sk.yml b/modules/openid_connect/config/locales/crowdin/sk.yml
index 745d6822c33..1325bda00b2 100644
--- a/modules/openid_connect/config/locales/crowdin/sk.yml
+++ b/modules/openid_connect/config/locales/crowdin/sk.yml
@@ -92,6 +92,7 @@ sk:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/sl.yml b/modules/openid_connect/config/locales/crowdin/sl.yml
index d62b6a4dae3..18d98dbe3e4 100644
--- a/modules/openid_connect/config/locales/crowdin/sl.yml
+++ b/modules/openid_connect/config/locales/crowdin/sl.yml
@@ -92,6 +92,7 @@ sl:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Dodaj novega OpenID ponudnika
label_edit: Uredi OpenID ponudnik %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/sr.yml b/modules/openid_connect/config/locales/crowdin/sr.yml
index 69653198741..e4d88508f06 100644
--- a/modules/openid_connect/config/locales/crowdin/sr.yml
+++ b/modules/openid_connect/config/locales/crowdin/sr.yml
@@ -92,6 +92,7 @@ sr:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/sv.yml b/modules/openid_connect/config/locales/crowdin/sv.yml
index ba4aaf94619..88a6f9acbd0 100644
--- a/modules/openid_connect/config/locales/crowdin/sv.yml
+++ b/modules/openid_connect/config/locales/crowdin/sv.yml
@@ -92,6 +92,7 @@ sv:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Lägg till en ny OpenID-leverantör
label_edit: Redigera OpenID-leverantör %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/th.yml b/modules/openid_connect/config/locales/crowdin/th.yml
index 3ec4653df12..632a0fd8ec8 100644
--- a/modules/openid_connect/config/locales/crowdin/th.yml
+++ b/modules/openid_connect/config/locales/crowdin/th.yml
@@ -92,6 +92,7 @@ th:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/tr.yml b/modules/openid_connect/config/locales/crowdin/tr.yml
index b2a667f94a5..058853aa056 100644
--- a/modules/openid_connect/config/locales/crowdin/tr.yml
+++ b/modules/openid_connect/config/locales/crowdin/tr.yml
@@ -92,6 +92,7 @@ tr:
custom:
name: Özel
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Yeni bir OpenID sağlayıcı ekle
label_edit: OpenID sağlayıcıyı düzenle %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/uk.yml b/modules/openid_connect/config/locales/crowdin/uk.yml
index 4b2366eac97..3afc93e5449 100644
--- a/modules/openid_connect/config/locales/crowdin/uk.yml
+++ b/modules/openid_connect/config/locales/crowdin/uk.yml
@@ -92,6 +92,7 @@ uk:
custom:
name: Власний
upsale:
+ title: "Єдиний вхід (SSO) з OpenID Connect"
description: Підключіть OpenProject до постачальника ідентифікаційних даних OpenID
label_add_new: Додати нового постачальника OpenID
label_edit: Редагувати OpenID провайдера %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/uz.yml b/modules/openid_connect/config/locales/crowdin/uz.yml
index 4474b9b1f06..baac338feac 100644
--- a/modules/openid_connect/config/locales/crowdin/uz.yml
+++ b/modules/openid_connect/config/locales/crowdin/uz.yml
@@ -92,6 +92,7 @@ uz:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/vi.yml b/modules/openid_connect/config/locales/crowdin/vi.yml
index b175ce0c00b..815e62ae382 100644
--- a/modules/openid_connect/config/locales/crowdin/vi.yml
+++ b/modules/openid_connect/config/locales/crowdin/vi.yml
@@ -92,6 +92,7 @@ vi:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Thêm nhà cung cấp OpenID mới
label_edit: Chỉnh sửa nhà cung cấp OpenID %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/zh-CN.yml b/modules/openid_connect/config/locales/crowdin/zh-CN.yml
index 52a3771898e..a916e965e4f 100644
--- a/modules/openid_connect/config/locales/crowdin/zh-CN.yml
+++ b/modules/openid_connect/config/locales/crowdin/zh-CN.yml
@@ -92,6 +92,7 @@ zh-CN:
custom:
name: 自定义
upsale:
+ title: "与 OpenID 连接的单点登录 (SSO)"
description: 将 OpenProject 连接到 OpenID connect 身份提供商
label_add_new: 添加一个新的 OpenID 提供商
label_edit: 编辑 OpenID 提供商 %{name}
diff --git a/modules/openid_connect/config/locales/crowdin/zh-TW.yml b/modules/openid_connect/config/locales/crowdin/zh-TW.yml
index 0e5889b4100..7c746c432ce 100644
--- a/modules/openid_connect/config/locales/crowdin/zh-TW.yml
+++ b/modules/openid_connect/config/locales/crowdin/zh-TW.yml
@@ -92,6 +92,7 @@ zh-TW:
custom:
name: 自訂
upsale:
+ title: "使用 OpenID 連線的單一登入 (SSO)"
description: 將 OpenProject 連接到 OpenID connect 身分提供者
label_add_new: 新增 OpenID 提供商
label_edit: 編輯 OpenID 提供商:%{name}
diff --git a/modules/openid_connect/config/locales/en.yml b/modules/openid_connect/config/locales/en.yml
index 9b0f42e7842..d4869fc40cf 100644
--- a/modules/openid_connect/config/locales/en.yml
+++ b/modules/openid_connect/config/locales/en.yml
@@ -97,6 +97,7 @@ en:
custom:
name: Custom
upsale:
+ title: "Single Sign-On (SSO) with OpenID connect"
description: Connect OpenProject to an OpenID connect identity provider
label_add_new: Add a new OpenID provider
label_edit: Edit OpenID provider %{name}
diff --git a/modules/openid_connect/spec/features/administration/oidc_custom_crud_spec.rb b/modules/openid_connect/spec/features/administration/oidc_custom_crud_spec.rb
index 9b5a5608986..60a993f3aff 100644
--- a/modules/openid_connect/spec/features/administration/oidc_custom_crud_spec.rb
+++ b/modules/openid_connect/spec/features/administration/oidc_custom_crud_spec.rb
@@ -175,7 +175,7 @@ RSpec.describe "OIDC administration CRUD",
context "without EE", without_ee: %i[sso_auth_providers] do
it "renders the upsale page" do
visit "/admin/openid_connect/providers"
- expect(page).to have_text "OpenID providers is an Enterprise add-on"
+ expect(page).to have_enterprise_banner(:premium)
end
end
end
diff --git a/modules/overviews/app/components/_index.sass b/modules/overviews/app/components/_index.sass
index fc1fdcf84e0..3ffde8d69d3 100644
--- a/modules/overviews/app/components/_index.sass
+++ b/modules/overviews/app/components/_index.sass
@@ -1 +1 @@
-@import "./project_custom_fields/sections/project_custom_fields/show_component.sass"
+@import "./overviews/project_custom_fields/item_component.sass"
diff --git a/modules/overviews/app/components/project_custom_fields/sections/edit_component.html.erb b/modules/overviews/app/components/overviews/project_custom_fields/edit_component.html.erb
similarity index 100%
rename from modules/overviews/app/components/project_custom_fields/sections/edit_component.html.erb
rename to modules/overviews/app/components/overviews/project_custom_fields/edit_component.html.erb
diff --git a/modules/overviews/app/components/project_custom_fields/sections/edit_component.rb b/modules/overviews/app/components/overviews/project_custom_fields/edit_component.rb
similarity index 95%
rename from modules/overviews/app/components/project_custom_fields/sections/edit_component.rb
rename to modules/overviews/app/components/overviews/project_custom_fields/edit_component.rb
index ebf29c10644..7317293ba46 100644
--- a/modules/overviews/app/components/project_custom_fields/sections/edit_component.rb
+++ b/modules/overviews/app/components/overviews/project_custom_fields/edit_component.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -26,8 +28,8 @@
# See COPYRIGHT and LICENSE files for more details.
#++
-module ProjectCustomFields
- module Sections
+module Overviews
+ module ProjectCustomFields
class EditComponent < ApplicationComponent
include ApplicationHelper
include OpTurbo::Streamable
diff --git a/modules/overviews/app/components/project_custom_fields/sections/edit_dialog_component.html.erb b/modules/overviews/app/components/overviews/project_custom_fields/edit_dialog_component.html.erb
similarity index 81%
rename from modules/overviews/app/components/project_custom_fields/sections/edit_dialog_component.html.erb
rename to modules/overviews/app/components/overviews/project_custom_fields/edit_dialog_component.html.erb
index 1c4859b33cb..2ffb36b5c94 100644
--- a/modules/overviews/app/components/project_custom_fields/sections/edit_dialog_component.html.erb
+++ b/modules/overviews/app/components/overviews/project_custom_fields/edit_dialog_component.html.erb
@@ -8,12 +8,12 @@
) do |d|
d.with_header(variant: :large)
d.with_body(classes: "Overlay-body_autocomplete_height") do
- render(::ProjectCustomFields::Sections::EditComponent.new(project_custom_field_section: @project_custom_field_section, project: @project))
+ render(Overviews::ProjectCustomFields::EditComponent.new(project_custom_field_section: @project_custom_field_section, project: @project))
end
d.with_footer do
component_collection do |footer_collection|
footer_collection.with_component(
- Primer::ButtonComponent.new(
+ Primer::Beta::Button.new(
data: {
"close-dialog-id": "edit-project-custom-fields-dialog-#{@project_custom_field_section.id}"
}
@@ -22,7 +22,7 @@
t("button_cancel")
end
footer_collection.with_component(
- Primer::ButtonComponent.new(
+ Primer::Beta::Button.new(
scheme: :primary,
type: :submit,
form: "project-section-edit-form",
diff --git a/modules/overviews/app/components/project_custom_fields/sections/edit_dialog_component.rb b/modules/overviews/app/components/overviews/project_custom_fields/edit_dialog_component.rb
similarity index 95%
rename from modules/overviews/app/components/project_custom_fields/sections/edit_dialog_component.rb
rename to modules/overviews/app/components/overviews/project_custom_fields/edit_dialog_component.rb
index 102d7b04746..6099f6ee07b 100644
--- a/modules/overviews/app/components/project_custom_fields/sections/edit_dialog_component.rb
+++ b/modules/overviews/app/components/overviews/project_custom_fields/edit_dialog_component.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -26,8 +28,8 @@
# See COPYRIGHT and LICENSE files for more details.
#++
-module ProjectCustomFields
- module Sections
+module Overviews
+ module ProjectCustomFields
class EditDialogComponent < ApplicationComponent
include ApplicationHelper
include OpTurbo::Streamable
diff --git a/modules/overviews/app/components/project_custom_fields/sections/project_custom_fields/show_component.html.erb b/modules/overviews/app/components/overviews/project_custom_fields/item_component.html.erb
similarity index 100%
rename from modules/overviews/app/components/project_custom_fields/sections/project_custom_fields/show_component.html.erb
rename to modules/overviews/app/components/overviews/project_custom_fields/item_component.html.erb
diff --git a/modules/overviews/app/components/overviews/project_custom_fields/item_component.rb b/modules/overviews/app/components/overviews/project_custom_fields/item_component.rb
new file mode 100644
index 00000000000..954fecc3a3b
--- /dev/null
+++ b/modules/overviews/app/components/overviews/project_custom_fields/item_component.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+module Overviews
+ module ProjectCustomFields
+ class ItemComponent < ApplicationComponent
+ include ApplicationHelper
+ include CustomFieldsHelper
+ include OpPrimer::ComponentHelpers
+
+ def initialize(project_custom_field:, project_custom_field_values:)
+ super
+
+ @project_custom_field = project_custom_field
+ @project_custom_field_values = project_custom_field_values
+ end
+
+ private
+
+ def not_set?
+ @project_custom_field_values.empty? || @project_custom_field_values.all? { |cf_value| cf_value.value.blank? }
+ end
+
+ def render_value
+ case @project_custom_field.field_format
+ when "link"
+ render_link
+ when "text"
+ render_long_text
+ when "user"
+ render_user
+ else
+ render(Primer::Beta::Text.new) do
+ @project_custom_field_values&.map do |cf_value|
+ format_value(cf_value.value, @project_custom_field)
+ end&.join(", ")
+ end
+ end
+ end
+
+ def render_long_text
+ render OpenProject::Common::AttributeComponent.new("dialog-cf-#{@project_custom_field.id}",
+ @project_custom_field.name,
+ @project_custom_field_values&.first&.value,
+ lines: 3)
+ end
+
+ def render_user
+ if @project_custom_field.multi_value?
+ flex_layout do |avatar_container|
+ @project_custom_field_values&.each do |cf_value|
+ avatar_container.with_row do
+ render_avatar(cf_value.typed_value)
+ end
+ end
+ end
+ else
+ render_avatar(@project_custom_field_values&.first&.typed_value)
+ end
+ end
+
+ def render_avatar(user)
+ render(Users::AvatarComponent.new(user:, size: :mini))
+ end
+
+ def render_link
+ href = @project_custom_field_values&.first&.value
+ link = Addressable::URI.parse(href)
+ return href unless link
+
+ target = link.host == Setting.host_without_protocol ? "_top" : "_blank"
+ render(Primer::Beta::Link.new(href:, rel: "noopener noreferrer", target:)) do
+ href
+ end
+ end
+ end
+ end
+end
diff --git a/modules/overviews/app/components/project_custom_fields/sections/project_custom_fields/show_component.sass b/modules/overviews/app/components/overviews/project_custom_fields/item_component.sass
similarity index 100%
rename from modules/overviews/app/components/project_custom_fields/sections/project_custom_fields/show_component.sass
rename to modules/overviews/app/components/overviews/project_custom_fields/item_component.sass
diff --git a/modules/overviews/app/components/project_custom_fields/sections/show_component.html.erb b/modules/overviews/app/components/overviews/project_custom_fields/show_component.html.erb
similarity index 93%
rename from modules/overviews/app/components/project_custom_fields/sections/show_component.html.erb
rename to modules/overviews/app/components/overviews/project_custom_fields/show_component.html.erb
index db29d31d191..bdcaadcbde3 100644
--- a/modules/overviews/app/components/project_custom_fields/sections/show_component.html.erb
+++ b/modules/overviews/app/components/overviews/project_custom_fields/show_component.html.erb
@@ -26,7 +26,7 @@
margin = i == @project_custom_fields.size - 1 ? 0 : 3
details_container.with_row(mb: margin) do
render(
- ProjectCustomFields::Sections::ProjectCustomFields::ShowComponent.new(
+ Overviews::ProjectCustomFields::ItemComponent.new(
project_custom_field:,
project_custom_field_values: get_eager_loaded_project_custom_field_values_for(project_custom_field.id)
)
diff --git a/modules/overviews/app/components/project_custom_fields/sections/show_component.rb b/modules/overviews/app/components/overviews/project_custom_fields/show_component.rb
similarity index 96%
rename from modules/overviews/app/components/project_custom_fields/sections/show_component.rb
rename to modules/overviews/app/components/overviews/project_custom_fields/show_component.rb
index 78137a19d77..341176dc930 100644
--- a/modules/overviews/app/components/project_custom_fields/sections/show_component.rb
+++ b/modules/overviews/app/components/overviews/project_custom_fields/show_component.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -26,8 +28,8 @@
# See COPYRIGHT and LICENSE files for more details.
#++
-module ProjectCustomFields
- module Sections
+module Overviews
+ module ProjectCustomFields
class ShowComponent < ApplicationComponent
include ApplicationHelper
include OpPrimer::ComponentHelpers
diff --git a/modules/overviews/app/components/project_custom_fields/side_panel_component.html.erb b/modules/overviews/app/components/overviews/project_custom_fields/side_panel_component.html.erb
similarity index 90%
rename from modules/overviews/app/components/project_custom_fields/side_panel_component.html.erb
rename to modules/overviews/app/components/overviews/project_custom_fields/side_panel_component.html.erb
index b6aa482acd6..2eecef3a612 100644
--- a/modules/overviews/app/components/project_custom_fields/side_panel_component.html.erb
+++ b/modules/overviews/app/components/overviews/project_custom_fields/side_panel_component.html.erb
@@ -9,7 +9,7 @@
) do |panel|
available_project_custom_fields_grouped_by_section.each do |project_custom_field_section, project_custom_fields|
panel.with_section(
- ProjectCustomFields::Sections::ShowComponent.new(
+ Overviews::ProjectCustomFields::ShowComponent.new(
project: @project,
project_custom_field_section:,
project_custom_fields: project_custom_fields
diff --git a/modules/overviews/app/components/overviews/project_custom_fields/side_panel_component.rb b/modules/overviews/app/components/overviews/project_custom_fields/side_panel_component.rb
new file mode 100644
index 00000000000..2f531e43142
--- /dev/null
+++ b/modules/overviews/app/components/overviews/project_custom_fields/side_panel_component.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+module Overviews
+ module ProjectCustomFields
+ class SidePanelComponent < ApplicationComponent
+ include ApplicationHelper
+ include OpPrimer::ComponentHelpers
+ include OpTurbo::Streamable
+
+ def initialize(project:)
+ super
+
+ @project = project
+ end
+
+ private
+
+ def available_project_custom_fields_grouped_by_section
+ @available_project_custom_fields_grouped_by_section ||=
+ @project.available_custom_fields.group_by(&:project_custom_field_section)
+ end
+ end
+ end
+end
diff --git a/modules/overviews/app/components/project_life_cycles/sections/edit_component.html.erb b/modules/overviews/app/components/overviews/project_phases/edit_component.html.erb
similarity index 52%
rename from modules/overviews/app/components/project_life_cycles/sections/edit_component.html.erb
rename to modules/overviews/app/components/overviews/project_phases/edit_component.html.erb
index 3ba10a71d01..4d7e766733b 100644
--- a/modules/overviews/app/components/project_life_cycles/sections/edit_component.html.erb
+++ b/modules/overviews/app/components/overviews/project_phases/edit_component.html.erb
@@ -7,19 +7,14 @@
method: :put,
data: {
controller: "overview--project-life-cycles-form",
- "overview--project-life-cycles-form-url-value": project_life_cycles_form_path(project_id: model.id),
+ "overview--project-life-cycles-form-url-value": preview_project_phase_path(model),
"application-target": "dynamic",
turbo: true,
turbo_stream: true,
"test-selector": "async-dialog-content"
- },
- url: update_project_life_cycles_path(project_id: model.id)
+ }
) do |f|
- render(Primer::Forms::SpacingWrapper.new) do
- f.fields_for(:available_phases) do |life_cycle_form|
- render(Projects::LifeCycles::Form.new(life_cycle_form))
- end
- end
+ render(Projects::LifeCycles::Form.new(f))
end
end
%>
diff --git a/modules/overviews/app/components/project_life_cycles/sections/edit_component.rb b/modules/overviews/app/components/overviews/project_phases/edit_component.rb
similarity index 95%
rename from modules/overviews/app/components/project_life_cycles/sections/edit_component.rb
rename to modules/overviews/app/components/overviews/project_phases/edit_component.rb
index 69e395d772f..f49d49dd713 100644
--- a/modules/overviews/app/components/project_life_cycles/sections/edit_component.rb
+++ b/modules/overviews/app/components/overviews/project_phases/edit_component.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -26,8 +28,8 @@
# See COPYRIGHT and LICENSE files for more details.
#++
-module ProjectLifeCycles
- module Sections
+module Overviews
+ module ProjectPhases
class EditComponent < ApplicationComponent
include ApplicationHelper
include OpTurbo::Streamable
diff --git a/modules/overviews/app/components/project_life_cycles/sections/edit_dialog_component.html.erb b/modules/overviews/app/components/overviews/project_phases/edit_dialog_component.html.erb
similarity index 85%
rename from modules/overviews/app/components/project_life_cycles/sections/edit_dialog_component.html.erb
rename to modules/overviews/app/components/overviews/project_phases/edit_dialog_component.html.erb
index 1a027271f46..4c5725836bd 100644
--- a/modules/overviews/app/components/project_life_cycles/sections/edit_dialog_component.html.erb
+++ b/modules/overviews/app/components/overviews/project_phases/edit_dialog_component.html.erb
@@ -8,12 +8,12 @@
) do |d|
d.with_header(variant: :large)
d.with_body do
- render(::ProjectLifeCycles::Sections::EditComponent.new(model))
+ render(Overviews::ProjectPhases::EditComponent.new(model))
end
d.with_footer do
component_collection do |footer_collection|
footer_collection.with_component(
- Primer::ButtonComponent.new(
+ Primer::Beta::Button.new(
data: {
"close-dialog-id": DIALOG_ID
}
@@ -22,7 +22,7 @@
t("button_cancel")
end
footer_collection.with_component(
- Primer::ButtonComponent.new(
+ Primer::Beta::Button.new(
scheme: :primary,
type: :submit,
form: "project-life-cycles-edit-form",
diff --git a/modules/overviews/app/components/project_life_cycles/sections/edit_dialog_component.rb b/modules/overviews/app/components/overviews/project_phases/edit_dialog_component.rb
similarity index 97%
rename from modules/overviews/app/components/project_life_cycles/sections/edit_dialog_component.rb
rename to modules/overviews/app/components/overviews/project_phases/edit_dialog_component.rb
index 9d9a248eda1..3e916d9b167 100644
--- a/modules/overviews/app/components/project_life_cycles/sections/edit_dialog_component.rb
+++ b/modules/overviews/app/components/overviews/project_phases/edit_dialog_component.rb
@@ -28,8 +28,8 @@
# See COPYRIGHT and LICENSE files for more details.
#++
-module ProjectLifeCycles
- module Sections
+module Overviews
+ module ProjectPhases
class EditDialogComponent < ApplicationComponent
include ApplicationHelper
include OpTurbo::Streamable
diff --git a/modules/overviews/app/components/project_life_cycles/sections/project_life_cycles/show_component.html.erb b/modules/overviews/app/components/overviews/project_phases/item_component.html.erb
similarity index 74%
rename from modules/overviews/app/components/project_life_cycles/sections/project_life_cycles/show_component.html.erb
rename to modules/overviews/app/components/overviews/project_phases/item_component.html.erb
index 14c08c4f9a6..ced74188556 100644
--- a/modules/overviews/app/components/project_life_cycles/sections/project_life_cycles/show_component.html.erb
+++ b/modules/overviews/app/components/overviews/project_phases/item_component.html.erb
@@ -20,10 +20,12 @@
end
life_cycle_container.with_row(w: :full) do
- if not_set?
- render(Primer::Beta::Text.new) { t("placeholders.default") }
- else
- render Projects::PhaseComponent.new(phase: model)
+ render(authorized_edit_link) do
+ if not_set?
+ render(Primer::Beta::Text.new) { t("placeholders.default") }
+ else
+ render Projects::PhaseComponent.new(phase: model)
+ end
end
end
end
diff --git a/modules/overviews/app/components/overviews/project_phases/item_component.rb b/modules/overviews/app/components/overviews/project_phases/item_component.rb
new file mode 100644
index 00000000000..2e8baef8755
--- /dev/null
+++ b/modules/overviews/app/components/overviews/project_phases/item_component.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+module Overviews
+ module ProjectPhases
+ class ItemComponent < ApplicationComponent
+ include ApplicationHelper
+ include OpPrimer::ComponentHelpers
+
+ private
+
+ def not_set?
+ model.not_set?
+ end
+
+ def icon
+ :"op-phase"
+ end
+
+ def icon_color_class
+ helpers.hl_inline_class("project_phase_definition", model.definition)
+ end
+
+ def text
+ model.name
+ end
+
+ def authorized_edit_link
+ if allowed_to_edit?
+ Primer::Beta::Link.new(
+ href: edit_project_phase_path(model),
+ data: { controller: "async-dialog" },
+ aria: { label: I18n.t(:label_edit) },
+ test_selector: "project-life-cycle-edit-button-#{model.id}",
+ underline: false
+ )
+ else
+ Primer::Content.new
+ end
+ end
+
+ def allowed_to_edit?
+ User.current.allowed_in_project?(:edit_project_phases, model.project)
+ end
+ end
+ end
+end
diff --git a/modules/overviews/app/components/overviews/project_phases/show_component.html.erb b/modules/overviews/app/components/overviews/project_phases/show_component.html.erb
new file mode 100644
index 00000000000..180acce00ae
--- /dev/null
+++ b/modules/overviews/app/components/overviews/project_phases/show_component.html.erb
@@ -0,0 +1,25 @@
+<%=
+ component_wrapper do
+ render(
+ Primer::OpenProject::SidePanel::Section.new(
+ classes: "op-project-life-cyle-section-container",
+ test_selector: "project-life-cycle-section"
+ )
+ ) do |section|
+ section.with_title { t("label_life_cycle_step_plural") }
+
+ flex_layout do |details_container|
+ @phases.each_with_index do |life_cycle_step, i|
+ margin = i == @phases.size - 1 ? 0 : 3
+ details_container.with_row(mb: margin) do
+ render(
+ Overviews::ProjectPhases::ItemComponent.new(
+ life_cycle_step
+ )
+ )
+ end
+ end
+ end
+ end
+ end
+%>
diff --git a/modules/overviews/app/components/project_life_cycles/sections/show_component.rb b/modules/overviews/app/components/overviews/project_phases/show_component.rb
similarity index 86%
rename from modules/overviews/app/components/project_life_cycles/sections/show_component.rb
rename to modules/overviews/app/components/overviews/project_phases/show_component.rb
index ff8bfff92a1..2f2561282d9 100644
--- a/modules/overviews/app/components/project_life_cycles/sections/show_component.rb
+++ b/modules/overviews/app/components/overviews/project_phases/show_component.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -26,8 +28,8 @@
# See COPYRIGHT and LICENSE files for more details.
#++
-module ProjectLifeCycles
- module Sections
+module Overviews
+ module ProjectPhases
class ShowComponent < ApplicationComponent
include ApplicationHelper
include OpPrimer::ComponentHelpers
@@ -37,13 +39,7 @@ module ProjectLifeCycles
super
@project = project
- @life_cycle_steps = @project.available_phases
- end
-
- private
-
- def allowed_to_edit?
- User.current.allowed_in_project?(:edit_project_phases, @project)
+ @phases = @project.available_phases
end
end
end
diff --git a/modules/overviews/app/components/project_life_cycles/side_panel_component.html.erb b/modules/overviews/app/components/overviews/project_phases/side_panel_component.html.erb
similarity index 69%
rename from modules/overviews/app/components/project_life_cycles/side_panel_component.html.erb
rename to modules/overviews/app/components/overviews/project_phases/side_panel_component.html.erb
index 230767e0bc9..61a3e308f65 100644
--- a/modules/overviews/app/components/project_life_cycles/side_panel_component.html.erb
+++ b/modules/overviews/app/components/overviews/project_phases/side_panel_component.html.erb
@@ -6,7 +6,7 @@
test_selector: "project-life-cycles-sidebar-async-content"
)
) do |panel|
- panel.with_section(ProjectLifeCycles::Sections::ShowComponent.new(project: @project))
+ panel.with_section(Overviews::ProjectPhases::ShowComponent.new(project: @project))
end
end
%>
diff --git a/modules/overviews/app/components/overviews/project_phases/side_panel_component.rb b/modules/overviews/app/components/overviews/project_phases/side_panel_component.rb
new file mode 100644
index 00000000000..4929ccba078
--- /dev/null
+++ b/modules/overviews/app/components/overviews/project_phases/side_panel_component.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+module Overviews
+ module ProjectPhases
+ class SidePanelComponent < ApplicationComponent
+ include ApplicationHelper
+ include OpPrimer::ComponentHelpers
+ include OpTurbo::Streamable
+
+ def initialize(project:)
+ super
+
+ @project = project
+ end
+ end
+ end
+end
diff --git a/modules/overviews/app/components/project_custom_fields/sections/project_custom_fields/show_component.rb b/modules/overviews/app/components/project_custom_fields/sections/project_custom_fields/show_component.rb
deleted file mode 100644
index a8681d715e5..00000000000
--- a/modules/overviews/app/components/project_custom_fields/sections/project_custom_fields/show_component.rb
+++ /dev/null
@@ -1,105 +0,0 @@
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-module ProjectCustomFields
- module Sections
- module ProjectCustomFields
- class ShowComponent < ApplicationComponent
- include ApplicationHelper
- include CustomFieldsHelper
- include OpPrimer::ComponentHelpers
-
- def initialize(project_custom_field:, project_custom_field_values:)
- super
-
- @project_custom_field = project_custom_field
- @project_custom_field_values = project_custom_field_values
- end
-
- private
-
- def not_set?
- @project_custom_field_values.empty? || @project_custom_field_values.all? { |cf_value| cf_value.value.blank? }
- end
-
- def render_value
- case @project_custom_field.field_format
- when "link"
- render_link
- when "text"
- render_long_text
- when "user"
- render_user
- else
- render(Primer::Beta::Text.new) do
- @project_custom_field_values&.map do |cf_value|
- format_value(cf_value.value, @project_custom_field)
- end&.join(", ")
- end
- end
- end
-
- def render_long_text
- render OpenProject::Common::AttributeComponent.new("dialog-cf-#{@project_custom_field.id}",
- @project_custom_field.name,
- @project_custom_field_values&.first&.value,
- lines: 3)
- end
-
- def render_user
- if @project_custom_field.multi_value?
- flex_layout do |avatar_container|
- @project_custom_field_values&.each do |cf_value|
- avatar_container.with_row do
- render_avatar(cf_value.typed_value)
- end
- end
- end
- else
- render_avatar(@project_custom_field_values&.first&.typed_value)
- end
- end
-
- def render_avatar(user)
- render(Users::AvatarComponent.new(user:, size: :mini))
- end
-
- def render_link
- href = @project_custom_field_values&.first&.value
- link = Addressable::URI.parse(href)
- return href unless link
-
- target = link.host == Setting.host_without_protocol ? "_top" : "_blank"
- render(Primer::Beta::Link.new(href:, rel: "noopener noreferrer", target:)) do
- href
- end
- end
- end
- end
- end
-end
diff --git a/modules/overviews/app/components/project_life_cycles/sections/show_component.html.erb b/modules/overviews/app/components/project_life_cycles/sections/show_component.html.erb
deleted file mode 100644
index 4823da14107..00000000000
--- a/modules/overviews/app/components/project_life_cycles/sections/show_component.html.erb
+++ /dev/null
@@ -1,38 +0,0 @@
-<%=
- component_wrapper do
- render(
- Primer::OpenProject::SidePanel::Section.new(
- classes: "op-project-life-cyle-section-container",
- test_selector: "project-life-cycle-section"
- )
- ) do |section|
- section.with_title { t("label_life_cycle_step_plural") }
-
- if allowed_to_edit?
- section.with_action_icon(
- icon: :pencil,
- tag: :a,
- href: project_life_cycles_dialog_path(project_id: @project.id),
- data: {
- controller: "async-dialog"
- },
- test_selector: "project-life-cycles-edit-button",
- aria: { label: I18n.t(:label_edit) }
- )
- end
-
- flex_layout do |details_container|
- @life_cycle_steps.each_with_index do |life_cycle_step, i|
- margin = i == @life_cycle_steps.size - 1 ? 0 : 3
- details_container.with_row(mb: margin) do
- render(
- ProjectLifeCycles::Sections::ProjectLifeCycles::ShowComponent.new(
- life_cycle_step
- )
- )
- end
- end
- end
- end
- end
-%>
diff --git a/modules/overviews/app/components/project_life_cycles/side_panel_component.rb b/modules/overviews/app/components/project_life_cycles/side_panel_component.rb
deleted file mode 100644
index d837969ffa3..00000000000
--- a/modules/overviews/app/components/project_life_cycles/side_panel_component.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-module ProjectLifeCycles
- class SidePanelComponent < ApplicationComponent
- include ApplicationHelper
- include OpPrimer::ComponentHelpers
- include OpTurbo::Streamable
-
- def initialize(project:)
- super
-
- @project = project
- end
- end
-end
diff --git a/modules/overviews/app/controllers/overviews/overviews_controller.rb b/modules/overviews/app/controllers/overviews/overviews_controller.rb
index 976d38d23ef..bc08e436faf 100644
--- a/modules/overviews/app/controllers/overviews/overviews_controller.rb
+++ b/modules/overviews/app/controllers/overviews/overviews_controller.rb
@@ -46,7 +46,7 @@ module ::Overviews
def project_custom_field_section_dialog
respond_with_dialog(
- ProjectCustomFields::Sections::EditDialogComponent.new(
+ Overviews::ProjectCustomFields::EditDialogComponent.new(
project: @project,
project_custom_field_section: find_project_custom_field_section
)
@@ -80,46 +80,6 @@ module ::Overviews
render :project_life_cycles_sidebar, layout: false
end
- def project_life_cycles_dialog
- respond_with_dialog(
- ProjectLifeCycles::Sections::EditDialogComponent.new(@project)
- )
- end
-
- def project_life_cycles_form
- service_call = ::ProjectLifeCycleSteps::PreviewAttributesService
- .new(user: current_user,
- model: @project,
- contract_class: ProjectLifeCycleSteps::UpdateContract)
- .call(permitted_params.project_phases)
-
- update_via_turbo_stream(
- component: ProjectLifeCycles::Sections::EditComponent.new(service_call.result),
- method: "morph"
- )
- # TODO: :unprocessable_entity is not nice, change the dialog logic to accept :ok
- # without dismissing the dialog, alternatively use turbo frames instead of streams.
- respond_to_with_turbo_streams(status: :unprocessable_entity)
- end
-
- def update_project_life_cycles
- service_call = ::ProjectLifeCycleSteps::UpdateService
- .new(user: current_user, model: @project)
- .call(permitted_params.project_phases)
-
- if service_call.success?
- update_via_turbo_stream(
- component: ProjectLifeCycles::SidePanelComponent.new(project: @project)
- )
- else
- update_via_turbo_stream(
- component: ProjectLifeCycles::Sections::EditComponent.new(service_call.result)
- )
- end
-
- respond_to_with_turbo_streams(status: service_call.success? ? :ok : :unprocessable_entity)
- end
-
def jump_to_project_menu_item
if params[:jump]
# try to redirect to the requested menu item
@@ -145,7 +105,7 @@ module ::Overviews
def handle_errors(project_with_errors, section)
update_via_turbo_stream(
- component: ProjectCustomFields::Sections::EditComponent.new(
+ component: Overviews::ProjectCustomFields::EditComponent.new(
project: project_with_errors,
project_custom_field_section: section
)
@@ -154,7 +114,7 @@ module ::Overviews
def update_sidebar_component
update_via_turbo_stream(
- component: ProjectCustomFields::SidePanelComponent.new(project: @project)
+ component: Overviews::ProjectCustomFields::SidePanelComponent.new(project: @project)
)
end
end
diff --git a/modules/overviews/app/controllers/overviews/project_phases_controller.rb b/modules/overviews/app/controllers/overviews/project_phases_controller.rb
new file mode 100644
index 00000000000..908c0917d54
--- /dev/null
+++ b/modules/overviews/app/controllers/overviews/project_phases_controller.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+# -- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+# ++
+
+module ::Overviews
+ class ProjectPhasesController < ::ApplicationController
+ include OpTurbo::ComponentStream
+ include OpTurbo::DialogStreamHelper
+
+ before_action :find_project_phase_and_project
+ before_action :authorize
+
+ def edit
+ respond_with_dialog(Overviews::ProjectPhases::EditDialogComponent.new(@project_phase))
+ end
+
+ def preview
+ service_call = ::ProjectLifeCycleSteps::SetAttributesService
+ .new(user: current_user,
+ model: @project_phase,
+ contract_class: ProjectLifeCycleSteps::UpdateContract)
+ .call(permitted_params.project_phase)
+
+ update_via_turbo_stream(
+ component: Overviews::ProjectPhases::EditComponent.new(service_call.result),
+ method: "morph"
+ )
+ # TODO: :unprocessable_entity is not nice, change the dialog logic to accept :ok
+ # without dismissing the dialog, alternatively use turbo frames instead of streams.
+ respond_to_with_turbo_streams(status: :unprocessable_entity)
+ end
+
+ def update
+ service_call = ::ProjectLifeCycleSteps::UpdateService
+ .new(user: current_user, model: @project_phase)
+ .call(permitted_params.project_phase)
+
+ component, status =
+ if service_call.success?
+ [Overviews::ProjectPhases::SidePanelComponent.new(project: @project), :ok]
+ else
+ [Overviews::ProjectPhases::EditComponent.new(service_call.result), :unprocessable_entity]
+ end
+
+ update_via_turbo_stream(component:)
+ respond_to_with_turbo_streams(status:)
+ end
+
+ private
+
+ def find_project_phase_and_project
+ @project_phase = Project::Phase.where(active: true)
+ .eager_load(:definition, :project)
+ .find(params[:id])
+ @project = @project_phase.project
+ end
+ end
+end
diff --git a/modules/overviews/app/views/overviews/overviews/project_custom_fields_sidebar.html.erb b/modules/overviews/app/views/overviews/overviews/project_custom_fields_sidebar.html.erb
index 63d2b46f856..b04c17adec2 100644
--- a/modules/overviews/app/views/overviews/overviews/project_custom_fields_sidebar.html.erb
+++ b/modules/overviews/app/views/overviews/overviews/project_custom_fields_sidebar.html.erb
@@ -27,5 +27,5 @@ See COPYRIGHT and LICENSE files for more details.
++#%>
<%= content_tag("turbo-frame", id: "project-custom-fields-sidebar") do %>
- <%= render(ProjectCustomFields::SidePanelComponent.new(project: @project)) %>
+ <%= render(Overviews::ProjectCustomFields::SidePanelComponent.new(project: @project)) %>
<% end %>
diff --git a/modules/overviews/app/views/overviews/overviews/project_life_cycles_sidebar.html.erb b/modules/overviews/app/views/overviews/overviews/project_life_cycles_sidebar.html.erb
index 1b05538bb3c..5821f23a127 100644
--- a/modules/overviews/app/views/overviews/overviews/project_life_cycles_sidebar.html.erb
+++ b/modules/overviews/app/views/overviews/overviews/project_life_cycles_sidebar.html.erb
@@ -27,5 +27,5 @@ See COPYRIGHT and LICENSE files for more details.
++#%>
<%= content_tag("turbo-frame", id: "project-life-cycles-sidebar") do %>
- <%= render(ProjectLifeCycles::SidePanelComponent.new(project: @project)) %>
+ <%= render(Overviews::ProjectPhases::SidePanelComponent.new(project: @project)) %>
<% end %>
diff --git a/modules/overviews/config/routes.rb b/modules/overviews/config/routes.rb
index c6a9a0b1b44..c800c2e7cdf 100644
--- a/modules/overviews/config/routes.rb
+++ b/modules/overviews/config/routes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
Rails.application.routes.draw do
constraints(project_id: Regexp.new("(?!(#{Project::RESERVED_IDENTIFIERS.join('|')})$)(\\w|-)+"), format: :html) do
get "projects/:project_id",
@@ -10,13 +12,13 @@ Rails.application.routes.draw do
put "projects/:project_id/update_project_custom_values/:section_id", to: "overviews/overviews#update_project_custom_values",
as: :update_project_custom_values
- get "projects/:project_id/project_life_cycles_sidebar", to: "overviews/overviews#project_life_cycles_sidebar",
- as: :project_life_cycles_sidebar
- get "projects/:project_id/project_life_cycles_dialog", to: "overviews/overviews#project_life_cycles_dialog",
- as: :project_life_cycles_dialog
- put "projects/:project_id/project_life_cycles_form", to: "overviews/overviews#project_life_cycles_form",
- as: :project_life_cycles_form
- put "projects/:project_id/update_project_life_cycles", to: "overviews/overviews#update_project_life_cycles",
- as: :update_project_life_cycles
+ get "projects/:project_id/project_life_cycles_sidebar",
+ to: "overviews/overviews#project_life_cycles_sidebar", as: :project_life_cycles_sidebar
+ end
+
+ resources :project_phases, controller: "overviews/project_phases", only: %i[edit update] do
+ member do
+ put :preview
+ end
end
end
diff --git a/modules/overviews/lib/overviews/engine.rb b/modules/overviews/lib/overviews/engine.rb
index 3e73ddfebeb..fef3453e7cc 100644
--- a/modules/overviews/lib/overviews/engine.rb
+++ b/modules/overviews/lib/overviews/engine.rb
@@ -72,9 +72,9 @@ module Overviews
OpenProject::AccessControl.permission(:edit_project_phases)
.controller_actions
.push(
- "overviews/overviews/project_life_cycles_dialog",
- "overviews/overviews/project_life_cycles_form",
- "overviews/overviews/update_project_life_cycles"
+ "overviews/project_phases/edit",
+ "overviews/project_phases/preview",
+ "overviews/project_phases/update"
)
OpenProject::AccessControl.permission(:view_work_packages)
diff --git a/modules/reporting/config/locales/crowdin/ro.yml b/modules/reporting/config/locales/crowdin/ro.yml
index 1c664d6edf7..459bfcc7f5a 100644
--- a/modules/reporting/config/locales/crowdin/ro.yml
+++ b/modules/reporting/config/locales/crowdin/ro.yml
@@ -70,7 +70,7 @@ ro:
label_filter: "Filtrează"
label_filter_add: "Adaugă filtru"
label_filter_plural: "Filtre"
- label_group_by: "Grupează după"
+ label_group_by: "Grupare după"
label_group_by_add: "Adaugă atributul Grupează-după"
label_inactive: "Inactiv"
label_no: "Nu"
diff --git a/modules/reporting/config/locales/crowdin/zh-TW.yml b/modules/reporting/config/locales/crowdin/zh-TW.yml
index 160d6764df0..5885d7e25a2 100644
--- a/modules/reporting/config/locales/crowdin/zh-TW.yml
+++ b/modules/reporting/config/locales/crowdin/zh-TW.yml
@@ -53,7 +53,7 @@ zh-TW:
label_money: "現金價值"
label_month_reporting: "月"
label_new_report: "新建成本報表"
- label_open: "開啟"
+ label_open: "開啟中"
label_operator: "操作員"
label_private_report_plural: "私密成本報告"
label_progress_bar_explanation: "產生報告中..."
@@ -70,7 +70,7 @@ zh-TW:
label_filter: "篩選條件"
label_filter_add: "新增篩選條件"
label_filter_plural: "篩選條件"
- label_group_by: "分類"
+ label_group_by: "分組依據"
label_group_by_add: "新增分組依據屬性"
label_inactive: "«不活動»"
label_no: "否"
diff --git a/modules/reporting/spec/features/export_cost_report_spec.rb b/modules/reporting/spec/features/export_cost_report_spec.rb
index a687fdd7cc4..09dc85507bf 100644
--- a/modules/reporting/spec/features/export_cost_report_spec.rb
+++ b/modules/reporting/spec/features/export_cost_report_spec.rb
@@ -31,7 +31,7 @@
require_relative "../spec_helper"
require_relative "support/pages/cost_report_page"
-RSpec.describe "Cost reports XLS export", :js, with_flag: { track_start_and_end_times_for_time_entries: true } do
+RSpec.describe "Cost reports XLS export", :js do
shared_let(:project) { create(:project) }
shared_let(:user) { create(:admin) }
shared_let(:cost_type) { create(:cost_type, name: "Post-war", unit: "cap", unit_plural: "caps") }
diff --git a/modules/reporting/spec/features/time_entries_spec.rb b/modules/reporting/spec/features/time_entries_spec.rb
index d7867e33d44..59cf2e54764 100644
--- a/modules/reporting/spec/features/time_entries_spec.rb
+++ b/modules/reporting/spec/features/time_entries_spec.rb
@@ -4,8 +4,7 @@ require "spec_helper"
require_relative "support/pages/cost_report_page"
require_relative "support/components/cost_reports_base_table"
-RSpec.describe "Cost report showing time entries with start & end times", :js,
- with_flag: { track_start_and_end_times_for_time_entries: true } do
+RSpec.describe "Cost report showing time entries with start & end times", :js do
shared_let(:project) { create(:project) }
shared_let(:user) { create(:admin) }
shared_let(:work_package) { create(:work_package, project:) }
diff --git a/modules/overviews/app/components/project_life_cycles/sections/project_life_cycles/show_component.rb b/modules/storages/app/common/storages/peripherals/connection_validators/base_connection_validator.rb
similarity index 61%
rename from modules/overviews/app/components/project_life_cycles/sections/project_life_cycles/show_component.rb
rename to modules/storages/app/common/storages/peripherals/connection_validators/base_connection_validator.rb
index d1e6f09be73..7846ee10823 100644
--- a/modules/overviews/app/components/project_life_cycles/sections/project_life_cycles/show_component.rb
+++ b/modules/storages/app/common/storages/peripherals/connection_validators/base_connection_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -26,30 +28,35 @@
# See COPYRIGHT and LICENSE files for more details.
#++
-module ProjectLifeCycles
- module Sections
- module ProjectLifeCycles
- class ShowComponent < ApplicationComponent
- include ApplicationHelper
- include OpPrimer::ComponentHelpers
+module Storages
+ module Peripherals
+ module ConnectionValidators
+ class BaseConnectionValidator
+ class << self
+ def validation_groups
+ @validation_groups ||= {}
+ end
+
+ def register_group(group_name, klass, precondition: ->(*) { true })
+ validation_groups[group_name] = { klass:, precondition: }
+ end
+ end
+
+ def initialize(storage)
+ @storage = storage
+ end
+
+ def call
+ validation_groups.each_with_object(ValidatorResult.new) do |(key, group_metadata), result|
+ if group_metadata[:precondition].call(@storage, result)
+ result.add_group_result(key, group_metadata[:klass].call(@storage))
+ end
+ end
+ end
private
- def not_set?
- model.not_set?
- end
-
- def icon
- :"op-phase"
- end
-
- def icon_color_class
- helpers.hl_inline_class("project_phase_definition", model.definition)
- end
-
- def text
- model.name
- end
+ def validation_groups = self.class.validation_groups
end
end
end
diff --git a/modules/storages/app/common/storages/peripherals/connection_validators/base_validator_group.rb b/modules/storages/app/common/storages/peripherals/connection_validators/base_validator_group.rb
index 36e5501170a..d6e6c904cfe 100644
--- a/modules/storages/app/common/storages/peripherals/connection_validators/base_validator_group.rb
+++ b/modules/storages/app/common/storages/peripherals/connection_validators/base_validator_group.rb
@@ -34,6 +34,10 @@ module Storages
class BaseValidatorGroup
include TaggedLogging
+ def self.call(storage)
+ new(storage).call
+ end
+
def initialize(storage)
@storage = storage
@results = ValidationGroupResult.new
diff --git a/modules/storages/app/common/storages/peripherals/connection_validators/nextcloud/authentication_validator.rb b/modules/storages/app/common/storages/peripherals/connection_validators/nextcloud/authentication_validator.rb
index 511c53dceb0..07185f8b8f0 100644
--- a/modules/storages/app/common/storages/peripherals/connection_validators/nextcloud/authentication_validator.rb
+++ b/modules/storages/app/common/storages/peripherals/connection_validators/nextcloud/authentication_validator.rb
@@ -52,7 +52,7 @@ module Storages
end
def oauth_token
- if OAuthClientToken.where(user: @user, oauth_client: @storage.oauth_client).any?
+ if OAuthClientToken.for_user_and_client(@user, @storage.oauth_client).exists?
pass_check(:existing_token)
else
warn_check(:existing_token, message(:oauth_token_missing), halt_validation: true)
diff --git a/modules/storages/app/common/storages/peripherals/connection_validators/nextcloud_validator.rb b/modules/storages/app/common/storages/peripherals/connection_validators/nextcloud_validator.rb
index 7c75def5754..20ec6c4e7ae 100644
--- a/modules/storages/app/common/storages/peripherals/connection_validators/nextcloud_validator.rb
+++ b/modules/storages/app/common/storages/peripherals/connection_validators/nextcloud_validator.rb
@@ -31,41 +31,15 @@
module Storages
module Peripherals
module ConnectionValidators
- class NextcloudValidator
- # Class Level interface will be moved to a superclass/mixin once we do the OneDrive port
- class << self
- def validation_groups
- @validation_groups ||= {}
- end
-
- def register_group(group_name, klass, when: ->(*) { true })
- validation_groups[group_name] = { klass:, when: }
- end
- end
+ class NextcloudValidator < BaseConnectionValidator
register_group :base_configuration, Nextcloud::StorageConfigurationValidator
register_group :authentication, Nextcloud::AuthenticationValidator,
- when: ->(_, result) { result.group(:base_configuration).non_failure? }
+ precondition: ->(_, result) { result.group(:base_configuration).non_failure? }
register_group :ampf_configuration, Nextcloud::AmpfConfigurationValidator,
- when: ->(storage, result) {
+ precondition: ->(storage, result) {
result.group(:base_configuration).non_failure? && storage.automatic_management_enabled?
}
-
- def initialize(storage:)
- @storage = storage
- end
-
- def validate
- validation_groups.each_with_object(ValidatorResult.new) do |(key, group_metadata), result|
- if group_metadata[:when].call(@storage, result)
- result.add_group_result(key, group_metadata[:klass].new(@storage).call)
- end
- end
- end
-
- private
-
- def validation_groups = self.class.validation_groups
end
end
end
diff --git a/modules/storages/app/common/storages/peripherals/connection_validators/one_drive/ampf_configuration_validator.rb b/modules/storages/app/common/storages/peripherals/connection_validators/one_drive/ampf_configuration_validator.rb
new file mode 100644
index 00000000000..d6448d73895
--- /dev/null
+++ b/modules/storages/app/common/storages/peripherals/connection_validators/one_drive/ampf_configuration_validator.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+module Storages
+ module Peripherals
+ module ConnectionValidators
+ module OneDrive
+ class AmpfConfigurationValidator < BaseValidatorGroup
+ TEST_FOLDER_NAME = "ConnectionValidatorFolder"
+
+ private
+
+ def validate
+ register_checks :client_folder_creation, :client_folder_removal, :drive_contents
+
+ client_permissions
+ unexpected_content
+ end
+
+ def unexpected_content
+ unexpected_files = files_query.on_failure { fail_check(:drive_content, message(:unknown_error)) }
+ .result.files.reject { managed_project_folder_ids.include?(it.id) }
+
+ if unexpected_files.empty?
+ pass_check(:drive_contents)
+ else
+ log_extraneous_files(unexpected_files)
+ warn_check(:drive_contents, message("one_drive.unexpected_content"))
+ end
+ end
+
+ # Testing setting permissions and checking permission inheritance would be great
+ # but there are some challenges to it. We need to figure out a good way to go about this
+ # 2025-04-08 @mereghost
+ def client_permissions
+ folder = create_folder.result
+ delete_folder(folder)
+ end
+
+ def delete_folder(folder)
+ Registry["one_drive.commands.delete_folder"]
+ .call(storage: @storage, auth_strategy:, location: folder.id)
+ .on_failure { fail_check(:client_folder_removal, message("one_drive.client_cant_delete_folder")) }
+ .on_success { pass_check(:client_folder_removal) }
+ end
+
+ def create_folder
+ Registry["one_drive.commands.create_folder"]
+ .call(storage: @storage, auth_strategy:, folder_name: TEST_FOLDER_NAME, parent_location: ParentFolder.root)
+ .on_success { pass_check(:client_folder_creation) }
+ .on_failure do
+ reason = it.result == :already_exists ? :existing_test_folder : :client_write_permission_missing
+
+ fail_check(:client_folder_creation, message("one_drive.#{reason}", folder_name: TEST_FOLDER_NAME))
+ end
+ end
+
+ def log_extraneous_files(unexpected_files)
+ file_representation = unexpected_files.map do |file|
+ "Name: #{file.name}, ID: #{file.id}, Location: #{file.location}"
+ end
+
+ warn "Unexpected files/folder found in group folder:\n\t#{file_representation.join("\n\t")}"
+ end
+
+ def managed_project_folder_ids
+ @managed_project_folder_ids ||= ProjectStorage.automatic.where(storage: @storage)
+ .pluck(:project_folder_id).to_set
+ end
+
+ def files_query = Registry["one_drive.queries.files"].call(storage: @storage, auth_strategy:, folder: ParentFolder.root)
+ def auth_strategy = Registry["one_drive.authentication.userless"].call
+ end
+ end
+ end
+ end
+end
diff --git a/modules/meeting/lib/api/v3/meeting_contents/meeting_content_representer.rb b/modules/storages/app/common/storages/peripherals/connection_validators/one_drive/authentication_validator.rb
similarity index 52%
rename from modules/meeting/lib/api/v3/meeting_contents/meeting_content_representer.rb
rename to modules/storages/app/common/storages/peripherals/connection_validators/one_drive/authentication_validator.rb
index 02d7341e290..17256dacaad 100644
--- a/modules/meeting/lib/api/v3/meeting_contents/meeting_content_representer.rb
+++ b/modules/storages/app/common/storages/peripherals/connection_validators/one_drive/authentication_validator.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -27,30 +28,42 @@
# See COPYRIGHT and LICENSE files for more details.
#++
-module API
- module V3
- module MeetingContents
- class MeetingContentRepresenter < ::API::Decorators::Single
- include API::Decorators::LinkedResource
- include API::Caching::CachedRepresenter
- include ::API::V3::Attachments::AttachableRepresenterMixin
+module Storages
+ module Peripherals
+ module ConnectionValidators
+ module OneDrive
+ class AuthenticationValidator < BaseValidatorGroup
+ def initialize(storage)
+ super
+ @user = User.current
+ end
- self_link title_getter: ->(*) {}
+ private
- property :id
+ def validate
+ register_checks(:existing_token, :user_bound_request)
- associated_resource :project,
- link: ->(*) do
- next unless represented.project.present?
+ oauth_token
+ user_bound_request
+ end
- {
- href: api_v3_paths.project(represented.project.id),
- title: represented.project.name
- }
- end
+ def oauth_token
+ if OAuthClientToken.for_user_and_client(@user, @storage.oauth_client).exists?
+ pass_check(:existing_token)
+ else
+ warn_check(:existing_token, message(:oauth_token_missing), halt_validation: true)
+ end
+ end
- def _type
- "MeetingContent"
+ def user_bound_request
+ Registry["one_drive.queries.user"].call(storage: @storage, auth_strategy:).on_failure do
+ fail_check(:user_bound_request, message("oauth_request_#{it.result}"))
+ end
+
+ pass_check(:user_bound_request)
+ end
+
+ def auth_strategy = Registry["one_drive.authentication.user_bound"].call(storage: @storage, user: @user)
end
end
end
diff --git a/modules/storages/app/common/storages/peripherals/connection_validators/one_drive/storage_configuration_validator.rb b/modules/storages/app/common/storages/peripherals/connection_validators/one_drive/storage_configuration_validator.rb
new file mode 100644
index 00000000000..474f63d5b8e
--- /dev/null
+++ b/modules/storages/app/common/storages/peripherals/connection_validators/one_drive/storage_configuration_validator.rb
@@ -0,0 +1,141 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+module Storages
+ module Peripherals
+ module ConnectionValidators
+ module OneDrive
+ class StorageConfigurationValidator < BaseValidatorGroup
+ using ServiceResultRefinements
+
+ private
+
+ def validate
+ register_checks :storage_configured, :diagnostic_request, :tenant_id, :client_secret, :client_id,
+ :drive_id_format, :drive_id_not_found
+
+ storage_configuration_status
+ diagnostic_request
+ check_tenant_id
+ check_client_secret
+ check_client_id
+ malformed_drive_id
+ drive_not_found
+ end
+
+ def malformed_drive_id
+ return pass_check(:drive_id_format) if query_result.success?
+
+ if error_payload.dig(:error, :code) == "invalidRequest"
+ fail_check(:drive_id_format, message("one_drive.drive_id_wrong"))
+ else
+ pass_check(:drive_id_format)
+ end
+ end
+
+ def drive_not_found
+ if query_result.result == :not_found
+ fail_check(:drive_id_not_found, message("one_drive.drive_id_not_found"))
+ else
+ pass_check(:drive_id_not_found)
+ end
+ end
+
+ def check_tenant_id
+ return pass_check(:tenant_id) if query_result.success?
+
+ tenant_id_regex = /tenant (?:identifier )?'#{@storage.tenant_id}' (?:not found|is neither)/i
+
+ if error_payload[:error] == "invalid_request" && error_payload[:error_description].match?(tenant_id_regex)
+ fail_check(:tenant_id, message(:tenant_id_wrong))
+ else
+ pass_check(:tenant_id)
+ end
+ end
+
+ def check_client_id
+ return pass_check(:client_id) if query_result.success?
+
+ if error_payload[:error] == "unauthorized_client"
+ fail_check(:client_id, message(:client_id_wrong))
+ else
+ pass_check(:client_id)
+ end
+ end
+
+ def check_client_secret
+ return pass_check(:client_secret) if query_result.success?
+
+ if error_payload[:error] == "invalid_client"
+ fail_check(:client_secret, message(:client_secret_wrong))
+ else
+ pass_check(:client_secret)
+ end
+ end
+
+ def diagnostic_request
+ if query_result.result == :error
+ error "Connection validation failed with unknown error:\n" \
+ "\tstorage: ##{@storage.id} #{@storage.name}\n" \
+ "\tstatus: #{query_result.result}\n" \
+ "\tresponse: #{query_result.error_payload}"
+
+ fail_check(:diagnostic_request, message(:unknown_error))
+ else
+ pass_check :diagnostic_request
+ end
+ end
+
+ def storage_configuration_status
+ if @storage.configured?
+ pass_check(:storage_configured)
+ else
+ fail_check(:storage_configured, message(:not_configured))
+ end
+ end
+
+ def query_result
+ @query_result ||= Registry.resolve("#{@storage}.queries.files")
+ .call(storage: @storage, auth_strategy:, folder: ParentFolder.root)
+ end
+
+ def auth_strategy = Registry.resolve("one_drive.authentication.userless").call
+
+ def error_payload
+ return {} if query_result.success?
+ return query_result.error_payload if query_result.error_payload.is_a?(Hash)
+
+ @error_payload ||= MultiJson.load(query_result.error_payload, symbolize_keys: true)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/modules/meeting/app/services/meeting_contents/set_attributes_service.rb b/modules/storages/app/common/storages/peripherals/connection_validators/one_drive_validator.rb
similarity index 65%
rename from modules/meeting/app/services/meeting_contents/set_attributes_service.rb
rename to modules/storages/app/common/storages/peripherals/connection_validators/one_drive_validator.rb
index 67fbe5cddc2..c280bae9085 100644
--- a/modules/meeting/app/services/meeting_contents/set_attributes_service.rb
+++ b/modules/storages/app/common/storages/peripherals/connection_validators/one_drive_validator.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -27,13 +28,17 @@
# See COPYRIGHT and LICENSE files for more details.
#++
-module MeetingContents
- class SetAttributesService < ::BaseServices::SetAttributes
- include Attachments::SetReplacements
-
- def set_default_attributes(_params)
- model.change_by_system do
- model.author = user if model.author.nil?
+module Storages
+ module Peripherals
+ module ConnectionValidators
+ class OneDriveValidator < BaseConnectionValidator
+ register_group :base_configuration, OneDrive::StorageConfigurationValidator
+ register_group :authentication, OneDrive::AuthenticationValidator,
+ precondition: ->(_, result) { result.group(:base_configuration).non_failure? }
+ register_group :ampf_configuration, OneDrive::AmpfConfigurationValidator,
+ precondition: ->(storage, result) {
+ result.group(:base_configuration).non_failure? && storage.automatic_management_enabled?
+ }
end
end
end
diff --git a/modules/storages/app/common/storages/peripherals/nextcloud_registry.rb b/modules/storages/app/common/storages/peripherals/nextcloud_registry.rb
index b03004d6b96..4ed520a72cd 100644
--- a/modules/storages/app/common/storages/peripherals/nextcloud_registry.rb
+++ b/modules/storages/app/common/storages/peripherals/nextcloud_registry.rb
@@ -59,32 +59,36 @@ module Storages
namespace("components") do
namespace("forms") do
- register(:automatically_managed_folders, ::Storages::Admin::Forms::AutomaticallyManagedProjectFoldersFormComponent)
- register(:general_information, ::Storages::Admin::Forms::GeneralInfoFormComponent)
- register(:storage_audience, ::Storages::Admin::Forms::StorageAudienceFormComponent)
- register(:oauth_application, ::Storages::Admin::OAuthApplicationInfoCopyComponent)
- register(:oauth_client, ::Storages::Admin::Forms::OAuthClientFormComponent)
+ register(:automatically_managed_folders, Admin::Forms::AutomaticallyManagedProjectFoldersFormComponent)
+ register(:general_information, Admin::Forms::GeneralInfoFormComponent)
+ register(:storage_audience, Admin::Forms::StorageAudienceFormComponent)
+ register(:oauth_application, Admin::OAuthApplicationInfoCopyComponent)
+ register(:oauth_client, Admin::Forms::OAuthClientFormComponent)
end
register(:setup_wizard, NextcloudStorageWizard)
- register(:automatically_managed_folders, ::Storages::Admin::AutomaticallyManagedProjectFoldersInfoComponent)
- register(:general_information, ::Storages::Admin::GeneralInfoComponent)
- register(:storage_audience, ::Storages::Admin::StorageAudienceInfoComponent)
- register(:oauth_application, ::Storages::Admin::OAuthApplicationInfoComponent)
- register(:oauth_client, ::Storages::Admin::OAuthClientInfoComponent)
+ register(:automatically_managed_folders, Admin::AutomaticallyManagedProjectFoldersInfoComponent)
+ register(:general_information, Admin::GeneralInfoComponent)
+ register(:storage_audience, Admin::StorageAudienceInfoComponent)
+ register(:oauth_application, Admin::OAuthApplicationInfoComponent)
+ register(:oauth_client, Admin::OAuthClientInfoComponent)
end
namespace("contracts") do
- register(:storage, ::Storages::Storages::NextcloudContract)
- register(:general_information, ::Storages::Storages::NextcloudGeneralInformationContract)
- register(:storage_audience, ::Storages::Storages::NextcloudAudienceContract)
+ register(:storage, Storages::NextcloudContract)
+ register(:general_information, Storages::NextcloudGeneralInformationContract)
+ register(:storage_audience, Storages::NextcloudAudienceContract)
end
namespace("models") do
register(:managed_folder_identifier, ManagedFolderIdentifier::Nextcloud)
end
+ namespace("validators") do
+ register(:connection, ConnectionValidators::NextcloudValidator)
+ end
+
namespace("authentication") do
register(:userless, StorageInteraction::AuthenticationStrategies::NextcloudStrategies::UserLess, call: false)
register(:user_bound, StorageInteraction::AuthenticationStrategies::NextcloudStrategies::UserBound)
diff --git a/modules/storages/app/common/storages/peripherals/one_drive_registry.rb b/modules/storages/app/common/storages/peripherals/one_drive_registry.rb
index 3e561d31c45..c50074e3109 100644
--- a/modules/storages/app/common/storages/peripherals/one_drive_registry.rb
+++ b/modules/storages/app/common/storages/peripherals/one_drive_registry.rb
@@ -53,29 +53,33 @@ module Storages
namespace("components") do
namespace("forms") do
- register(:access_management, ::Storages::Admin::Forms::AccessManagementFormComponent)
- register(:general_information, ::Storages::Admin::Forms::GeneralInfoFormComponent)
- register(:oauth_client, ::Storages::Admin::Forms::OAuthClientFormComponent)
- register(:redirect_uri, ::Storages::Admin::Forms::RedirectUriFormComponent)
+ register(:access_management, Admin::Forms::AccessManagementFormComponent)
+ register(:general_information, Admin::Forms::GeneralInfoFormComponent)
+ register(:oauth_client, Admin::Forms::OAuthClientFormComponent)
+ register(:redirect_uri, Admin::Forms::RedirectUriFormComponent)
end
register(:setup_wizard, OneDriveStorageWizard)
- register(:access_management, ::Storages::Admin::AccessManagementComponent)
- register(:general_information, ::Storages::Admin::GeneralInfoComponent)
- register(:oauth_client, ::Storages::Admin::OAuthClientInfoComponent)
- register(:redirect_uri, ::Storages::Admin::RedirectUriComponent)
+ register(:access_management, Admin::AccessManagementComponent)
+ register(:general_information, Admin::GeneralInfoComponent)
+ register(:oauth_client, Admin::OAuthClientInfoComponent)
+ register(:redirect_uri, Admin::RedirectUriComponent)
end
namespace("contracts") do
- register(:storage, ::Storages::Storages::OneDriveContract)
- register(:general_information, ::Storages::Storages::OneDriveContract)
+ register(:storage, Storages::OneDriveContract)
+ register(:general_information, Storages::OneDriveContract)
end
namespace("models") do
register(:managed_folder_identifier, ManagedFolderIdentifier::OneDrive)
end
+ namespace("validations") do
+ register(:connection, ConnectionValidators::OneDriveValidator)
+ end
+
namespace("authentication") do
register(:userless, StorageInteraction::AuthenticationStrategies::OneDriveStrategies::UserLess, call: false)
register(:user_bound, StorageInteraction::AuthenticationStrategies::OneDriveStrategies::UserBound)
diff --git a/modules/storages/app/common/storages/peripherals/storage_interaction/authentication_strategies/strategy.rb b/modules/storages/app/common/storages/peripherals/storage_interaction/authentication_strategies/strategy.rb
index fe8098ad1bf..0e2680a4758 100644
--- a/modules/storages/app/common/storages/peripherals/storage_interaction/authentication_strategies/strategy.rb
+++ b/modules/storages/app/common/storages/peripherals/storage_interaction/authentication_strategies/strategy.rb
@@ -56,6 +56,14 @@ module Storages
@token = token
self
end
+
+ def ==(other)
+ @key == other.key && @use_cache == other.use_cache && @user == other.user && @token == other.token
+ end
+
+ def hash
+ [@key, @use_cache, @user, @token].hash
+ end
end
end
end
diff --git a/modules/storages/app/common/storages/peripherals/storage_parent_folder_extractor.rb b/modules/storages/app/common/storages/peripherals/storage_parent_folder_extractor.rb
index a5fe0f728cf..c8b2d8581ba 100644
--- a/modules/storages/app/common/storages/peripherals/storage_parent_folder_extractor.rb
+++ b/modules/storages/app/common/storages/peripherals/storage_parent_folder_extractor.rb
@@ -33,6 +33,8 @@ module Storages
ParentFolder = Data.define(:path) do
delegate :split, :empty?, to: :path
+ def self.root = new("/")
+
def root?
path == "/"
end
diff --git a/modules/storages/app/components/storages/admin/storages/projects_storage_form_modal_component.html.erb b/modules/storages/app/components/storages/admin/storages/projects_storage_form_modal_component.html.erb
index fd3e5c3201e..5c14bfa3dde 100644
--- a/modules/storages/app/components/storages/admin/storages/projects_storage_form_modal_component.html.erb
+++ b/modules/storages/app/components/storages/admin/storages/projects_storage_form_modal_component.html.erb
@@ -85,8 +85,8 @@ See COPYRIGHT and LICENSE files for more details.
concat(
render(Primer::Alpha::Dialog::Footer.new(show_divider: false)) do
- concat(render(Primer::ButtonComponent.new(data: { "close-dialog-id": dialog_id })) { cancel_button_text })
- concat(render(Primer::ButtonComponent.new(scheme: :primary, type: :submit)) { submit_button_text })
+ concat(render(Primer::Beta::Button.new(data: { "close-dialog-id": dialog_id })) { cancel_button_text })
+ concat(render(Primer::Beta::Button.new(scheme: :primary, type: :submit)) { submit_button_text })
end
)
end
diff --git a/modules/storages/app/components/storages/open_project_storage_modal_component.html.erb b/modules/storages/app/components/storages/open_project_storage_modal_component.html.erb
index 51001781bd3..6ca4884f067 100644
--- a/modules/storages/app/components/storages/open_project_storage_modal_component.html.erb
+++ b/modules/storages/app/components/storages/open_project_storage_modal_component.html.erb
@@ -10,6 +10,6 @@
<%= render(self.class::Body.new(@state)) %>
<% end %>
<% dialog.with_footer do %>
- <%= render(Primer::ButtonComponent.new(data: { "close-dialog-id": self.class.dialog_id })) { I18n.t("button_close") } %>
+ <%= render(Primer::Beta::Button.new(data: { "close-dialog-id": self.class.dialog_id })) { I18n.t("button_close") } %>
<% end %>
<% end %>
diff --git a/modules/storages/app/views/storages/admin/storages/upsale.html.erb b/modules/storages/app/views/storages/admin/storages/upsale.html.erb
index 6f3fcba3990..aae18ca0597 100644
--- a/modules/storages/app/views/storages/admin/storages/upsale.html.erb
+++ b/modules/storages/app/views/storages/admin/storages/upsale.html.erb
@@ -1,9 +1,5 @@
<% html_title(t(:label_administration), t("storages.upsale.title")) -%>
-<%= render template: "common/upsale",
- locals: {
- feature_title: t("storages.upsale.title"),
- feature_description: t("storages.upsale.description"),
- feature_reference: "one_drive_sharepoint_file_storage",
- feature_video: "enterprise/one_drive_sharepoint_integration.mp4"
- } %>
+<%= render EnterpriseEdition::UpsalePageComponent.new(:one_drive_sharepoint_file_storage,
+ video: "enterprise/one_drive_sharepoint_integration.mp4",
+ i18n_scope: "storages.upsale") %>
diff --git a/modules/storages/config/locales/crowdin/af.yml b/modules/storages/config/locales/crowdin/af.yml
index affc8044625..cff880c5870 100644
--- a/modules/storages/config/locales/crowdin/af.yml
+++ b/modules/storages/config/locales/crowdin/af.yml
@@ -250,6 +250,13 @@ af:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/ar.yml b/modules/storages/config/locales/crowdin/ar.yml
index ad77ff4006b..3b0abd7c58f 100644
--- a/modules/storages/config/locales/crowdin/ar.yml
+++ b/modules/storages/config/locales/crowdin/ar.yml
@@ -250,6 +250,13 @@ ar:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/az.yml b/modules/storages/config/locales/crowdin/az.yml
index 4a6f95db32e..5ca6f25ab83 100644
--- a/modules/storages/config/locales/crowdin/az.yml
+++ b/modules/storages/config/locales/crowdin/az.yml
@@ -250,6 +250,13 @@ az:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/be.yml b/modules/storages/config/locales/crowdin/be.yml
index efbde5f153b..d5e94a73c9c 100644
--- a/modules/storages/config/locales/crowdin/be.yml
+++ b/modules/storages/config/locales/crowdin/be.yml
@@ -250,6 +250,13 @@ be:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/bg.yml b/modules/storages/config/locales/crowdin/bg.yml
index 4d0a1549662..804df4dc699 100644
--- a/modules/storages/config/locales/crowdin/bg.yml
+++ b/modules/storages/config/locales/crowdin/bg.yml
@@ -250,6 +250,13 @@ bg:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/ca.yml b/modules/storages/config/locales/crowdin/ca.yml
index 82c2c12d651..9e3885a79fc 100644
--- a/modules/storages/config/locales/crowdin/ca.yml
+++ b/modules/storages/config/locales/crowdin/ca.yml
@@ -250,6 +250,13 @@ ca:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/ckb-IR.yml b/modules/storages/config/locales/crowdin/ckb-IR.yml
index d0bae34d347..f68533f92d8 100644
--- a/modules/storages/config/locales/crowdin/ckb-IR.yml
+++ b/modules/storages/config/locales/crowdin/ckb-IR.yml
@@ -250,6 +250,13 @@ ckb-IR:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/cs.yml b/modules/storages/config/locales/crowdin/cs.yml
index 1f41a301bc6..99c932d88a4 100644
--- a/modules/storages/config/locales/crowdin/cs.yml
+++ b/modules/storages/config/locales/crowdin/cs.yml
@@ -250,6 +250,13 @@ cs:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Zkontrolujte připojení k serveru.
subtitle: Ověřování připojení
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/da.yml b/modules/storages/config/locales/crowdin/da.yml
index eda61b3a2fc..8816ab4f0d7 100644
--- a/modules/storages/config/locales/crowdin/da.yml
+++ b/modules/storages/config/locales/crowdin/da.yml
@@ -250,6 +250,13 @@ da:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/de.yml b/modules/storages/config/locales/crowdin/de.yml
index 07143edfcff..d7aa2bcd96f 100644
--- a/modules/storages/config/locales/crowdin/de.yml
+++ b/modules/storages/config/locales/crowdin/de.yml
@@ -173,7 +173,7 @@ de:
storages:
buttons:
complete_without_setup: Ohne fortfahren
- done_complete_setup: Fertig, Einrichtung abschließen
+ done_complete_setup: Fertig, Einrichtung abgeschlossen
done_continue: Fertig, Fortfahren
open_storage: Dateispeicher öffnen
replace_oauth_application: OpenProject OAuth ersetzen
@@ -250,6 +250,13 @@ de:
oidc_cant_refresh_token: Beim Versuch, Ihren Zugriff auf den Speicher zu überprüfen, ist ein Fehler aufgetreten. Bitte überprüfen Sie die Server-Protokolle auf weitere Informationen.
oidc_non_oidc_user: Der aktuelle Benutzer wurde zwar bereitgestellt, aber nicht von einem OpenID Connect (OIDC) Identity Provider. Bitte wiederholen Sie die Prüfung mit einem OIDC-bereitgestellten Benutzer.
oidc_non_provisioned_user: Der aktuelle Benutzer wird nicht von einem OpenID Connect Identity Provider bereitgestellt. Bitte führen Sie die Prüfung mit einem bereitgestellten Benutzer erneut durch.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Überprüfen Sie Ihre Verbindung mit dem Server.
subtitle: Verbindungsvalidierung
tenant_id_wrong: Die konfigurierte Verzeichnis-(Mandanten-)ID ist ungültig. Bitte überprüfen Sie die Konfiguration.
diff --git a/modules/storages/config/locales/crowdin/el.yml b/modules/storages/config/locales/crowdin/el.yml
index 4356623a5b5..fe79415d2e0 100644
--- a/modules/storages/config/locales/crowdin/el.yml
+++ b/modules/storages/config/locales/crowdin/el.yml
@@ -250,6 +250,13 @@ el:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/eo.yml b/modules/storages/config/locales/crowdin/eo.yml
index 8a174430235..981c11b4e06 100644
--- a/modules/storages/config/locales/crowdin/eo.yml
+++ b/modules/storages/config/locales/crowdin/eo.yml
@@ -250,6 +250,13 @@ eo:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/es.yml b/modules/storages/config/locales/crowdin/es.yml
index fb4707e88d5..caf0b8a626d 100644
--- a/modules/storages/config/locales/crowdin/es.yml
+++ b/modules/storages/config/locales/crowdin/es.yml
@@ -250,6 +250,13 @@ es:
oidc_cant_refresh_token: Se ha producido un error al intentar comprobar su acceso al almacenamiento. Compruebe los registros del servidor para más información.
oidc_non_oidc_user: El usuario actual, aunque está aprovisionado, no fue aprovisionado por un proveedor de identidad OpenID Connect (OIDC). Vuelva a ejecutar la comprobación con un usuario aprovisionado por OIDC.
oidc_non_provisioned_user: El usuario actual no ha sido proporcionado por un proveedor de identidad OpenID Connect. Vuelva a ejecutar la comprobación con un usuario proporcionado.
+ one_drive:
+ client_cant_delete_folder: El cliente tiene problemas para eliminar carpetas. Consulte la documentación de configuración de su almacenamiento.
+ client_write_permission_missing: Parece que el cliente no tiene permisos de escritura. Consulte la documentación de configuración de su almacenamiento.
+ drive_id_not_found: No se ha podido encontrar el identificador de unidad configurado. Compruebe la configuración.
+ drive_id_wrong: El ID de unidad configurada no parece válido. Compruebe la configuración.
+ existing_test_folder: La carpeta %{folder_name} necesaria para la prueba ya existe. Elimínela e inténtelo de nuevo.
+ unexpected_content: Contenido inesperado encontrado dentro la unidad.
placeholder: Compruebe su conexión con el servidor.
subtitle: Validación de conexión
tenant_id_wrong: El id de directorio (tenant) configurado no es válido. Por favor, compruebe la configuración.
diff --git a/modules/storages/config/locales/crowdin/et.yml b/modules/storages/config/locales/crowdin/et.yml
index a8eff498651..879daa0a8b1 100644
--- a/modules/storages/config/locales/crowdin/et.yml
+++ b/modules/storages/config/locales/crowdin/et.yml
@@ -250,6 +250,13 @@ et:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/eu.yml b/modules/storages/config/locales/crowdin/eu.yml
index 35efbe5a5bf..11ba1e21419 100644
--- a/modules/storages/config/locales/crowdin/eu.yml
+++ b/modules/storages/config/locales/crowdin/eu.yml
@@ -250,6 +250,13 @@ eu:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/fa.yml b/modules/storages/config/locales/crowdin/fa.yml
index 4d12459494a..dc94fc0d0f3 100644
--- a/modules/storages/config/locales/crowdin/fa.yml
+++ b/modules/storages/config/locales/crowdin/fa.yml
@@ -250,6 +250,13 @@ fa:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/fi.yml b/modules/storages/config/locales/crowdin/fi.yml
index ac10e5301c0..6803acdf56f 100644
--- a/modules/storages/config/locales/crowdin/fi.yml
+++ b/modules/storages/config/locales/crowdin/fi.yml
@@ -250,6 +250,13 @@ fi:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/fil.yml b/modules/storages/config/locales/crowdin/fil.yml
index d1d03acc613..9e739e7dfb0 100644
--- a/modules/storages/config/locales/crowdin/fil.yml
+++ b/modules/storages/config/locales/crowdin/fil.yml
@@ -250,6 +250,13 @@ fil:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/fr.yml b/modules/storages/config/locales/crowdin/fr.yml
index 65bf4c513a0..2901753210c 100644
--- a/modules/storages/config/locales/crowdin/fr.yml
+++ b/modules/storages/config/locales/crowdin/fr.yml
@@ -250,6 +250,13 @@ fr:
oidc_cant_refresh_token: Une erreur s'est produite lors de la vérification de votre accès au stockage. Veuillez consulter les journaux du serveur pour obtenir plus d'informations.
oidc_non_oidc_user: L'utilisateur actuel, même s'il est provisionné, n'a pas été provisionné par un fournisseur d'identité OpenID Connect (OIDC). Veuillez refaire la vérification avec un utilisateur OIDC provisionné.
oidc_non_provisioned_user: L'utilisateur actuel n'est pas fourni par un fournisseur d'identité OpenID Connect. Veuillez réexécuter la vérification avec un utilisateur fourni.
+ one_drive:
+ client_cant_delete_folder: Le client rencontre des difficultés pour supprimer des dossiers. Veuillez consulter la documentation d'installation de votre espace de stockage.
+ client_write_permission_missing: Le client semble ne pas avoir les autorisations d'écriture. Veuillez vérifier la documentation d'installation de votre espace de stockage.
+ drive_id_not_found: L'identifiant du lecteur configuré est introuvable. Veuillez vérifier la configuration.
+ drive_id_wrong: L'identifiant du lecteur configuré ne semble pas valide. Veuillez vérifier la configuration.
+ existing_test_folder: Le dossier %{folder_name} requis pour le test existe déjà. Veuillez le supprimer et réessayer.
+ unexpected_content: Contenu inattendu trouvé dans le lecteur.
placeholder: Vérifiez votre connexion avec le serveur.
subtitle: Validation de la connexion
tenant_id_wrong: L'identifiant du répertoire (locataire) configuré n'est pas valide. Veuillez vérifier la configuration.
diff --git a/modules/storages/config/locales/crowdin/he.yml b/modules/storages/config/locales/crowdin/he.yml
index 94032c41f32..02ff4de0c7e 100644
--- a/modules/storages/config/locales/crowdin/he.yml
+++ b/modules/storages/config/locales/crowdin/he.yml
@@ -250,6 +250,13 @@ he:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/hi.yml b/modules/storages/config/locales/crowdin/hi.yml
index 053c2d2023c..c143e0f6e20 100644
--- a/modules/storages/config/locales/crowdin/hi.yml
+++ b/modules/storages/config/locales/crowdin/hi.yml
@@ -250,6 +250,13 @@ hi:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/hr.yml b/modules/storages/config/locales/crowdin/hr.yml
index 2ec561d55fc..b470b37a46a 100644
--- a/modules/storages/config/locales/crowdin/hr.yml
+++ b/modules/storages/config/locales/crowdin/hr.yml
@@ -250,6 +250,13 @@ hr:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/hu.yml b/modules/storages/config/locales/crowdin/hu.yml
index 3d6a874455c..149ac6fa058 100644
--- a/modules/storages/config/locales/crowdin/hu.yml
+++ b/modules/storages/config/locales/crowdin/hu.yml
@@ -250,6 +250,13 @@ hu:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/id.yml b/modules/storages/config/locales/crowdin/id.yml
index d51bc3fe4fd..ffe1646dbc3 100644
--- a/modules/storages/config/locales/crowdin/id.yml
+++ b/modules/storages/config/locales/crowdin/id.yml
@@ -250,6 +250,13 @@ id:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/it.yml b/modules/storages/config/locales/crowdin/it.yml
index a1eb56ae852..c1fb8b66b3d 100644
--- a/modules/storages/config/locales/crowdin/it.yml
+++ b/modules/storages/config/locales/crowdin/it.yml
@@ -250,6 +250,13 @@ it:
oidc_cant_refresh_token: Si è verificato un errore durante il tentativo di verificare il tuo accesso all'archiviazione. Controlla i log del server per ulteriori informazioni.
oidc_non_oidc_user: L'utente attuale, pur essendo stato registrato, non è stato fornito da un provider di identità OpenID Connect (OIDC). Esegui nuovamente il controllo con un utente fornito da OIDC.
oidc_non_provisioned_user: L'utente attuale non è fornito da un provider di identità OpenID Connect. Esegui nuovamente il controllo con un utente fornito.
+ one_drive:
+ client_cant_delete_folder: Il cliente riscontra problemi con l'eliminazione delle cartelle. Consulta la documentazione di configurazione del tuo archivio.
+ client_write_permission_missing: Il client non ha i permessi di scrittura necessari. Consulta la documentazione di configurazione del tuo archivio.
+ drive_id_not_found: Impossibile trovare l'ID dell'archivio configurato. Controlla la configurazione.
+ drive_id_wrong: L'ID dell'archivio configurato non è valido. Controlla la configurazione.
+ existing_test_folder: La cartella %{folder_name} necessaria per il test esiste già. Eliminala e riprova.
+ unexpected_content: Contenuto inatteso nello spazio di archiviazione.
placeholder: Verifica la tua connessione con il server.
subtitle: Verifica della connessione
tenant_id_wrong: L'ID della directory non è valido. Verifica la configurazione.
diff --git a/modules/storages/config/locales/crowdin/ja.yml b/modules/storages/config/locales/crowdin/ja.yml
index cd56291b2f3..f71ac01c1c6 100644
--- a/modules/storages/config/locales/crowdin/ja.yml
+++ b/modules/storages/config/locales/crowdin/ja.yml
@@ -250,6 +250,13 @@ ja:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/ka.yml b/modules/storages/config/locales/crowdin/ka.yml
index 453f5d29dec..f7161f9281b 100644
--- a/modules/storages/config/locales/crowdin/ka.yml
+++ b/modules/storages/config/locales/crowdin/ka.yml
@@ -250,6 +250,13 @@ ka:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/kk.yml b/modules/storages/config/locales/crowdin/kk.yml
index 1f757bedde2..ecdc00e4ef9 100644
--- a/modules/storages/config/locales/crowdin/kk.yml
+++ b/modules/storages/config/locales/crowdin/kk.yml
@@ -250,6 +250,13 @@ kk:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/ko.yml b/modules/storages/config/locales/crowdin/ko.yml
index 03f61861d02..6a6651cbfea 100644
--- a/modules/storages/config/locales/crowdin/ko.yml
+++ b/modules/storages/config/locales/crowdin/ko.yml
@@ -250,6 +250,13 @@ ko:
oidc_cant_refresh_token: 저장소에 대한 액세스를 확인하는 동안 오류가 발생했습니다. 자세한 내용은 서버 로그를 확인하세요.
oidc_non_oidc_user: 현재 사용자는 프로비저닝되는 동안, OpenID Connect(OIDC) ID 공급자에 의해 프로비저닝되지 않았습니다. OIDC 프로비저닝 사용자로 확인을 다시 실행하세요.
oidc_non_provisioned_user: 현재 사용자는 OpenID Connect ID 공급자에 의해 제공되지 않았습니다. 제공된 사용자로 확인을 다시 실행하세요.
+ one_drive:
+ client_cant_delete_folder: 클라이언트에서 폴더를 삭제하는 중에 문제가 발생했습니다. 저장소에 대한 설정 설명서를 확인하세요.
+ client_write_permission_missing: 클라이언트에 쓰기 권한이 없는 것 같습니다. 저장소에 대한 설정 설명서를 확인하세요.
+ drive_id_not_found: 구성된 드라이브 ID를 찾을 수 없습니다. 구성을 확인하세요.
+ drive_id_wrong: 구성된 드라이브 ID가 유효하지 않은 것 같습니다. 구성을 확인하세요.
+ existing_test_folder: 테스트에 필요한 %{folder_name} 폴더가 이미 있습니다. 이 폴더를 삭제한 후에 다시 시도하세요.
+ unexpected_content: 드라이브에서 예기치 않은 콘텐츠가 발견되었습니다.
placeholder: 서버에 대한 연결을 확인하세요.
subtitle: 연결 유효성 검사
tenant_id_wrong: 구성된 디렉터리(테넌트) ID가 잘못되었습니다. 구성을 확인하세요.
diff --git a/modules/storages/config/locales/crowdin/lt.yml b/modules/storages/config/locales/crowdin/lt.yml
index f0c5298aa46..673d67dd105 100644
--- a/modules/storages/config/locales/crowdin/lt.yml
+++ b/modules/storages/config/locales/crowdin/lt.yml
@@ -250,6 +250,13 @@ lt:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/lv.yml b/modules/storages/config/locales/crowdin/lv.yml
index e08358d0569..181ed849083 100644
--- a/modules/storages/config/locales/crowdin/lv.yml
+++ b/modules/storages/config/locales/crowdin/lv.yml
@@ -250,6 +250,13 @@ lv:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/mn.yml b/modules/storages/config/locales/crowdin/mn.yml
index 03478cb6383..3e41f104bd6 100644
--- a/modules/storages/config/locales/crowdin/mn.yml
+++ b/modules/storages/config/locales/crowdin/mn.yml
@@ -250,6 +250,13 @@ mn:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/ms.yml b/modules/storages/config/locales/crowdin/ms.yml
index 0f55e2e2b8b..a22e011fca3 100644
--- a/modules/storages/config/locales/crowdin/ms.yml
+++ b/modules/storages/config/locales/crowdin/ms.yml
@@ -250,6 +250,13 @@ ms:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Semak sambungan anda terhadap pelayan.
subtitle: Pengesahan sambungan
tenant_id_wrong: Id direktori (penyewa) yang dikonfigurasikan adalah tidak sah. Sila semak konfigurasi.
diff --git a/modules/storages/config/locales/crowdin/ne.yml b/modules/storages/config/locales/crowdin/ne.yml
index f004121097a..2f9f4adf6c7 100644
--- a/modules/storages/config/locales/crowdin/ne.yml
+++ b/modules/storages/config/locales/crowdin/ne.yml
@@ -250,6 +250,13 @@ ne:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/nl.yml b/modules/storages/config/locales/crowdin/nl.yml
index 22b19df5dc4..1c09fdb400d 100644
--- a/modules/storages/config/locales/crowdin/nl.yml
+++ b/modules/storages/config/locales/crowdin/nl.yml
@@ -250,6 +250,13 @@ nl:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Controleer uw verbinding met de server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/no.yml b/modules/storages/config/locales/crowdin/no.yml
index c465aea61ad..fb45924f8d9 100644
--- a/modules/storages/config/locales/crowdin/no.yml
+++ b/modules/storages/config/locales/crowdin/no.yml
@@ -250,6 +250,13 @@
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/pl.yml b/modules/storages/config/locales/crowdin/pl.yml
index 9264fea76a7..c8cfab67b8c 100644
--- a/modules/storages/config/locales/crowdin/pl.yml
+++ b/modules/storages/config/locales/crowdin/pl.yml
@@ -250,6 +250,13 @@ pl:
oidc_cant_refresh_token: Podczas próby sprawdzenia Twojego dostępu do magazynu wystąpił błąd. Aby uzyskać dalsze informacje, sprawdź dzienniki serwera.
oidc_non_oidc_user: Bieżący użytkownik, choć otrzymał konfigurację, nie otrzymał konfiguracji od dostawcy tożsamości OpenID Connect (OIDC). Przeprowadź ponowne sprawdzenie z użytkownikiem, który otrzymał konfigurację od OIDC.
oidc_non_provisioned_user: Bieżący użytkownik nie jest dostarczany przez dostawcę tożsamości OpenID Connect. Ponownie uruchom sprawdzanie z dostarczonym użytkownikiem.
+ one_drive:
+ client_cant_delete_folder: Klient ma problemy z usunięciem folderów. Sprawdź dokumentację konfiguracji swojej pamięci masowej.
+ client_write_permission_missing: Klient nie ma uprawnień zapisu. Sprawdź dokumentację konfiguracji swojej pamięci masowej.
+ drive_id_not_found: Nie można znaleźć skonfigurowanego Drive ID. Sprawdź konfigurację.
+ drive_id_wrong: Skonfigurowany Drive ID jest nieprawidłowy. Sprawdź konfigurację.
+ existing_test_folder: Wymagany do testowania folder %{folder_name} już istnieje. Usuń go i spróbuj ponownie.
+ unexpected_content: Na dysku znaleziono nieoczekiwaną zawartość.
placeholder: Sprawdź połączenie z serwerem.
subtitle: Weryfikacja połączenia
tenant_id_wrong: Skonfigurowany katalog (dzierżawca) jest nieprawidłowy. Sprawdź konfigurację.
diff --git a/modules/storages/config/locales/crowdin/pt-BR.yml b/modules/storages/config/locales/crowdin/pt-BR.yml
index a1f7b7e7623..8709bc3d0a2 100644
--- a/modules/storages/config/locales/crowdin/pt-BR.yml
+++ b/modules/storages/config/locales/crowdin/pt-BR.yml
@@ -250,6 +250,13 @@ pt-BR:
oidc_cant_refresh_token: Ocorreu um erro ao tentar verificar seu acesso ao armazenamento. Verifique os logs do servidor para mais informações.
oidc_non_oidc_user: O usuário atual, embora provisionado, não foi provisionado por um Provedor de Identidade OpenID Connect (OIDC). Execute a verificação novamente com um usuário provisionado pelo OIDC.
oidc_non_provisioned_user: O usuário atual não foi fornecido por um Provedor de Identidade OpenID Connect. Execute a verificação novamente com um usuário fornecido.
+ one_drive:
+ client_cant_delete_folder: O cliente está com dificuldades para excluir pastas. Verifique a documentação de configuração do seu armazenamento.
+ client_write_permission_missing: O cliente parece não ter permissão para gravar. Verifique a documentação de configuração do armazenamento.
+ drive_id_not_found: O drive Id configurado não foi encontrado. Verifique a configuração.
+ drive_id_wrong: O Id da unidade configurada parece ser inválido. Verifique a configuração.
+ existing_test_folder: A pasta %{folder_name}, necessária para o teste, já existe. Exclua-a e tente novamente.
+ unexpected_content: Conteúdo inesperado encontrado na unidade.
placeholder: Verifique sua conexão com o servidor.
subtitle: Validação de conexão
tenant_id_wrong: O ID do diretório (inquilino) configurado é inválido. Verifique a configuração.
diff --git a/modules/storages/config/locales/crowdin/pt-PT.yml b/modules/storages/config/locales/crowdin/pt-PT.yml
index 31696e96abe..9d215059245 100644
--- a/modules/storages/config/locales/crowdin/pt-PT.yml
+++ b/modules/storages/config/locales/crowdin/pt-PT.yml
@@ -250,6 +250,13 @@ pt-PT:
oidc_cant_refresh_token: Ocorreu um erro ao tentar verificar o seu acesso ao armazenamento. Consulte os registos do servidor para obter mais informações.
oidc_non_oidc_user: O utilizador atual, embora aprovisionado, não foi aprovisionado por um fornecedor de identidade OpenID Connect (OIDC). Volte a executar a verificação com um utilizador aprovisionado pelo OIDC.
oidc_non_provisioned_user: O utilizador atual não é fornecido por um fornecedor de identidade OpenID Connect. Volte a executar a verificação com um utilizador fornecido.
+ one_drive:
+ client_cant_delete_folder: O cliente está a ter problemas a eliminar pastas. Consulte a documentação de configuração para o seu armazenamento.
+ client_write_permission_missing: O cliente parece estár ter permissões de escrita em falta. Consulte a documentação de configuração para o seu armazenamento.
+ drive_id_not_found: Não foi possível encontrar o Drive ID configurado. Verifique a configuração.
+ drive_id_wrong: O Drive ID configurado parece ser inválido. Verifique a configuração.
+ existing_test_folder: A pasta %{folder_name} necessária para teste já existe. Elimine a pasta e tente novamente.
+ unexpected_content: Conteúdo inesperado encontrado na unidade.
placeholder: Verifique a sua ligação com o servidor.
subtitle: Validação da ligação
tenant_id_wrong: O ID do diretório (inquilino) configurado é inválido. Verifique a configuração.
diff --git a/modules/storages/config/locales/crowdin/ro.yml b/modules/storages/config/locales/crowdin/ro.yml
index 2d3dd5ab394..4d231155739 100644
--- a/modules/storages/config/locales/crowdin/ro.yml
+++ b/modules/storages/config/locales/crowdin/ro.yml
@@ -250,6 +250,13 @@ ro:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/ru.yml b/modules/storages/config/locales/crowdin/ru.yml
index 58d4247d62e..6a6957cb766 100644
--- a/modules/storages/config/locales/crowdin/ru.yml
+++ b/modules/storages/config/locales/crowdin/ru.yml
@@ -250,6 +250,13 @@ ru:
oidc_cant_refresh_token: Произошла ошибка во время проверки Вашего доступа к хранилищу. Пожалуйста, проверьте журналы сервера для получения дополнительной информации.
oidc_non_oidc_user: Текущий пользователь не представлен провайдером идентификации OpenID Connect(OIDC). Пожалуйста, повторите проверку пользователя, идентифицированного через OIDC.
oidc_non_provisioned_user: Текущий пользователь не представлен провайдером идентификации OpenID Connect. Пожалуйста, повторите проверку пользователя.
+ one_drive:
+ client_cant_delete_folder: У клиента возникли проблемы с удалением папок. Пожалуйста, проверьте документацию по конфигурации для вашего хранилища.
+ client_write_permission_missing: Возможно, клиент не имеет прав на запись. Пожалуйста, проверьте документацию по конфигурации для вашего хранилища.
+ drive_id_not_found: Настроенный идентификатор диска не найден. Пожалуйста, проверьте конфигурацию.
+ drive_id_wrong: Настроенный идентификатор диска не найден. Пожалуйста, проверьте конфигурацию.
+ existing_test_folder: Папка %{folder_name}, необходимая для тестирования, уже существует. Удалите ее и повторите попытку.
+ unexpected_content: На диске обнаружено неожидаемое содержимое.
placeholder: Проверьте подключение к серверу.
subtitle: Проверка соединения
tenant_id_wrong: Настроенный идентификатор каталога недействителен. Пожалуйста, проверьте конфигурацию.
diff --git a/modules/storages/config/locales/crowdin/rw.yml b/modules/storages/config/locales/crowdin/rw.yml
index 94ce3308e9e..c0c4df0c0f8 100644
--- a/modules/storages/config/locales/crowdin/rw.yml
+++ b/modules/storages/config/locales/crowdin/rw.yml
@@ -250,6 +250,13 @@ rw:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/si.yml b/modules/storages/config/locales/crowdin/si.yml
index 55d415475a0..a5d6346c5de 100644
--- a/modules/storages/config/locales/crowdin/si.yml
+++ b/modules/storages/config/locales/crowdin/si.yml
@@ -250,6 +250,13 @@ si:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/sk.yml b/modules/storages/config/locales/crowdin/sk.yml
index 84b3ff4db28..940ae818fae 100644
--- a/modules/storages/config/locales/crowdin/sk.yml
+++ b/modules/storages/config/locales/crowdin/sk.yml
@@ -250,6 +250,13 @@ sk:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/sl.yml b/modules/storages/config/locales/crowdin/sl.yml
index ae8b6cc4a02..c6b69f099a0 100644
--- a/modules/storages/config/locales/crowdin/sl.yml
+++ b/modules/storages/config/locales/crowdin/sl.yml
@@ -250,6 +250,13 @@ sl:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/sr.yml b/modules/storages/config/locales/crowdin/sr.yml
index be750425ddc..1e559a077fb 100644
--- a/modules/storages/config/locales/crowdin/sr.yml
+++ b/modules/storages/config/locales/crowdin/sr.yml
@@ -250,6 +250,13 @@ sr:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/sv.yml b/modules/storages/config/locales/crowdin/sv.yml
index 5c69ea99ec7..6781a7b71fe 100644
--- a/modules/storages/config/locales/crowdin/sv.yml
+++ b/modules/storages/config/locales/crowdin/sv.yml
@@ -250,6 +250,13 @@ sv:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/th.yml b/modules/storages/config/locales/crowdin/th.yml
index 48de95db3c3..f65461f8701 100644
--- a/modules/storages/config/locales/crowdin/th.yml
+++ b/modules/storages/config/locales/crowdin/th.yml
@@ -250,6 +250,13 @@ th:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/tr.yml b/modules/storages/config/locales/crowdin/tr.yml
index f538524fbef..318d5b01492 100644
--- a/modules/storages/config/locales/crowdin/tr.yml
+++ b/modules/storages/config/locales/crowdin/tr.yml
@@ -250,6 +250,13 @@ tr:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Sunucuyla olan bağlantıyı kontrol edin.
subtitle: Bağlantı doğrulaması
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/uk.yml b/modules/storages/config/locales/crowdin/uk.yml
index 736cee4d84e..20132de3a18 100644
--- a/modules/storages/config/locales/crowdin/uk.yml
+++ b/modules/storages/config/locales/crowdin/uk.yml
@@ -250,6 +250,13 @@ uk:
oidc_cant_refresh_token: Під час перевірки доступу до сховища сталася помилка. Щоб отримати додаткову інформацію, перевірте журнали сервера.
oidc_non_oidc_user: Поточний користувач має доступ, але його надав не постачальник ідентифікаційних даних OpenID Connect (OIDC). Повторіть перевірку з користувачем, створеним через OIDC.
oidc_non_provisioned_user: Поточного користувача не надано постачальником ідентифікаційних даних OpenID Connect. Повторіть перевірку з наданим користувачем.
+ one_drive:
+ client_cant_delete_folder: Клієнту не вдалося видалити папки. Ознайомтеся з документацією щодо конфігурації для свого сховища.
+ client_write_permission_missing: Схоже, у клієнта не має дозволів на записування. Ознайомтеся з документацією щодо конфігурації для свого сховища.
+ drive_id_not_found: Налаштований ідентифікатор Drive ID не знайдено. Перевірте конфігурацію.
+ drive_id_wrong: Схоже, налаштований ідентифікатор Drive ID недійсний. Перевірте конфігурацію.
+ existing_test_folder: Папка %{folder_name}, необхідна для тестування, уже існує. Видаліть її та повторіть спробу.
+ unexpected_content: На диску знайдено неочікуваний контент.
placeholder: Перевірте підключення до сервера.
subtitle: Перевірка підключення
tenant_id_wrong: Налаштований ідентифікатор каталогу (клієнта) недійсний. Перевірте конфігурацію.
diff --git a/modules/storages/config/locales/crowdin/uz.yml b/modules/storages/config/locales/crowdin/uz.yml
index 1d7e1e1e958..0eb357924c7 100644
--- a/modules/storages/config/locales/crowdin/uz.yml
+++ b/modules/storages/config/locales/crowdin/uz.yml
@@ -250,6 +250,13 @@ uz:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/config/locales/crowdin/vi.yml b/modules/storages/config/locales/crowdin/vi.yml
index e38d49cfc96..b308507dfa6 100644
--- a/modules/storages/config/locales/crowdin/vi.yml
+++ b/modules/storages/config/locales/crowdin/vi.yml
@@ -250,6 +250,13 @@ vi:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Kiểm tra kết nối của bạn với máy chủ.
subtitle: Xác thực kết nối
tenant_id_wrong: ID thư mục (người thuê) cấu hình không hợp lệ. Vui lòng kiểm tra cấu hình.
diff --git a/modules/storages/config/locales/crowdin/zh-CN.yml b/modules/storages/config/locales/crowdin/zh-CN.yml
index 660e8c58c4d..6135de044b8 100644
--- a/modules/storages/config/locales/crowdin/zh-CN.yml
+++ b/modules/storages/config/locales/crowdin/zh-CN.yml
@@ -250,6 +250,13 @@ zh-CN:
oidc_cant_refresh_token: 尝试检查您对存储的访问时出错。请检查服务器日志以获取更多信息。
oidc_non_oidc_user: 当前用户在预配的情况下,没有被 OpenID Connect (OIDC) 身份提供商预置。请重新运行与OIDC 预置用户的检查。
oidc_non_provisioned_user: 当前用户并非由 OpenID Connect 身份提供商提供。请针对提供的用户重新运行检查。
+ one_drive:
+ client_cant_delete_folder: 客户端在删除文件夹时遇到问题。请检查您的存储的设置文档。
+ client_write_permission_missing: 客户端似乎没有写入权限。请检查您的存储的设置文档。
+ drive_id_not_found: 找不到配置的驱动器 ID。请检查配置。
+ drive_id_wrong: 配置的驱动器 ID 似乎无效。请检查配置。
+ existing_test_folder: 测试所需的文件夹 %{folder_name} 已经存在。请将其删除并重试。
+ unexpected_content: 在驱动器中找到意外内容。
placeholder: 检查您与服务器的连接。
subtitle: 连接验证
tenant_id_wrong: 已配置的 OAuth 2 客户端ID无效。请检查配置。
diff --git a/modules/storages/config/locales/crowdin/zh-TW.yml b/modules/storages/config/locales/crowdin/zh-TW.yml
index af5ebddc2b5..066bd0f0123 100644
--- a/modules/storages/config/locales/crowdin/zh-TW.yml
+++ b/modules/storages/config/locales/crowdin/zh-TW.yml
@@ -250,6 +250,13 @@ zh-TW:
oidc_cant_refresh_token: 在嘗試檢查您存取儲存空間時發生錯誤。請檢查伺服器日誌以取得進一步資訊。
oidc_non_oidc_user: 目前使用者雖然已配置,但不是由 OpenID Connect (OIDC) 身分提供者配置的。請使用 OIDC 配置的使用者重新執行檢查。
oidc_non_provisioned_user: 當前用戶不是由 OpenID Connect Identity Provider 提供。請使用提供的使用者重新執行檢查。
+ one_drive:
+ client_cant_delete_folder: 用戶刪除資料夾失敗,請檢查儲存空間的文件設定。
+ client_write_permission_missing: 用戶似乎缺少寫入權限,請檢查儲存空間的文件設定。
+ drive_id_not_found: 找不到已設定的磁碟代碼。請檢查相關設定。
+ drive_id_wrong: 設定的磁碟代碼似乎不對。請檢查相關設定。
+ existing_test_folder: 測試所需的資料夾 %{folder_name} 已經存在。請刪除後再重試。
+ unexpected_content: 在磁碟中發現未預期的內容。
placeholder: 根據伺服器檢查您的連線。
subtitle: 連線驗證
tenant_id_wrong: 設定的目錄 (租戶) id 無效。請檢查設定。
diff --git a/modules/storages/config/locales/en.yml b/modules/storages/config/locales/en.yml
index b444f8424dc..f8cb2c7a370 100644
--- a/modules/storages/config/locales/en.yml
+++ b/modules/storages/config/locales/en.yml
@@ -251,6 +251,13 @@ en:
oidc_cant_refresh_token: There was an error while trying to check your access to the storage. Please check the server logs for further information.
oidc_non_oidc_user: The current user, while provisioned, wasn't provisioned by an OpenID Connect (OIDC) Identity Provider. Please re-run the check with an OIDC provisioned user.
oidc_non_provisioned_user: The current user isn't provided by an OpenID Connect Identity Provider. Please re-run the check with a provided user.
+ one_drive:
+ client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage.
+ client_write_permission_missing: The client seems to have write permissions missing. Please check the setup documentation for your storage.
+ drive_id_not_found: The configured drive id could not be found. Please check the configuration.
+ drive_id_wrong: The configured drive id seems invalid. Please check the configuration.
+ existing_test_folder: The folder %{folder_name} needed for testing already exists. Please delete it and try again.
+ unexpected_content: Unexpected content found in the drive.
placeholder: Check your connection against the server.
subtitle: Connection validation
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
diff --git a/modules/storages/spec/common/storages/peripherals/connection_validators/nextcloud_connection_validator_spec.rb b/modules/storages/spec/common/storages/peripherals/connection_validators/base_connection_validator_spec.rb
similarity index 57%
rename from modules/storages/spec/common/storages/peripherals/connection_validators/nextcloud_connection_validator_spec.rb
rename to modules/storages/spec/common/storages/peripherals/connection_validators/base_connection_validator_spec.rb
index ebae600a5cb..5aaaaade745 100644
--- a/modules/storages/spec/common/storages/peripherals/connection_validators/nextcloud_connection_validator_spec.rb
+++ b/modules/storages/spec/common/storages/peripherals/connection_validators/base_connection_validator_spec.rb
@@ -34,28 +34,43 @@ require_module_spec_helper
module Storages
module Peripherals
module ConnectionValidators
- RSpec.describe NextcloudValidator, :webmock do
- it "does not run the AMPF tests if the storage is not automatically managed",
- vcr: "nextcloud/capabilities_success" do
- results = described_class.new(storage: create(:nextcloud_storage_with_local_connection)).validate
+ class TestValidator < BaseConnectionValidator
+ def self.reset_groups!
+ @validation_groups = nil
+ end
+ end
- expect { results.group(:ampf_configuration) }.to raise_error(KeyError)
+ RSpec.describe BaseConnectionValidator, :webmock do
+ let(:storage) { create(:nextcloud_storage_configured) }
+
+ subject(:validator) { TestValidator.new(storage) }
+
+ after { TestValidator.reset_groups! }
+
+ it "returns a ValidationResult" do
+ expect(validator.call).to be_a(ValidatorResult)
+ end
+
+ it "only runs a verification if the precondition evaluates as truthy" do
+ test_group = class_spy(Nextcloud::StorageConfigurationValidator)
+ TestValidator.register_group :test_group, test_group, precondition: ->(_, _) { false }
+
+ result = validator.call
+ expect(result).to be_empty
+ expect(test_group).not_to have_received(:call)
end
it "aggregates all the results from the tests", vcr: "nextcloud/capabilities_success" do
- results = described_class.new(storage: create(:nextcloud_storage_with_local_connection)).validate
+ TestValidator.register_group :base_configuration, Nextcloud::StorageConfigurationValidator
+ TestValidator.register_group :authentication, Nextcloud::AuthenticationValidator,
+ precondition: ->(_, result) { result.group(:base_configuration).non_failure? }
+
+ results = TestValidator.new(create(:nextcloud_storage_with_local_connection)).call
expect(results).to be_warning
expect(results.group(:base_configuration)).to be_success
expect(results.group(:authentication)).to be_warning
end
-
- it "does not run any further tests if base configuration failed", vcr: "nextcloud/capabilities_invalid_data" do
- results = described_class.new(storage: create(:nextcloud_storage_with_local_connection)).validate
-
- expect { results.group(:authentication) }.to raise_error(KeyError)
- expect { results.group(:ampf_configuration) }.to raise_error(KeyError)
- end
end
end
end
diff --git a/modules/storages/spec/common/storages/peripherals/connection_validators/one_drive/ampf_configuration_validator_spec.rb b/modules/storages/spec/common/storages/peripherals/connection_validators/one_drive/ampf_configuration_validator_spec.rb
new file mode 100644
index 00000000000..67f994a943b
--- /dev/null
+++ b/modules/storages/spec/common/storages/peripherals/connection_validators/one_drive/ampf_configuration_validator_spec.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+require "spec_helper"
+require_module_spec_helper
+
+module Storages
+ module Peripherals
+ module ConnectionValidators
+ module OneDrive
+ RSpec.describe AmpfConfigurationValidator, :webmock do
+ let(:storage) { create(:sharepoint_dev_drive_storage, :as_automatically_managed) }
+ let(:auth_strategy) { Registry["one_drive.authentication.userless"].call }
+ let(:folder_name) { described_class::TEST_FOLDER_NAME }
+
+ subject(:validator) { described_class.new(storage) }
+
+ it "returns a GroupValidationResult", vcr: "one_drive/validator_ampf_clean_run" do
+ results = validator.call
+
+ expect(results).to be_a(ValidationGroupResult)
+ expect(results).to be_success
+ end
+
+ describe "possible error scenarios" do
+ it "fails when there's unexpected folder and files in the drive", vcr: "one_drive/validator_extraneous_files" do
+ results = validator.call
+
+ expect(results[:drive_contents]).to be_a_warning
+ expect(results[:drive_contents].message).to eq(I18n.t(i18n_key("one_drive.unexpected_content")))
+ end
+
+ it "fails when folders can't be created" do
+ create_cmd = class_double(StorageInteraction::OneDrive::CreateFolderCommand)
+ allow(create_cmd).to receive(:call)
+ .with(storage:, auth_strategy:, folder_name:, parent_location: ParentFolder.root)
+ .and_return(ServiceResult.failure)
+
+ Registry.stub("one_drive.commands.create_folder", create_cmd)
+
+ results = validator.call
+
+ expect(results[:client_folder_creation]).to be_a_failure
+ expect(results[:client_folder_creation].message)
+ .to eq(I18n.t(i18n_key("one_drive.client_write_permission_missing")))
+ end
+
+ it "fails when the test folder already exists on the remote", vcr: "one_drive/validator_test_folder_already_exists" do
+ Registry["one_drive.commands.create_folder"]
+ .call(storage:, auth_strategy:, folder_name:, parent_location: ParentFolder.root)
+
+ result = validator.call
+ expect(result[:client_folder_creation]).to be_a_failure
+ expect(result[:client_folder_creation].message)
+ .to eq(I18n.t(i18n_key("one_drive.existing_test_folder"), folder_name:))
+ ensure
+ StorageInteraction::OneDrive::DeleteFolderCommand.call(storage:, auth_strategy:, location: created_folder)
+ end
+
+ it "fails when folders can't be deleted", vcr: "one_drive/validator_create_folder" do
+ delete_cmd = class_double(StorageInteraction::OneDrive::DeleteFolderCommand)
+ allow(delete_cmd).to receive(:call).with(storage:, auth_strategy:, location: /.+/)
+ .and_return(ServiceResult.failure)
+
+ Registry.stub("one_drive.commands.delete_folder", delete_cmd)
+
+ results = validator.call
+
+ expect(results[:client_folder_removal]).to be_a_failure
+ expect(results[:client_folder_removal].message).to eq(I18n.t(i18n_key("one_drive.client_cant_delete_folder")))
+ ensure
+ StorageInteraction::OneDrive::DeleteFolderCommand.call(storage:, auth_strategy:, location: created_folder)
+ end
+ end
+
+ private
+
+ def created_folder
+ Registry["one_drive.queries.files"].call(storage:, auth_strategy:, folder: ParentFolder.root).on_success do
+ folder = it.result.files.detect { |file| file.name.include?(folder_name) }
+
+ return folder.id
+ end
+ end
+
+ def i18n_key(key) = "storages.health.connection_validation.#{key}"
+ end
+ end
+ end
+ end
+end
diff --git a/modules/storages/spec/common/storages/peripherals/connection_validators/one_drive/authentication_validator_spec.rb b/modules/storages/spec/common/storages/peripherals/connection_validators/one_drive/authentication_validator_spec.rb
new file mode 100644
index 00000000000..eea23f6958d
--- /dev/null
+++ b/modules/storages/spec/common/storages/peripherals/connection_validators/one_drive/authentication_validator_spec.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+require "spec_helper"
+require_module_spec_helper
+
+module Storages
+ module Peripherals
+ module ConnectionValidators
+ module OneDrive
+ RSpec.describe AuthenticationValidator, :webmock do
+ subject(:validator) { described_class.new(storage) }
+
+ context "when using OAuth2" do
+ let(:user) { create(:user) }
+ let(:storage) { create(:sharepoint_dev_drive_storage, oauth_client_token_user: user) }
+
+ before { User.current = user }
+
+ it "passes when the user has a token and the request works", vcr: "one_drive/user_query_success" do
+ expect(validator.call).to be_success
+ end
+
+ it "returns a warning when there's no token for the current user" do
+ User.current = create(:user)
+ result = validator.call
+
+ expect(result[:existing_token]).to be_a_warning
+ expect(result[:existing_token].message).to eq(I18n.t(i18n_key(:oauth_token_missing)))
+ expect(result[:user_bound_request]).to be_skipped
+ end
+
+ it "returns a failure if the remote call failed" do
+ Registry.stub("one_drive.queries.user", ->(_) { ServiceResult.failure(result: :unauthorized) })
+
+ result = validator.call
+ expect(result[:user_bound_request]).to be_a_failure
+ expect(result[:user_bound_request].message).to eq(I18n.t(i18n_key(:oauth_request_unauthorized)))
+ end
+ end
+
+ private
+
+ def i18n_key(key) = "storages.health.connection_validation.#{key}"
+ end
+ end
+ end
+ end
+end
diff --git a/modules/storages/spec/common/storages/peripherals/connection_validators/one_drive/storage_configuration_validator_spec.rb b/modules/storages/spec/common/storages/peripherals/connection_validators/one_drive/storage_configuration_validator_spec.rb
new file mode 100644
index 00000000000..11a18dcba43
--- /dev/null
+++ b/modules/storages/spec/common/storages/peripherals/connection_validators/one_drive/storage_configuration_validator_spec.rb
@@ -0,0 +1,153 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+require "spec_helper"
+require_module_spec_helper
+
+module Storages
+ module Peripherals
+ module ConnectionValidators
+ module OneDrive
+ RSpec.describe StorageConfigurationValidator, :webmock do
+ let(:storage) { create(:sharepoint_dev_drive_storage, :as_automatically_managed) }
+ let(:auth_strategy) { Registry["one_drive.authentication.userless"].call }
+
+ subject(:validator) { described_class.new(storage) }
+
+ it "returns a GroupValidationResult", vcr: "one_drive/files_query_userless" do
+ results = validator.call
+
+ expect(results).to be_a(ValidationGroupResult)
+ expect(results).to be_success
+ end
+
+ describe "possible error scenarios" do
+ let(:files_double) { class_double(StorageInteraction::OneDrive::FilesQuery) }
+ let(:result) { ServiceResult.success }
+
+ before do
+ allow(files_double).to receive(:call).with(storage:, auth_strategy:, folder: ParentFolder.root).and_return(result)
+ end
+
+ context "when the storage isn't configured" do
+ let(:storage) { create(:one_drive_storage) }
+
+ it "the check fails" do
+ results = validator.call
+ expect(results[:storage_configured]).to be_a_failure
+ expect(results[:storage_configured].message).to eq(I18n.t(i18n_key(:not_configured)))
+ end
+ end
+
+ context "when diagnostic request fails with an unhandled error" do
+ let(:result) { ServiceResult.failure(result: :error, errors: StorageError.new(code: :error)) }
+
+ before { Registry.stub("one_drive.queries.files", files_double) }
+
+ it "the check fails" do
+ results = validator.call
+
+ expect(results[:diagnostic_request]).to be_a_failure
+ expect(results[:diagnostic_request].message).to eq(I18n.t(i18n_key(:unknown_error)))
+ end
+
+ it "logs an error" do
+ allow(Rails.logger).to receive(:error)
+ validator.call
+
+ expect(Rails.logger).to have_received(:error).with(/Connection validation failed with unknown/)
+ end
+ end
+
+ context "when the tenant id is wrong" do
+ it "but looks like an actual valid value", vcr: "one_drive/validation_wrong_tenant_id" do
+ storage.tenant_id = "itdoesnotexists9000.sharepoint.com"
+ results = described_class.new(storage).call
+
+ expect(results[:tenant_id]).to be_a_failure
+ expect(results[:tenant_id].message).to eq(I18n.t(i18n_key(:tenant_id_wrong)))
+ end
+
+ it "but is blatantly wrong", vcr: "one_drive/validation_absurd_tenant_id" do
+ storage.tenant_id = "wrong"
+ results = described_class.new(storage).call
+
+ expect(results[:tenant_id]).to be_a_failure
+ expect(results[:tenant_id].message).to eq(I18n.t(i18n_key(:tenant_id_wrong)))
+ end
+ end
+
+ context "when the client secret is wrong" do
+ it "fails the check", vcr: "one_drive/validation_wrong_client_secret" do
+ storage.oauth_client.client_secret = "wrong"
+ results = described_class.new(storage).call
+
+ expect(results[:client_secret]).to be_a_failure
+ expect(results[:client_secret].message).to eq(I18n.t(i18n_key(:client_secret_wrong)))
+ end
+ end
+
+ context "when the client id is wrong" do
+ it "fails the check", vcr: "one_drive/validation_wrong_client_id" do
+ storage.oauth_client.client_id = "wrong"
+ results = described_class.new(storage).call
+
+ expect(results[:client_id]).to be_a_failure
+ expect(results[:client_id].message).to eq(I18n.t(i18n_key(:client_id_wrong)))
+ end
+ end
+
+ context "when the drive id is wrong" do
+ it "fails when looks malformed", vcr: "one_drive/validation_drive_id_malformed" do
+ storage.drive_id = "not-a-drive-id"
+ results = described_class.new(storage).call
+
+ expect(results[:drive_id_format]).to be_a_failure
+ expect(results[:drive_id_format].message).to eq(I18n.t(i18n_key("one_drive.drive_id_wrong")))
+ end
+
+ it "fails when is not found", vcr: "one_drive/validation_drive_id_not_found" do
+ storage.drive_id = "#{storage.drive_id[0..-2]}0"
+ results = described_class.new(storage).call
+
+ expect(results[:drive_id_not_found]).to be_a_failure
+ expect(results[:drive_id_not_found].message).to eq(I18n.t(i18n_key("one_drive.drive_id_not_found")))
+ end
+ end
+ end
+
+ private
+
+ def i18n_key(key) = "storages.health.connection_validation.#{key}"
+ end
+ end
+ end
+ end
+end
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/files_query_userless.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/files_query_userless.yml
new file mode 100644
index 00000000000..f3baf965b52
--- /dev/null
+++ b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/files_query_userless.yml
@@ -0,0 +1,114 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://login.microsoftonline.com/4d44bf36-9b56-45c0-8807-bbf386dd047f/oauth2/v2.0/token
+ body:
+ encoding: ASCII-8BIT
+ string: grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default+offline_access&client_id=b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff&client_secret=nB58Q%7EwWf6.4zUIojn2nzkrpzDWlkQtDSWPcLa6R
+ headers:
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Type:
+ - application/x-www-form-urlencoded
+ Content-Length:
+ - '199'
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Pragma:
+ - no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Expires:
+ - "-1"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains
+ X-Content-Type-Options:
+ - nosniff
+ P3p:
+ - CP="DSP CUR OTPi IND OTRi ONL FIN"
+ X-Ms-Request-Id:
+ - b50c2aba-1a4b-4915-a71e-b29d53290900
+ X-Ms-Ests-Server:
+ - 2.1.20393.4 - SEC ProdSlices
+ X-Ms-Srs:
+ - 1.P
+ Content-Security-Policy-Report-Only:
+ - object-src 'none'; base-uri 'self'; script-src 'self' 'nonce-3sWFN2meJ8S-bmklrr6Kbg'
+ 'unsafe-inline' 'unsafe-eval' https://*.msauth.net https://*.msftauth.net
+ https://*.msftauthimages.net https://*.msauthimages.net https://*.msidentity.com
+ https://*.microsoftonline-p.com https://*.microsoftazuread-sso.com https://*.azureedge.net
+ https://*.outlook.com https://*.office.com https://*.office365.com https://*.microsoft.com
+ https://*.bing.com 'report-sample'; report-uri https://csp.microsoft.com/report/ESTS-UX-All
+ X-Xss-Protection:
+ - '0'
+ Set-Cookie:
+ - fpc=AqVrum5XuR9KsQqo956mtLBRVKK0AQAAAG24hd8OAAAA; expires=Wed, 07-May-2025
+ 12:13:34 GMT; path=/; secure; HttpOnly; SameSite=None, x-ms-gateway-slice=estsfd;
+ path=/; secure; samesite=none; httponly, stsservicecookie=estsfd; path=/;
+ secure; samesite=none; httponly
+ Date:
+ - Mon, 07 Apr 2025 12:13:33 GMT
+ Content-Length:
+ - '1696'
+ body:
+ encoding: UTF-8
+ string: '{"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"
"}'
+ recorded_at: Mon, 07 Apr 2025 12:13:34 GMT
+- request:
+ method: get
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root/children?$select=id,name,size,webUrl,lastModifiedBy,createdBy,fileSystemInfo,file,folder,parentReference
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Authorization:
+ - Bearer
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Content-Type:
+ - application/json; odata.metadata=minimal; odata.streaming=true; IEEE754Compatible=false;
+ charset=utf-8
+ Content-Encoding:
+ - gzip
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - 9625a92b-e916-425f-b889-e4f0f06ec4e1
+ Client-Request-Id:
+ - 9625a92b-e916-425f-b889-e4f0f06ec4e1
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"001","RoleInstance":"FR2PEPF000001EE"}}'
+ Date:
+ - Mon, 07 Apr 2025 12:13:33 GMT
+ body:
+ encoding: UTF-8
+ string: '{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#Collection(driveItem)","value":[{"@odata.etag":"\"{8DA9DCAF-43E4-419C-B52E-F1303A0D3372},1\"","createdBy":{"user":{"email":"op.admin@ymt6d.onmicrosoft.com","id":"5b5a7dc4-4539-41ba-9fa9-100f0a26acb7","displayName":"Eric
+ Schubert"}},"id":"01ANJ53W5P3SUY3ZCDTRA3KLXRGA5A2M3S","lastModifiedBy":{"user":{"email":"op.admin@ymt6d.onmicrosoft.com","id":"5b5a7dc4-4539-41ba-9fa9-100f0a26acb7","displayName":"Eric
+ Schubert"}},"name":"data","parentReference":{"driveType":"documentLibrary","driveId":"b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs","id":"01ANJ53W56Y2GOVW7725BZO354PWSELRRZ","name":"Marcello
+ VCR","path":"/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root:","siteId":"1099e315-d0c7-47c7-8640-aa9504b70fff"},"webUrl":"https://ymt6d.sharepoint.com/sites/OPTest/Marcello%20VCR/data","fileSystemInfo":{"createdDateTime":"2025-04-07T12:02:26Z","lastModifiedDateTime":"2025-04-07T12:02:26Z"},"folder":{"childCount":0},"size":0},{"@odata.etag":"\"{992C5835-AA54-4B71-B3AF-197B1F4578E8},1\"","createdBy":{"user":{"email":"op.admin@ymt6d.onmicrosoft.com","id":"5b5a7dc4-4539-41ba-9fa9-100f0a26acb7","displayName":"Eric
+ Schubert"}},"id":"01ANJ53WZVLAWJSVFKOFF3HLYZPMPUK6HI","lastModifiedBy":{"user":{"email":"op.admin@ymt6d.onmicrosoft.com","id":"5b5a7dc4-4539-41ba-9fa9-100f0a26acb7","displayName":"Eric
+ Schubert"}},"name":"simply_oidc.jpg","parentReference":{"driveType":"documentLibrary","driveId":"b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs","id":"01ANJ53W56Y2GOVW7725BZO354PWSELRRZ","name":"Marcello
+ VCR","path":"/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root:","siteId":"1099e315-d0c7-47c7-8640-aa9504b70fff"},"webUrl":"https://ymt6d.sharepoint.com/sites/OPTest/Marcello%20VCR/simply_oidc.jpg","file":{"hashes":{"quickXorHash":"qzqT/ZJgJnvhu77aF+2RcdH62Sc="},"mimeType":"image/jpeg"},"fileSystemInfo":{"createdDateTime":"2025-04-07T12:02:42Z","lastModifiedDateTime":"2025-04-07T12:02:42Z"},"size":56483}]}'
+ recorded_at: Mon, 07 Apr 2025 12:13:34 GMT
+recorded_with: VCR 6.3.1
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_absurd_tenant_id.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_absurd_tenant_id.yml
new file mode 100644
index 00000000000..32e26326194
--- /dev/null
+++ b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_absurd_tenant_id.yml
@@ -0,0 +1,71 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://login.microsoftonline.com/wrong/oauth2/v2.0/token
+ body:
+ encoding: ASCII-8BIT
+ string: grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default+offline_access&client_id=b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff&client_secret=nB58Q%7EwWf6.4zUIojn2nzkrpzDWlkQtDSWPcLa6R
+ headers:
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Type:
+ - application/x-www-form-urlencoded
+ Content-Length:
+ - '199'
+ response:
+ status:
+ code: 400
+ message: Bad Request
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Pragma:
+ - no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Expires:
+ - "-1"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains
+ X-Content-Type-Options:
+ - nosniff
+ P3p:
+ - CP="DSP CUR OTPi IND OTRi ONL FIN"
+ X-Ms-Request-Id:
+ - 6d866228-7a7d-4c37-aedc-c219ccd99200
+ X-Ms-Ests-Server:
+ - 2.1.20393.4 - FRC ProdSlices
+ X-Ms-Srs:
+ - 1.P
+ Content-Security-Policy-Report-Only:
+ - object-src 'none'; base-uri 'self'; script-src 'self' 'nonce-ZuX_mRovdaFEsnx7JOyHtQ'
+ 'unsafe-inline' 'unsafe-eval' https://*.msauth.net https://*.msftauth.net
+ https://*.msftauthimages.net https://*.msauthimages.net https://*.msidentity.com
+ https://*.microsoftonline-p.com https://*.microsoftazuread-sso.com https://*.azureedge.net
+ https://*.outlook.com https://*.office.com https://*.office365.com https://*.microsoft.com
+ https://*.bing.com 'report-sample'; report-uri https://csp.microsoft.com/report/ESTS-UX-All
+ X-Xss-Protection:
+ - '0'
+ Set-Cookie:
+ - fpc=AkEOv7Godm1MpiXqOWOQa-U; expires=Wed, 07-May-2025 12:54:01 GMT; path=/;
+ secure; HttpOnly; SameSite=None, x-ms-gateway-slice=estsfd; path=/; secure;
+ samesite=none; httponly, stsservicecookie=estsfd; path=/; secure; samesite=none;
+ httponly
+ Date:
+ - Mon, 07 Apr 2025 12:54:01 GMT
+ Content-Length:
+ - '519'
+ body:
+ encoding: UTF-8
+ string: '{"error":"invalid_request","error_description":"AADSTS900023: Specified
+ tenant identifier ''wrong'' is neither a valid DNS name, nor a valid external
+ domain. Trace ID: 6d866228-7a7d-4c37-aedc-c219ccd99200 Correlation ID: eaa0b807-04e6-483b-bfaf-e88ab94a8089
+ Timestamp: 2025-04-07 12:54:01Z","error_codes":[900023],"timestamp":"2025-04-07
+ 12:54:01Z","trace_id":"6d866228-7a7d-4c37-aedc-c219ccd99200","correlation_id":"eaa0b807-04e6-483b-bfaf-e88ab94a8089","error_uri":"https://login.microsoftonline.com/error?code=900023"}'
+ recorded_at: Mon, 07 Apr 2025 12:54:01 GMT
+recorded_with: VCR 6.3.1
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_drive_id_malformed.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_drive_id_malformed.yml
new file mode 100644
index 00000000000..a5bd6d4b617
--- /dev/null
+++ b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_drive_id_malformed.yml
@@ -0,0 +1,111 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://login.microsoftonline.com/4d44bf36-9b56-45c0-8807-bbf386dd047f/oauth2/v2.0/token
+ body:
+ encoding: ASCII-8BIT
+ string: grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default+offline_access&client_id=b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff&client_secret=nB58Q%7EwWf6.4zUIojn2nzkrpzDWlkQtDSWPcLa6R
+ headers:
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Type:
+ - application/x-www-form-urlencoded
+ Content-Length:
+ - '199'
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Pragma:
+ - no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Expires:
+ - "-1"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains
+ X-Content-Type-Options:
+ - nosniff
+ P3p:
+ - CP="DSP CUR OTPi IND OTRi ONL FIN"
+ X-Ms-Request-Id:
+ - 4baa8563-b8be-4599-9f6b-d4169d949d00
+ X-Ms-Ests-Server:
+ - 2.1.20393.4 - FRC ProdSlices
+ X-Ms-Srs:
+ - 1.P
+ Content-Security-Policy-Report-Only:
+ - object-src 'none'; base-uri 'self'; script-src 'self' 'nonce-ePygL8dNIpLEv2G4rO9RvA'
+ 'unsafe-inline' 'unsafe-eval' https://*.msauth.net https://*.msftauth.net
+ https://*.msftauthimages.net https://*.msauthimages.net https://*.msidentity.com
+ https://*.microsoftonline-p.com https://*.microsoftazuread-sso.com https://*.azureedge.net
+ https://*.outlook.com https://*.office.com https://*.office365.com https://*.microsoft.com
+ https://*.bing.com 'report-sample'; report-uri https://csp.microsoft.com/report/ESTS-UX-All
+ X-Xss-Protection:
+ - '0'
+ Set-Cookie:
+ - fpc=AoMQayjayslJnblHnzkv3xRRVKK0AQAAAFfQhd8OAAAA; expires=Wed, 07-May-2025
+ 13:55:35 GMT; path=/; secure; HttpOnly; SameSite=None, x-ms-gateway-slice=estsfd;
+ path=/; secure; samesite=none; httponly, stsservicecookie=estsfd; path=/;
+ secure; samesite=none; httponly
+ Date:
+ - Mon, 07 Apr 2025 13:55:34 GMT
+ Content-Length:
+ - '1696'
+ body:
+ encoding: UTF-8
+ string: '{"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":""}'
+ recorded_at: Mon, 07 Apr 2025 13:55:35 GMT
+- request:
+ method: get
+ uri: https://graph.microsoft.com/v1.0/drives/not-a-drive-id/root/children?$select=id,name,size,webUrl,lastModifiedBy,createdBy,fileSystemInfo,file,folder,parentReference
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Authorization:
+ - Bearer
+ response:
+ status:
+ code: 400
+ message: Bad Request
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Content-Type:
+ - application/json; odata.metadata=minimal; odata.streaming=true; IEEE754Compatible=false;
+ charset=utf-8
+ Content-Encoding:
+ - gzip
+ Vary:
+ - Accept-Encoding
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - 6f836231-a3f9-4005-85a1-f4246c7e0746
+ Client-Request-Id:
+ - 6f836231-a3f9-4005-85a1-f4246c7e0746
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"004","RoleInstance":"FR2PEPF000003FC"}}'
+ Date:
+ - Mon, 07 Apr 2025 13:55:35 GMT
+ body:
+ encoding: UTF-8
+ string: '{"error":{"code":"invalidRequest","message":"The provided drive id
+ appears to be malformed, or does not represent a valid drive."}}'
+ recorded_at: Mon, 07 Apr 2025 13:55:35 GMT
+recorded_with: VCR 6.3.1
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_drive_id_not_found.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_drive_id_not_found.yml
new file mode 100644
index 00000000000..f8fc1b89e7b
--- /dev/null
+++ b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_drive_id_not_found.yml
@@ -0,0 +1,109 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://login.microsoftonline.com/4d44bf36-9b56-45c0-8807-bbf386dd047f/oauth2/v2.0/token
+ body:
+ encoding: ASCII-8BIT
+ string: grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default+offline_access&client_id=b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff&client_secret=nB58Q%7EwWf6.4zUIojn2nzkrpzDWlkQtDSWPcLa6R
+ headers:
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Type:
+ - application/x-www-form-urlencoded
+ Content-Length:
+ - '199'
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Pragma:
+ - no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Expires:
+ - "-1"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains
+ X-Content-Type-Options:
+ - nosniff
+ P3p:
+ - CP="DSP CUR OTPi IND OTRi ONL FIN"
+ X-Ms-Request-Id:
+ - 5c707cf2-ee49-40ea-9f3a-12135a310400
+ X-Ms-Ests-Server:
+ - 2.1.20465.4 - SEC ProdSlices
+ X-Ms-Srs:
+ - 1.P
+ Content-Security-Policy-Report-Only:
+ - object-src 'none'; base-uri 'self'; script-src 'self' 'nonce-IKAIoxsi5jE7daVt7_RV1g'
+ 'unsafe-inline' 'unsafe-eval' https://*.msauth.net https://*.msftauth.net
+ https://*.msftauthimages.net https://*.msauthimages.net https://*.msidentity.com
+ https://*.microsoftonline-p.com https://*.microsoftazuread-sso.com https://*.azureedge.net
+ https://*.outlook.com https://*.office.com https://*.office365.com https://*.microsoft.com
+ https://*.bing.com 'report-sample'; report-uri https://csp.microsoft.com/report/ESTS-UX-All
+ X-Xss-Protection:
+ - '0'
+ Set-Cookie:
+ - fpc=Atm5OoxKOcJEm_fG2VtSgv5RVKK0AQAAAL_yhd8OAAAA; expires=Wed, 07-May-2025
+ 16:22:23 GMT; path=/; secure; HttpOnly; SameSite=None, x-ms-gateway-slice=estsfd;
+ path=/; secure; samesite=none; httponly, stsservicecookie=estsfd; path=/;
+ secure; samesite=none; httponly
+ Date:
+ - Mon, 07 Apr 2025 16:22:23 GMT
+ Content-Length:
+ - '1696'
+ body:
+ encoding: UTF-8
+ string: '{"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":""}'
+ recorded_at: Mon, 07 Apr 2025 16:22:23 GMT
+- request:
+ method: get
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPX0/root/children?$select=id,name,size,webUrl,lastModifiedBy,createdBy,fileSystemInfo,file,folder,parentReference
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Authorization:
+ - Bearer
+ response:
+ status:
+ code: 404
+ message: Not Found
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Content-Encoding:
+ - gzip
+ Vary:
+ - Accept-Encoding
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - '01824623-0f82-4e02-b56b-91e7db164c27'
+ Client-Request-Id:
+ - '01824623-0f82-4e02-b56b-91e7db164c27'
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"UK South","Slice":"E","Ring":"5","ScaleUnit":"001","RoleInstance":"LN2PEPF000114D0"}}'
+ Date:
+ - Mon, 07 Apr 2025 16:22:23 GMT
+ body:
+ encoding: UTF-8
+ string: '{"error":{"code":"itemNotFound","message":"Item not found"}}'
+ recorded_at: Mon, 07 Apr 2025 16:22:24 GMT
+recorded_with: VCR 6.3.1
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_wrong_client_id.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_wrong_client_id.yml
new file mode 100644
index 00000000000..5acf4d9239e
--- /dev/null
+++ b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_wrong_client_id.yml
@@ -0,0 +1,73 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://login.microsoftonline.com/4d44bf36-9b56-45c0-8807-bbf386dd047f/oauth2/v2.0/token
+ body:
+ encoding: ASCII-8BIT
+ string: grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default+offline_access&client_id=wrong&client_secret=nB58Q%7EwWf6.4zUIojn2nzkrpzDWlkQtDSWPcLa6R
+ headers:
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Type:
+ - application/x-www-form-urlencoded
+ Content-Length:
+ - '168'
+ response:
+ status:
+ code: 400
+ message: Bad Request
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Pragma:
+ - no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Expires:
+ - "-1"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains
+ X-Content-Type-Options:
+ - nosniff
+ P3p:
+ - CP="DSP CUR OTPi IND OTRi ONL FIN"
+ X-Ms-Request-Id:
+ - 65abb2e7-9ea0-4b11-ba8a-2e2bfcec4200
+ X-Ms-Ests-Server:
+ - 2.1.20465.4 - WEULR1 ProdSlices
+ X-Ms-Srs:
+ - 1.P
+ Content-Security-Policy-Report-Only:
+ - object-src 'none'; base-uri 'self'; script-src 'self' 'nonce-ewwu91fb4gwQqBkjUr57Yg'
+ 'unsafe-inline' 'unsafe-eval' https://*.msauth.net https://*.msftauth.net
+ https://*.msftauthimages.net https://*.msauthimages.net https://*.msidentity.com
+ https://*.microsoftonline-p.com https://*.microsoftazuread-sso.com https://*.azureedge.net
+ https://*.outlook.com https://*.office.com https://*.office365.com https://*.microsoft.com
+ https://*.bing.com 'report-sample'; report-uri https://csp.microsoft.com/report/ESTS-UX-All
+ X-Xss-Protection:
+ - '0'
+ Set-Cookie:
+ - fpc=AlOcQyq-LSdKqIifcwNWuRRkaFwcAQAAAEbJhd8OAAAA; expires=Wed, 07-May-2025
+ 13:25:27 GMT; path=/; secure; HttpOnly; SameSite=None, x-ms-gateway-slice=estsfd;
+ path=/; secure; samesite=none; httponly, stsservicecookie=estsfd; path=/;
+ secure; samesite=none; httponly
+ Date:
+ - Mon, 07 Apr 2025 13:25:27 GMT
+ Content-Length:
+ - '707'
+ body:
+ encoding: UTF-8
+ string: '{"error":"unauthorized_client","error_description":"AADSTS700016: Application
+ with identifier ''wrong'' was not found in the directory ''MSFT''. This can
+ happen if the application has not been installed by the administrator of the
+ tenant or consented to by any user in the tenant. You may have sent your authentication
+ request to the wrong tenant. Trace ID: 65abb2e7-9ea0-4b11-ba8a-2e2bfcec4200
+ Correlation ID: 194e4908-91a7-440c-947d-bf5faf49403f Timestamp: 2025-04-07
+ 13:25:27Z","error_codes":[700016],"timestamp":"2025-04-07 13:25:27Z","trace_id":"65abb2e7-9ea0-4b11-ba8a-2e2bfcec4200","correlation_id":"194e4908-91a7-440c-947d-bf5faf49403f","error_uri":"https://login.microsoftonline.com/error?code=700016"}'
+ recorded_at: Mon, 07 Apr 2025 13:25:27 GMT
+recorded_with: VCR 6.3.1
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_wrong_client_secret.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_wrong_client_secret.yml
new file mode 100644
index 00000000000..4f7bdd9cb57
--- /dev/null
+++ b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_wrong_client_secret.yml
@@ -0,0 +1,72 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://login.microsoftonline.com/4d44bf36-9b56-45c0-8807-bbf386dd047f/oauth2/v2.0/token
+ body:
+ encoding: ASCII-8BIT
+ string: grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default+offline_access&client_id=b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff&client_secret=wrong
+ headers:
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Type:
+ - application/x-www-form-urlencoded
+ Content-Length:
+ - '162'
+ response:
+ status:
+ code: 401
+ message: Unauthorized
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Pragma:
+ - no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Expires:
+ - "-1"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains
+ X-Content-Type-Options:
+ - nosniff
+ P3p:
+ - CP="DSP CUR OTPi IND OTRi ONL FIN"
+ X-Ms-Request-Id:
+ - a6f0383f-bbcb-48f5-a08d-36a574b40d00
+ X-Ms-Ests-Server:
+ - 2.1.20465.4 - WEULR1 ProdSlices
+ X-Ms-Srs:
+ - 1.P
+ Content-Security-Policy-Report-Only:
+ - object-src 'none'; base-uri 'self'; script-src 'self' 'nonce-qNTRKH43OU5V1dsSJzZDuQ'
+ 'unsafe-inline' 'unsafe-eval' https://*.msauth.net https://*.msftauth.net
+ https://*.msftauthimages.net https://*.msauthimages.net https://*.msidentity.com
+ https://*.microsoftonline-p.com https://*.microsoftazuread-sso.com https://*.azureedge.net
+ https://*.outlook.com https://*.office.com https://*.office365.com https://*.microsoft.com
+ https://*.bing.com 'report-sample'; report-uri https://csp.microsoft.com/report/ESTS-UX-All
+ X-Xss-Protection:
+ - '0'
+ Set-Cookie:
+ - fpc=AsH2DTOUkrFPnjpcckwANXxRVKK0AQAAAMnIhd8OAAAA; expires=Wed, 07-May-2025
+ 13:23:21 GMT; path=/; secure; HttpOnly; SameSite=None, x-ms-gateway-slice=estsfd;
+ path=/; secure; samesite=none; httponly, stsservicecookie=estsfd; path=/;
+ secure; samesite=none; httponly
+ Date:
+ - Mon, 07 Apr 2025 13:23:21 GMT
+ Content-Length:
+ - '623'
+ body:
+ encoding: UTF-8
+ string: '{"error":"invalid_client","error_description":"AADSTS7000215: Invalid
+ client secret provided. Ensure the secret being sent in the request is the
+ client secret value, not the client secret ID, for a secret added to app ''b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff''.
+ Trace ID: a6f0383f-bbcb-48f5-a08d-36a574b40d00 Correlation ID: 80ce0729-9980-4683-a4f8-39cfd3e9c96c
+ Timestamp: 2025-04-07 13:23:21Z","error_codes":[7000215],"timestamp":"2025-04-07
+ 13:23:21Z","trace_id":"a6f0383f-bbcb-48f5-a08d-36a574b40d00","correlation_id":"80ce0729-9980-4683-a4f8-39cfd3e9c96c","error_uri":"https://login.microsoftonline.com/error?code=7000215"}'
+ recorded_at: Mon, 07 Apr 2025 13:23:21 GMT
+recorded_with: VCR 6.3.1
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_wrong_tenant_id.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_wrong_tenant_id.yml
new file mode 100644
index 00000000000..7c908f6f820
--- /dev/null
+++ b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validation_wrong_tenant_id.yml
@@ -0,0 +1,73 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://login.microsoftonline.com/itdoesnotexists9000.sharepoint.com/oauth2/v2.0/token
+ body:
+ encoding: ASCII-8BIT
+ string: grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default+offline_access&client_id=b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff&client_secret=nB58Q%7EwWf6.4zUIojn2nzkrpzDWlkQtDSWPcLa6R
+ headers:
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Type:
+ - application/x-www-form-urlencoded
+ Content-Length:
+ - '199'
+ response:
+ status:
+ code: 400
+ message: Bad Request
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Pragma:
+ - no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Expires:
+ - "-1"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains
+ X-Content-Type-Options:
+ - nosniff
+ P3p:
+ - CP="DSP CUR OTPi IND OTRi ONL FIN"
+ X-Ms-Request-Id:
+ - 2a8d5b96-aa99-443e-a69e-03f02c2e4c00
+ X-Ms-Ests-Server:
+ - 2.1.20465.4 - NEULR1 ProdSlices
+ X-Ms-Srs:
+ - 1.P
+ Content-Security-Policy-Report-Only:
+ - object-src 'none'; base-uri 'self'; script-src 'self' 'nonce-9cTLDpFlQVtcJ6DtcOmi3g'
+ 'unsafe-inline' 'unsafe-eval' https://*.msauth.net https://*.msftauth.net
+ https://*.msftauthimages.net https://*.msauthimages.net https://*.msidentity.com
+ https://*.microsoftonline-p.com https://*.microsoftazuread-sso.com https://*.azureedge.net
+ https://*.outlook.com https://*.office.com https://*.office365.com https://*.microsoft.com
+ https://*.bing.com 'report-sample'; report-uri https://csp.microsoft.com/report/ESTS-UX-All
+ X-Xss-Protection:
+ - '0'
+ Set-Cookie:
+ - fpc=AscnkNupYDJFowKyY6L32lERw07PAQAAAI29hd8OAAAA; expires=Wed, 07-May-2025
+ 12:35:26 GMT; path=/; secure; HttpOnly; SameSite=None, x-ms-gateway-slice=estsfd;
+ path=/; secure; samesite=none; httponly, stsservicecookie=estsfd; path=/;
+ secure; samesite=none; httponly
+ Date:
+ - Mon, 07 Apr 2025 12:35:26 GMT
+ Content-Length:
+ - '680'
+ body:
+ encoding: UTF-8
+ string: '{"error":"invalid_request","error_description":"AADSTS90002: Tenant
+ ''itdoesnotexists9000.sharepoint.com'' not found. Check to make sure you have
+ the correct tenant ID and are signing into the correct cloud. Check with your
+ subscription administrator, this may happen if there are no active subscriptions
+ for the tenant. Trace ID: 2a8d5b96-aa99-443e-a69e-03f02c2e4c00 Correlation
+ ID: a1e47b18-9902-42ed-bb50-6478c5aa98cd Timestamp: 2025-04-07 12:35:26Z","error_codes":[90002],"timestamp":"2025-04-07
+ 12:35:26Z","trace_id":"2a8d5b96-aa99-443e-a69e-03f02c2e4c00","correlation_id":"a1e47b18-9902-42ed-bb50-6478c5aa98cd","error_uri":"https://login.microsoftonline.com/error?code=90002"}'
+ recorded_at: Mon, 07 Apr 2025 12:35:26 GMT
+recorded_with: VCR 6.3.1
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validator_ampf_clean_run.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validator_ampf_clean_run.yml
new file mode 100644
index 00000000000..4fe63906717
--- /dev/null
+++ b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validator_ampf_clean_run.yml
@@ -0,0 +1,240 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://login.microsoftonline.com/4d44bf36-9b56-45c0-8807-bbf386dd047f/oauth2/v2.0/token
+ body:
+ encoding: ASCII-8BIT
+ string: grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default+offline_access&client_id=b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff&client_secret=nB58Q%7EwWf6.4zUIojn2nzkrpzDWlkQtDSWPcLa6R
+ headers:
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Type:
+ - application/x-www-form-urlencoded
+ Content-Length:
+ - '199'
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Pragma:
+ - no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Expires:
+ - "-1"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains
+ X-Content-Type-Options:
+ - nosniff
+ P3p:
+ - CP="DSP CUR OTPi IND OTRi ONL FIN"
+ X-Ms-Request-Id:
+ - 75da94cc-f93b-4957-9472-7f69b50a0500
+ X-Ms-Ests-Server:
+ - 2.1.20465.4 - WEULR1 ProdSlices
+ X-Ms-Srs:
+ - 1.P
+ Content-Security-Policy-Report-Only:
+ - object-src 'none'; base-uri 'self'; script-src 'self' 'nonce-EnkQImmzmdKVuOaBI_JV2w'
+ 'unsafe-inline' 'unsafe-eval' https://*.msauth.net https://*.msftauth.net
+ https://*.msftauthimages.net https://*.msauthimages.net https://*.msidentity.com
+ https://*.microsoftonline-p.com https://*.microsoftazuread-sso.com https://*.azureedge.net
+ https://*.outlook.com https://*.office.com https://*.office365.com https://*.microsoft.com
+ https://*.bing.com 'report-sample'; report-uri https://csp.microsoft.com/report/ESTS-UX-All
+ X-Xss-Protection:
+ - '0'
+ Set-Cookie:
+ - fpc=AufQETJp6LNOiwGffjFiBhtRVKK0AQAAAKMlh98OAAAA; expires=Thu, 08-May-2025
+ 14:11:48 GMT; path=/; secure; HttpOnly; SameSite=None, x-ms-gateway-slice=estsfd;
+ path=/; secure; samesite=none; httponly, stsservicecookie=estsfd; path=/;
+ secure; samesite=none; httponly
+ Date:
+ - Tue, 08 Apr 2025 14:11:48 GMT
+ Content-Length:
+ - '1695'
+ body:
+ encoding: UTF-8
+ string: '{"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":""}'
+ recorded_at: Tue, 08 Apr 2025 14:11:48 GMT
+- request:
+ method: post
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root/children
+ body:
+ encoding: UTF-8
+ string: '{"name":"ConnectionValidatorFolder","folder":{},"@microsoft.graph.conflictBehavior":"fail"}'
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Length:
+ - '91'
+ Authorization:
+ - Bearer
+ response:
+ status:
+ code: 201
+ message: Created
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Content-Type:
+ - application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8
+ Content-Encoding:
+ - gzip
+ Etag:
+ - '"{DB39CBFF-7643-4C26-801D-7C3E22B888BA},1"'
+ Location:
+ - https://ymt6d.sharepoint.com/_api/v2.0/drives('b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs')/items('root')/children('01ANJ53W77ZM45WQ3WEZGIAHL4HYRLRCF2')
+ Vary:
+ - Accept-Encoding
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - 7454b765-25ac-43f0-b1e2-9f4ae7122668
+ Client-Request-Id:
+ - 7454b765-25ac-43f0-b1e2-9f4ae7122668
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"000","RoleInstance":"FR1PEPF00001A2F"}}'
+ Odata-Version:
+ - '4.0'
+ Date:
+ - Tue, 08 Apr 2025 14:11:48 GMT
+ body:
+ encoding: UTF-8
+ string: '{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#drives(''b%21FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY8CfNaHr_0ERYs5kgmEWFrX'')/root/children/$entity","@odata.etag":"\"{DB39CBFF-7643-4C26-801D-7C3E22B888BA},1\"","createdDateTime":"2025-04-08T14:11:49Z","eTag":"\"{DB39CBFF-7643-4C26-801D-7C3E22B888BA},1\"","id":"01ANJ53W77ZM45WQ3WEZGIAHL4HYRLRCF2","lastModifiedDateTime":"2025-04-08T14:11:49Z","name":"ConnectionValidatorFolder","size":0,"webUrl":"https://ymt6d.sharepoint.com/sites/OPTest/Marcello%20AMPF/ConnectionValidatorFolder","cTag":"\"c:{DB39CBFF-7643-4C26-801D-7C3E22B888BA},0\"","commentSettings":{"commentingDisabled":{"isDisabled":false}},"createdBy":{"application":{"displayName":"OP
+ Auth","id":"b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff"},"user":{"displayName":"SharePoint
+ App"}},"lastModifiedBy":{"application":{"displayName":"OP Auth","id":"b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff"},"user":{"displayName":"SharePoint
+ App"}},"parentReference":{"driveId":"b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs","driveType":"documentLibrary","id":"01ANJ53W56Y2GOVW7725BZO354PWSELRRZ","path":"/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root:","sharepointIds":{"listId":"87d67c02-fdaf-4504-8b39-920984585ad7","listItemUniqueId":"458e6945-3722-40b1-946f-4743a2d480f2","siteId":"1099e315-d0c7-47c7-8640-aa9504b70fff","siteUrl":"https://ymt6d.sharepoint.com/sites/OPTest","tenantId":"4d44bf36-9b56-45c0-8807-bbf386dd047f","webId":"6af246f0-e3be-45ef-b91a-8eca4dcc5d8f"}},"fileSystemInfo":{"createdDateTime":"2025-04-08T14:11:49Z","lastModifiedDateTime":"2025-04-08T14:11:49Z"},"folder":{"childCount":0,"view":{"sortBy":"name","sortOrder":"ascending","viewType":"thumbnails"}},"shared":{"scope":"unknown"}}'
+ recorded_at: Tue, 08 Apr 2025 14:11:48 GMT
+- request:
+ method: delete
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/items/01ANJ53W77ZM45WQ3WEZGIAHL4HYRLRCF2
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Authorization:
+ - Bearer
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ response:
+ status:
+ code: 204
+ message: No Content
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - 8458dc49-bea8-4d56-86ef-b519ef910600
+ Client-Request-Id:
+ - 8458dc49-bea8-4d56-86ef-b519ef910600
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"000","RoleInstance":"FR1PEPF000007A3"}}'
+ Date:
+ - Tue, 08 Apr 2025 14:11:49 GMT
+ body:
+ encoding: UTF-8
+ string: ''
+ recorded_at: Tue, 08 Apr 2025 14:11:49 GMT
+- request:
+ method: get
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root/children?$select=id,name,size,webUrl,lastModifiedBy,createdBy,fileSystemInfo,file,folder,parentReference
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Authorization:
+ - Bearer
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Content-Type:
+ - application/json; odata.metadata=minimal; odata.streaming=true; IEEE754Compatible=false;
+ charset=utf-8
+ Content-Encoding:
+ - gzip
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - 9f49a172-9d26-4a50-966d-fa5fe4911dff
+ Client-Request-Id:
+ - 9f49a172-9d26-4a50-966d-fa5fe4911dff
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"000","RoleInstance":"FR1PEPF0000149B"}}'
+ Date:
+ - Tue, 08 Apr 2025 14:11:48 GMT
+ body:
+ encoding: UTF-8
+ string: '{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#Collection(driveItem)","value":[]}'
+ recorded_at: Tue, 08 Apr 2025 14:11:49 GMT
+- request:
+ method: get
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root?$select=id,name,size,webUrl,lastModifiedBy,createdBy,fileSystemInfo,file,folder,parentReference
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Authorization:
+ - Bearer
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Content-Type:
+ - application/json; odata.metadata=minimal; odata.streaming=true; IEEE754Compatible=false;
+ charset=utf-8
+ Content-Encoding:
+ - gzip
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - 2a8bae40-ed69-49f2-a9f9-1960a5cdf5b1
+ Client-Request-Id:
+ - 2a8bae40-ed69-49f2-a9f9-1960a5cdf5b1
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"000","RoleInstance":"FR1PEPF0000149B"}}'
+ Date:
+ - Tue, 08 Apr 2025 14:11:48 GMT
+ body:
+ encoding: UTF-8
+ string: '{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#Collection(driveItem)/$entity","id":"01ANJ53W56Y2GOVW7725BZO354PWSELRRZ","name":"root","parentReference":{"driveType":"documentLibrary","driveId":"b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs"},"webUrl":"https://ymt6d.sharepoint.com/sites/OPTest/Marcello%20AMPF","fileSystemInfo":{"createdDateTime":"2025-04-07T12:03:17Z","lastModifiedDateTime":"2025-04-08T14:11:49Z"},"folder":{"childCount":0},"size":0}'
+ recorded_at: Tue, 08 Apr 2025 14:11:49 GMT
+recorded_with: VCR 6.3.1
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validator_create_folder.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validator_create_folder.yml
new file mode 100644
index 00000000000..e0549cc064c
--- /dev/null
+++ b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validator_create_folder.yml
@@ -0,0 +1,208 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://login.microsoftonline.com/4d44bf36-9b56-45c0-8807-bbf386dd047f/oauth2/v2.0/token
+ body:
+ encoding: ASCII-8BIT
+ string: grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default+offline_access&client_id=b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff&client_secret=nB58Q%7EwWf6.4zUIojn2nzkrpzDWlkQtDSWPcLa6R
+ headers:
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Type:
+ - application/x-www-form-urlencoded
+ Content-Length:
+ - '199'
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Pragma:
+ - no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Expires:
+ - "-1"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains
+ X-Content-Type-Options:
+ - nosniff
+ P3p:
+ - CP="DSP CUR OTPi IND OTRi ONL FIN"
+ X-Ms-Request-Id:
+ - cc781a8e-8383-4c4e-8f6a-354c2bd91a00
+ X-Ms-Ests-Server:
+ - 2.1.20465.4 - SEC ProdSlices
+ X-Ms-Srs:
+ - 1.P
+ Content-Security-Policy-Report-Only:
+ - object-src 'none'; base-uri 'self'; script-src 'self' 'nonce-sfaLk_7kQLXPiWO9sEYnIw'
+ 'unsafe-inline' 'unsafe-eval' https://*.msauth.net https://*.msftauth.net
+ https://*.msftauthimages.net https://*.msauthimages.net https://*.msidentity.com
+ https://*.microsoftonline-p.com https://*.microsoftazuread-sso.com https://*.azureedge.net
+ https://*.outlook.com https://*.office.com https://*.office365.com https://*.microsoft.com
+ https://*.bing.com 'report-sample'; report-uri https://csp.microsoft.com/report/ESTS-UX-All
+ X-Xss-Protection:
+ - '0'
+ Set-Cookie:
+ - fpc=AsrHTiwxKD9KmuMnc_TQ-gtRVKK0AQAAAEwih98OAAAA; expires=Thu, 08-May-2025
+ 13:57:33 GMT; path=/; secure; HttpOnly; SameSite=None, x-ms-gateway-slice=estsfd;
+ path=/; secure; samesite=none; httponly, stsservicecookie=estsfd; path=/;
+ secure; samesite=none; httponly
+ Date:
+ - Tue, 08 Apr 2025 13:57:32 GMT
+ Content-Length:
+ - '1696'
+ body:
+ encoding: UTF-8
+ string: '{"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":""}'
+ recorded_at: Tue, 08 Apr 2025 13:57:33 GMT
+- request:
+ method: post
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root/children
+ body:
+ encoding: UTF-8
+ string: '{"name":"ConnectionValidatorFolder","folder":{},"@microsoft.graph.conflictBehavior":"fail"}'
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Length:
+ - '91'
+ Authorization:
+ - Bearer
+ response:
+ status:
+ code: 201
+ message: Created
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Content-Type:
+ - application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8
+ Content-Encoding:
+ - gzip
+ Etag:
+ - '"{33CB5CFC-6E8E-4B49-9503-9E79A62D07ED},1"'
+ Location:
+ - https://ymt6d.sharepoint.com/_api/v2.0/drives('b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs')/items('root')/children('01ANJ53W74LTFTHDTOJFFZKA46PGTC2B7N')
+ Vary:
+ - Accept-Encoding
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - 557973a9-0916-4420-b20c-cfe22c147f78
+ Client-Request-Id:
+ - 557973a9-0916-4420-b20c-cfe22c147f78
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"000","RoleInstance":"FR1PEPF00000C1C"}}'
+ Odata-Version:
+ - '4.0'
+ Date:
+ - Tue, 08 Apr 2025 13:57:33 GMT
+ body:
+ encoding: UTF-8
+ string: '{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#drives(''b%21FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW'')/root/children/$entity","@odata.etag":"\"{33CB5CFC-6E8E-4B49-9503-9E79A62D07ED},1\"","createdDateTime":"2025-04-08T13:57:34Z","eTag":"\"{33CB5CFC-6E8E-4B49-9503-9E79A62D07ED},1\"","id":"01ANJ53W74LTFTHDTOJFFZKA46PGTC2B7N","lastModifiedDateTime":"2025-04-08T13:57:34Z","name":"ConnectionValidatorFolder","size":0,"webUrl":"https://ymt6d.sharepoint.com/sites/OPTest/Marcello%20VCR/ConnectionValidatorFolder","cTag":"\"c:{33CB5CFC-6E8E-4B49-9503-9E79A62D07ED},0\"","commentSettings":{"commentingDisabled":{"isDisabled":false}},"createdBy":{"application":{"displayName":"OP
+ Auth","id":"b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff"},"user":{"displayName":"SharePoint
+ App"}},"lastModifiedBy":{"application":{"displayName":"OP Auth","id":"b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff"},"user":{"displayName":"SharePoint
+ App"}},"parentReference":{"driveId":"b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs","driveType":"documentLibrary","id":"01ANJ53W56Y2GOVW7725BZO354PWSELRRZ","path":"/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root:","sharepointIds":{"listId":"5ea9a363-3a24-4fab-b9ae-cef9a25a3156","listItemUniqueId":"6bc8b02c-f419-4bec-8531-be5b69948a7d","siteId":"1099e315-d0c7-47c7-8640-aa9504b70fff","siteUrl":"https://ymt6d.sharepoint.com/sites/OPTest","tenantId":"4d44bf36-9b56-45c0-8807-bbf386dd047f","webId":"6af246f0-e3be-45ef-b91a-8eca4dcc5d8f"}},"fileSystemInfo":{"createdDateTime":"2025-04-08T13:57:34Z","lastModifiedDateTime":"2025-04-08T13:57:34Z"},"folder":{"childCount":0,"view":{"sortBy":"name","sortOrder":"ascending","viewType":"thumbnails"}},"shared":{"scope":"unknown"}}'
+ recorded_at: Tue, 08 Apr 2025 13:57:34 GMT
+- request:
+ method: get
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root/children?$select=id,name,size,webUrl,lastModifiedBy,createdBy,fileSystemInfo,file,folder,parentReference
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Authorization:
+ - Bearer
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Content-Type:
+ - application/json; odata.metadata=minimal; odata.streaming=true; IEEE754Compatible=false;
+ charset=utf-8
+ Content-Encoding:
+ - gzip
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - b9666e96-42f3-4c2f-abcc-03678ac30518
+ Client-Request-Id:
+ - b9666e96-42f3-4c2f-abcc-03678ac30518
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"000","RoleInstance":"FR1PEPF000007A8"}}'
+ Date:
+ - Tue, 08 Apr 2025 13:57:33 GMT
+ body:
+ encoding: UTF-8
+ string: '{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#Collection(driveItem)","value":[{"@odata.etag":"\"{33CB5CFC-6E8E-4B49-9503-9E79A62D07ED},2\"","createdBy":{"application":{"id":"b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff","displayName":"OP
+ Auth"},"user":{"displayName":"SharePoint App"}},"id":"01ANJ53W74LTFTHDTOJFFZKA46PGTC2B7N","lastModifiedBy":{"application":{"id":"b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff","displayName":"OP
+ Auth"},"user":{"displayName":"SharePoint App"}},"name":"ConnectionValidatorFolder","parentReference":{"driveType":"documentLibrary","driveId":"b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs","id":"01ANJ53W56Y2GOVW7725BZO354PWSELRRZ","name":"Marcello
+ VCR","path":"/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root:","siteId":"1099e315-d0c7-47c7-8640-aa9504b70fff"},"webUrl":"https://ymt6d.sharepoint.com/sites/OPTest/Marcello%20VCR/ConnectionValidatorFolder","fileSystemInfo":{"createdDateTime":"2025-04-08T13:57:34Z","lastModifiedDateTime":"2025-04-08T13:57:34Z"},"folder":{"childCount":0},"size":0},{"@odata.etag":"\"{8DA9DCAF-43E4-419C-B52E-F1303A0D3372},1\"","createdBy":{"user":{"email":"op.admin@ymt6d.onmicrosoft.com","id":"5b5a7dc4-4539-41ba-9fa9-100f0a26acb7","displayName":"Eric
+ Schubert"}},"id":"01ANJ53W5P3SUY3ZCDTRA3KLXRGA5A2M3S","lastModifiedBy":{"user":{"email":"op.admin@ymt6d.onmicrosoft.com","id":"5b5a7dc4-4539-41ba-9fa9-100f0a26acb7","displayName":"Eric
+ Schubert"}},"name":"data","parentReference":{"driveType":"documentLibrary","driveId":"b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs","id":"01ANJ53W56Y2GOVW7725BZO354PWSELRRZ","name":"Marcello
+ VCR","path":"/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root:","siteId":"1099e315-d0c7-47c7-8640-aa9504b70fff"},"webUrl":"https://ymt6d.sharepoint.com/sites/OPTest/Marcello%20VCR/data","fileSystemInfo":{"createdDateTime":"2025-04-07T12:02:26Z","lastModifiedDateTime":"2025-04-07T12:02:26Z"},"folder":{"childCount":0},"size":0},{"@odata.etag":"\"{992C5835-AA54-4B71-B3AF-197B1F4578E8},2\"","createdBy":{"user":{"email":"op.admin@ymt6d.onmicrosoft.com","id":"5b5a7dc4-4539-41ba-9fa9-100f0a26acb7","displayName":"Eric
+ Schubert"}},"id":"01ANJ53WZVLAWJSVFKOFF3HLYZPMPUK6HI","lastModifiedBy":{"user":{"email":"op.admin@ymt6d.onmicrosoft.com","id":"5b5a7dc4-4539-41ba-9fa9-100f0a26acb7","displayName":"Eric
+ Schubert"}},"name":"simply_oidc.jpg","parentReference":{"driveType":"documentLibrary","driveId":"b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs","id":"01ANJ53W56Y2GOVW7725BZO354PWSELRRZ","name":"Marcello
+ VCR","path":"/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root:","siteId":"1099e315-d0c7-47c7-8640-aa9504b70fff"},"webUrl":"https://ymt6d.sharepoint.com/sites/OPTest/Marcello%20VCR/simply_oidc.jpg","file":{"hashes":{"quickXorHash":"qzqT/ZJgJnvhu77aF+2RcdH62Sc="},"mimeType":"image/jpeg"},"fileSystemInfo":{"createdDateTime":"2025-04-07T12:02:42Z","lastModifiedDateTime":"2025-04-07T12:02:42Z"},"size":56483}]}'
+ recorded_at: Tue, 08 Apr 2025 13:57:34 GMT
+- request:
+ method: delete
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/items/01ANJ53W74LTFTHDTOJFFZKA46PGTC2B7N
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Authorization:
+ - Bearer
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ response:
+ status:
+ code: 204
+ message: No Content
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - b14d3091-56c0-4f21-9f7c-4ed0f4c7acc4
+ Client-Request-Id:
+ - b14d3091-56c0-4f21-9f7c-4ed0f4c7acc4
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"000","RoleInstance":"FR1PEPF0000107E"}}'
+ Date:
+ - Tue, 08 Apr 2025 13:57:33 GMT
+ body:
+ encoding: UTF-8
+ string: ''
+ recorded_at: Tue, 08 Apr 2025 13:57:34 GMT
+recorded_with: VCR 6.3.1
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validator_extraneous_files.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validator_extraneous_files.yml
new file mode 100644
index 00000000000..aec24c49724
--- /dev/null
+++ b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validator_extraneous_files.yml
@@ -0,0 +1,205 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://login.microsoftonline.com/4d44bf36-9b56-45c0-8807-bbf386dd047f/oauth2/v2.0/token
+ body:
+ encoding: ASCII-8BIT
+ string: grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default+offline_access&client_id=b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff&client_secret=nB58Q%7EwWf6.4zUIojn2nzkrpzDWlkQtDSWPcLa6R
+ headers:
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Type:
+ - application/x-www-form-urlencoded
+ Content-Length:
+ - '199'
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Pragma:
+ - no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Expires:
+ - "-1"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains
+ X-Content-Type-Options:
+ - nosniff
+ P3p:
+ - CP="DSP CUR OTPi IND OTRi ONL FIN"
+ X-Ms-Request-Id:
+ - 176612aa-10fa-4904-9d12-085a28152100
+ X-Ms-Ests-Server:
+ - 2.1.20465.4 - NEULR1 ProdSlices
+ X-Ms-Srs:
+ - 1.P
+ Content-Security-Policy-Report-Only:
+ - object-src 'none'; base-uri 'self'; script-src 'self' 'nonce-Zd8MbfrL29hkV1jA5397lg'
+ 'unsafe-inline' 'unsafe-eval' https://*.msauth.net https://*.msftauth.net
+ https://*.msftauthimages.net https://*.msauthimages.net https://*.msidentity.com
+ https://*.microsoftonline-p.com https://*.microsoftazuread-sso.com https://*.azureedge.net
+ https://*.outlook.com https://*.office.com https://*.office365.com https://*.microsoft.com
+ https://*.bing.com 'report-sample'; report-uri https://csp.microsoft.com/report/ESTS-UX-All
+ X-Xss-Protection:
+ - '0'
+ Set-Cookie:
+ - fpc=AkkUsDa-MSdNrojqp62cRydRVKK0AQAAAPIjh98OAAAA; expires=Thu, 08-May-2025
+ 14:04:34 GMT; path=/; secure; HttpOnly; SameSite=None, x-ms-gateway-slice=estsfd;
+ path=/; secure; samesite=none; httponly, stsservicecookie=estsfd; path=/;
+ secure; samesite=none; httponly
+ Date:
+ - Tue, 08 Apr 2025 14:04:34 GMT
+ Content-Length:
+ - '1701'
+ body:
+ encoding: UTF-8
+ string: '{"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":""}'
+ recorded_at: Tue, 08 Apr 2025 14:04:34 GMT
+- request:
+ method: post
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root/children
+ body:
+ encoding: UTF-8
+ string: '{"name":"ConnectionValidatorFolder","folder":{},"@microsoft.graph.conflictBehavior":"fail"}'
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Length:
+ - '91'
+ Authorization:
+ - Bearer
+ response:
+ status:
+ code: 201
+ message: Created
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Content-Type:
+ - application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8
+ Content-Encoding:
+ - gzip
+ Etag:
+ - '"{610A0BB0-1141-4B56-A0A3-15CB69398698},1"'
+ Location:
+ - https://ymt6d.sharepoint.com/_api/v2.0/drives('b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs')/items('root')/children('01ANJ53W5QBMFGCQIRKZF2BIYVZNUTTBUY')
+ Vary:
+ - Accept-Encoding
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - c596ba9b-d722-46be-927e-123f12e0e946
+ Client-Request-Id:
+ - c596ba9b-d722-46be-927e-123f12e0e946
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"002","RoleInstance":"FR3PEPF00000AB6"}}'
+ Odata-Version:
+ - '4.0'
+ Date:
+ - Tue, 08 Apr 2025 14:04:34 GMT
+ body:
+ encoding: UTF-8
+ string: '{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#drives(''b%21FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW'')/root/children/$entity","@odata.etag":"\"{610A0BB0-1141-4B56-A0A3-15CB69398698},1\"","createdDateTime":"2025-04-08T14:04:35Z","eTag":"\"{610A0BB0-1141-4B56-A0A3-15CB69398698},1\"","id":"01ANJ53W5QBMFGCQIRKZF2BIYVZNUTTBUY","lastModifiedDateTime":"2025-04-08T14:04:35Z","name":"ConnectionValidatorFolder","size":0,"webUrl":"https://ymt6d.sharepoint.com/sites/OPTest/Marcello%20VCR/ConnectionValidatorFolder","cTag":"\"c:{610A0BB0-1141-4B56-A0A3-15CB69398698},0\"","commentSettings":{"commentingDisabled":{"isDisabled":false}},"createdBy":{"application":{"displayName":"OP
+ Auth","id":"b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff"},"user":{"displayName":"SharePoint
+ App"}},"lastModifiedBy":{"application":{"displayName":"OP Auth","id":"b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff"},"user":{"displayName":"SharePoint
+ App"}},"parentReference":{"driveId":"b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs","driveType":"documentLibrary","id":"01ANJ53W56Y2GOVW7725BZO354PWSELRRZ","path":"/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root:","sharepointIds":{"listId":"5ea9a363-3a24-4fab-b9ae-cef9a25a3156","listItemUniqueId":"6bc8b02c-f419-4bec-8531-be5b69948a7d","siteId":"1099e315-d0c7-47c7-8640-aa9504b70fff","siteUrl":"https://ymt6d.sharepoint.com/sites/OPTest","tenantId":"4d44bf36-9b56-45c0-8807-bbf386dd047f","webId":"6af246f0-e3be-45ef-b91a-8eca4dcc5d8f"}},"fileSystemInfo":{"createdDateTime":"2025-04-08T14:04:35Z","lastModifiedDateTime":"2025-04-08T14:04:35Z"},"folder":{"childCount":0,"view":{"sortBy":"name","sortOrder":"ascending","viewType":"thumbnails"}},"shared":{"scope":"unknown"}}'
+ recorded_at: Tue, 08 Apr 2025 14:04:35 GMT
+- request:
+ method: delete
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/items/01ANJ53W5QBMFGCQIRKZF2BIYVZNUTTBUY
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Authorization:
+ - Bearer
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ response:
+ status:
+ code: 204
+ message: No Content
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - ae547ca3-7b02-4039-b9b4-3e2bc3f61816
+ Client-Request-Id:
+ - ae547ca3-7b02-4039-b9b4-3e2bc3f61816
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"002","RoleInstance":"FR3PEPF0000055B"}}'
+ Date:
+ - Tue, 08 Apr 2025 14:04:35 GMT
+ body:
+ encoding: UTF-8
+ string: ''
+ recorded_at: Tue, 08 Apr 2025 14:04:35 GMT
+- request:
+ method: get
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root/children?$select=id,name,size,webUrl,lastModifiedBy,createdBy,fileSystemInfo,file,folder,parentReference
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Authorization:
+ - Bearer
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Content-Type:
+ - application/json; odata.metadata=minimal; odata.streaming=true; IEEE754Compatible=false;
+ charset=utf-8
+ Content-Encoding:
+ - gzip
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - 67bed2c6-b993-4cbf-a597-5dc017c4285b
+ Client-Request-Id:
+ - 67bed2c6-b993-4cbf-a597-5dc017c4285b
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"002","RoleInstance":"FR3PEPF00000AB9"}}'
+ Date:
+ - Tue, 08 Apr 2025 14:04:35 GMT
+ body:
+ encoding: UTF-8
+ string: '{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#Collection(driveItem)","value":[{"@odata.etag":"\"{8DA9DCAF-43E4-419C-B52E-F1303A0D3372},1\"","createdBy":{"user":{"email":"op.admin@ymt6d.onmicrosoft.com","id":"5b5a7dc4-4539-41ba-9fa9-100f0a26acb7","displayName":"Eric
+ Schubert"}},"id":"01ANJ53W5P3SUY3ZCDTRA3KLXRGA5A2M3S","lastModifiedBy":{"user":{"email":"op.admin@ymt6d.onmicrosoft.com","id":"5b5a7dc4-4539-41ba-9fa9-100f0a26acb7","displayName":"Eric
+ Schubert"}},"name":"data","parentReference":{"driveType":"documentLibrary","driveId":"b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs","id":"01ANJ53W56Y2GOVW7725BZO354PWSELRRZ","name":"Marcello
+ VCR","path":"/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root:","siteId":"1099e315-d0c7-47c7-8640-aa9504b70fff"},"webUrl":"https://ymt6d.sharepoint.com/sites/OPTest/Marcello%20VCR/data","fileSystemInfo":{"createdDateTime":"2025-04-07T12:02:26Z","lastModifiedDateTime":"2025-04-07T12:02:26Z"},"folder":{"childCount":0},"size":0},{"@odata.etag":"\"{992C5835-AA54-4B71-B3AF-197B1F4578E8},2\"","createdBy":{"user":{"email":"op.admin@ymt6d.onmicrosoft.com","id":"5b5a7dc4-4539-41ba-9fa9-100f0a26acb7","displayName":"Eric
+ Schubert"}},"id":"01ANJ53WZVLAWJSVFKOFF3HLYZPMPUK6HI","lastModifiedBy":{"user":{"email":"op.admin@ymt6d.onmicrosoft.com","id":"5b5a7dc4-4539-41ba-9fa9-100f0a26acb7","displayName":"Eric
+ Schubert"}},"name":"simply_oidc.jpg","parentReference":{"driveType":"documentLibrary","driveId":"b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs","id":"01ANJ53W56Y2GOVW7725BZO354PWSELRRZ","name":"Marcello
+ VCR","path":"/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root:","siteId":"1099e315-d0c7-47c7-8640-aa9504b70fff"},"webUrl":"https://ymt6d.sharepoint.com/sites/OPTest/Marcello%20VCR/simply_oidc.jpg","file":{"hashes":{"quickXorHash":"qzqT/ZJgJnvhu77aF+2RcdH62Sc="},"mimeType":"image/jpeg"},"fileSystemInfo":{"createdDateTime":"2025-04-07T12:02:42Z","lastModifiedDateTime":"2025-04-07T12:02:42Z"},"size":56483}]}'
+ recorded_at: Tue, 08 Apr 2025 14:04:35 GMT
+recorded_with: VCR 6.3.1
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validator_test_folder_already_exists.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validator_test_folder_already_exists.yml
new file mode 100644
index 00000000000..72b661cacff
--- /dev/null
+++ b/modules/storages/spec/support/fixtures/vcr_cassettes/one_drive/validator_test_folder_already_exists.yml
@@ -0,0 +1,248 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://login.microsoftonline.com/4d44bf36-9b56-45c0-8807-bbf386dd047f/oauth2/v2.0/token
+ body:
+ encoding: ASCII-8BIT
+ string: grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default+offline_access&client_id=b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff&client_secret=nB58Q%7EwWf6.4zUIojn2nzkrpzDWlkQtDSWPcLa6R
+ headers:
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Type:
+ - application/x-www-form-urlencoded
+ Content-Length:
+ - '199'
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Pragma:
+ - no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Expires:
+ - "-1"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubDomains
+ X-Content-Type-Options:
+ - nosniff
+ P3p:
+ - CP="DSP CUR OTPi IND OTRi ONL FIN"
+ X-Ms-Request-Id:
+ - 61d16d60-7b4e-4d46-a540-824540685000
+ X-Ms-Ests-Server:
+ - 2.1.20465.4 - FRC ProdSlices
+ X-Ms-Srs:
+ - 1.P
+ Content-Security-Policy-Report-Only:
+ - object-src 'none'; base-uri 'self'; script-src 'self' 'nonce-Qu0I7ShVrVt-zJPL9d9rnw'
+ 'unsafe-inline' 'unsafe-eval' https://*.msauth.net https://*.msftauth.net
+ https://*.msftauthimages.net https://*.msauthimages.net https://*.msidentity.com
+ https://*.microsoftonline-p.com https://*.microsoftazuread-sso.com https://*.azureedge.net
+ https://*.outlook.com https://*.office.com https://*.office365.com https://*.microsoft.com
+ https://*.bing.com 'report-sample'; report-uri https://csp.microsoft.com/report/ESTS-UX-All
+ X-Xss-Protection:
+ - '0'
+ Set-Cookie:
+ - fpc=AsYLEKhH9htOoOEoaz18W89RVKK0AQAAAPSQiN8OAAAA; expires=Fri, 09-May-2025
+ 16:01:57 GMT; path=/; secure; HttpOnly; SameSite=None, x-ms-gateway-slice=estsfd;
+ path=/; secure; samesite=none; httponly, stsservicecookie=estsfd; path=/;
+ secure; samesite=none; httponly
+ Date:
+ - Wed, 09 Apr 2025 16:01:56 GMT
+ Content-Length:
+ - '1696'
+ body:
+ encoding: UTF-8
+ string: '{"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":""}'
+ recorded_at: Wed, 09 Apr 2025 16:01:57 GMT
+- request:
+ method: post
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root/children
+ body:
+ encoding: UTF-8
+ string: '{"name":"ConnectionValidatorFolder","folder":{},"@microsoft.graph.conflictBehavior":"fail"}'
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Length:
+ - '91'
+ Authorization:
+ - Bearer
+ response:
+ status:
+ code: 201
+ message: Created
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Content-Type:
+ - application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8
+ Content-Encoding:
+ - gzip
+ Etag:
+ - '"{14A012EF-8F6E-4AA3-B137-F12EC82C13C2},1"'
+ Location:
+ - https://ymt6d.sharepoint.com/_api/v2.0/drives('b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs')/items('root')/children('01ANJ53W7PCKQBI3UPUNFLCN7RF3ECYE6C')
+ Vary:
+ - Accept-Encoding
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - 6715ccb0-ed22-4c02-ad8c-3c71d3597a83
+ Client-Request-Id:
+ - 6715ccb0-ed22-4c02-ad8c-3c71d3597a83
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"003","RoleInstance":"FR1PEPF00001088"}}'
+ Odata-Version:
+ - '4.0'
+ Date:
+ - Wed, 09 Apr 2025 16:01:57 GMT
+ body:
+ encoding: UTF-8
+ string: '{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#drives(''b%21FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY8CfNaHr_0ERYs5kgmEWFrX'')/root/children/$entity","@odata.etag":"\"{14A012EF-8F6E-4AA3-B137-F12EC82C13C2},1\"","createdDateTime":"2025-04-09T16:01:58Z","eTag":"\"{14A012EF-8F6E-4AA3-B137-F12EC82C13C2},1\"","id":"01ANJ53W7PCKQBI3UPUNFLCN7RF3ECYE6C","lastModifiedDateTime":"2025-04-09T16:01:58Z","name":"ConnectionValidatorFolder","size":0,"webUrl":"https://ymt6d.sharepoint.com/sites/OPTest/Marcello%20AMPF/ConnectionValidatorFolder","cTag":"\"c:{14A012EF-8F6E-4AA3-B137-F12EC82C13C2},0\"","commentSettings":{"commentingDisabled":{"isDisabled":false}},"createdBy":{"application":{"displayName":"OP
+ Auth","id":"b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff"},"user":{"displayName":"SharePoint
+ App"}},"lastModifiedBy":{"application":{"displayName":"OP Auth","id":"b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff"},"user":{"displayName":"SharePoint
+ App"}},"parentReference":{"driveId":"b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs","driveType":"documentLibrary","id":"01ANJ53W56Y2GOVW7725BZO354PWSELRRZ","path":"/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root:","sharepointIds":{"listId":"87d67c02-fdaf-4504-8b39-920984585ad7","listItemUniqueId":"458e6945-3722-40b1-946f-4743a2d480f2","siteId":"1099e315-d0c7-47c7-8640-aa9504b70fff","siteUrl":"https://ymt6d.sharepoint.com/sites/OPTest","tenantId":"4d44bf36-9b56-45c0-8807-bbf386dd047f","webId":"6af246f0-e3be-45ef-b91a-8eca4dcc5d8f"}},"fileSystemInfo":{"createdDateTime":"2025-04-09T16:01:58Z","lastModifiedDateTime":"2025-04-09T16:01:58Z"},"folder":{"childCount":0,"view":{"sortBy":"name","sortOrder":"ascending","viewType":"thumbnails"}},"shared":{"scope":"unknown"}}'
+ recorded_at: Wed, 09 Apr 2025 16:01:57 GMT
+- request:
+ method: post
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root/children
+ body:
+ encoding: UTF-8
+ string: '{"name":"ConnectionValidatorFolder","folder":{},"@microsoft.graph.conflictBehavior":"fail"}'
+ headers:
+ Content-Type:
+ - application/json
+ Authorization:
+ - Bearer
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Length:
+ - '91'
+ response:
+ status:
+ code: 409
+ message: Conflict
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Content-Type:
+ - application/json
+ Content-Encoding:
+ - gzip
+ Vary:
+ - Accept-Encoding
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - 8ae2e5a3-51d0-4212-a282-f42474ecd061
+ Client-Request-Id:
+ - 8ae2e5a3-51d0-4212-a282-f42474ecd061
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"003","RoleInstance":"FR1PEPF0000161B"}}'
+ Date:
+ - Wed, 09 Apr 2025 16:01:57 GMT
+ body:
+ encoding: UTF-8
+ string: '{"error":{"code":"nameAlreadyExists","message":"Name already exists","innerError":{"date":"2025-04-09T16:01:58","request-id":"8ae2e5a3-51d0-4212-a282-f42474ecd061","client-request-id":"8ae2e5a3-51d0-4212-a282-f42474ecd061"}}}'
+ recorded_at: Wed, 09 Apr 2025 16:01:58 GMT
+- request:
+ method: get
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root/children?$select=id,name,size,webUrl,lastModifiedBy,createdBy,fileSystemInfo,file,folder,parentReference
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Authorization:
+ - Bearer
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Content-Type:
+ - application/json; odata.metadata=minimal; odata.streaming=true; IEEE754Compatible=false;
+ charset=utf-8
+ Content-Encoding:
+ - gzip
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - c5e05cfa-d4d1-4fed-9af1-3d99d5182905
+ Client-Request-Id:
+ - c5e05cfa-d4d1-4fed-9af1-3d99d5182905
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"003","RoleInstance":"FR1PEPF00000AF1"}}'
+ Date:
+ - Wed, 09 Apr 2025 16:01:58 GMT
+ body:
+ encoding: UTF-8
+ string: '{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#Collection(driveItem)","value":[{"@odata.etag":"\"{14A012EF-8F6E-4AA3-B137-F12EC82C13C2},2\"","createdBy":{"application":{"id":"b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff","displayName":"OP
+ Auth"},"user":{"displayName":"SharePoint App"}},"id":"01ANJ53W7PCKQBI3UPUNFLCN7RF3ECYE6C","lastModifiedBy":{"application":{"id":"b8a5bb54-5fb2-4e0e-9427-9d24dbac32ff","displayName":"OP
+ Auth"},"user":{"displayName":"SharePoint App"}},"name":"ConnectionValidatorFolder","parentReference":{"driveType":"documentLibrary","driveId":"b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs","id":"01ANJ53W56Y2GOVW7725BZO354PWSELRRZ","name":"Marcello
+ AMPF","path":"/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/root:","siteId":"1099e315-d0c7-47c7-8640-aa9504b70fff"},"webUrl":"https://ymt6d.sharepoint.com/sites/OPTest/Marcello%20AMPF/ConnectionValidatorFolder","fileSystemInfo":{"createdDateTime":"2025-04-09T16:01:58Z","lastModifiedDateTime":"2025-04-09T16:01:58Z"},"folder":{"childCount":0},"size":0}]}'
+ recorded_at: Wed, 09 Apr 2025 16:01:58 GMT
+- request:
+ method: delete
+ uri: https://graph.microsoft.com/v1.0/drives/b!dmVLG22QlE2PSW0AqVB7UOhZ8n7tjkVGkgqLNnuw2OBb-brzKzZAR4DYT1k9KPXs/items/01ANJ53W7PCKQBI3UPUNFLCN7RF3ECYE6C
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Authorization:
+ - Bearer
+ User-Agent:
+ - httpx.rb/1.3.4
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ response:
+ status:
+ code: 204
+ message: No Content
+ headers:
+ Cache-Control:
+ - no-store, no-cache
+ Strict-Transport-Security:
+ - max-age=31536000
+ Request-Id:
+ - 60c4b5b1-ff85-4d07-abdf-9255511eec30
+ Client-Request-Id:
+ - 60c4b5b1-ff85-4d07-abdf-9255511eec30
+ X-Ms-Ags-Diagnostic:
+ - '{"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"003","RoleInstance":"FR1PEPF00001213"}}'
+ Date:
+ - Wed, 09 Apr 2025 16:01:58 GMT
+ body:
+ encoding: UTF-8
+ string: ''
+ recorded_at: Wed, 09 Apr 2025 16:01:59 GMT
+recorded_with: VCR 6.3.1
diff --git a/modules/team_planner/app/controllers/team_planner/team_planner_controller.rb b/modules/team_planner/app/controllers/team_planner/team_planner_controller.rb
index 6b04456e264..fb39836b507 100644
--- a/modules/team_planner/app/controllers/team_planner/team_planner_controller.rb
+++ b/modules/team_planner/app/controllers/team_planner/team_planner_controller.rb
@@ -82,8 +82,6 @@ module ::TeamPlanner
@view = Query
.visible(current_user)
.find(params[:id])
- rescue ActiveRecord::RecordNotFound
- render_404
end
def visible_plans(project = nil)
diff --git a/modules/team_planner/app/views/team_planner/team_planner/upsale.html.erb b/modules/team_planner/app/views/team_planner/team_planner/upsale.html.erb
index 97b1af78182..bcd94d818e6 100644
--- a/modules/team_planner/app/views/team_planner/team_planner/upsale.html.erb
+++ b/modules/team_planner/app/views/team_planner/team_planner/upsale.html.erb
@@ -1,10 +1,8 @@
-<% html_title(t(:label_administration), t("team_planner.upsale.title")) -%>
+<% html_title(t(:label_administration), t("ee.upsale.team_planner_view.title")) -%>
-<%= render template: "common/upsale",
- locals: {
- feature_title: t("team_planner.upsale.title"),
- feature_description: t("team_planner.upsale.description"),
- feature_reference: "team_planner",
- feature_video: "enterprise/team-planner-animation.mp4",
- additional_classes: "upsale-notification_hide-breadcrumb"
- } %>
+<%=
+ render EnterpriseEdition::UpsalePageComponent.new(
+ :team_planner_view,
+ video: "enterprise/team-planner-animation.mp4"
+ )
+%>
diff --git a/modules/team_planner/config/locales/crowdin/af.yml b/modules/team_planner/config/locales/crowdin/af.yml
index 4e1f6fae8ef..4bf6d41f374 100644
--- a/modules/team_planner/config/locales/crowdin/af.yml
+++ b/modules/team_planner/config/locales/crowdin/af.yml
@@ -6,12 +6,16 @@ af:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Spanbeplanner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Spanbeplanner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/ar.yml b/modules/team_planner/config/locales/crowdin/ar.yml
index 275dedc6106..f9df89377e8 100644
--- a/modules/team_planner/config/locales/crowdin/ar.yml
+++ b/modules/team_planner/config/locales/crowdin/ar.yml
@@ -6,12 +6,16 @@ ar:
permission_view_team_planner: "عرض مخطط الفريق"
permission_manage_team_planner: "إدارة مخطط الفريق"
project_module_team_planner_view: "مخططي الفريق"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "مخطط الفريق"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "إنشاء مخطط فريق جديد"
label_team_planner_plural: "مخططي الفريق"
label_assignees: "المعينون"
- upsale:
- title: "مخطط الفريق"
- description: "احصل على نظرة عامة كاملة لتخطيط فريقك مع فريق تخطيط العمل. تمديد, اختصار و سحب و إسقاط مجموعات العمل لتعديل التواريخ أو نقلها أو تغيير المحال إليهم."
diff --git a/modules/team_planner/config/locales/crowdin/az.yml b/modules/team_planner/config/locales/crowdin/az.yml
index 28935738b38..4873bf3b977 100644
--- a/modules/team_planner/config/locales/crowdin/az.yml
+++ b/modules/team_planner/config/locales/crowdin/az.yml
@@ -6,12 +6,16 @@ az:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Təyin edilənlər"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/be.yml b/modules/team_planner/config/locales/crowdin/be.yml
index 6aef6ef8c09..e4f38e77437 100644
--- a/modules/team_planner/config/locales/crowdin/be.yml
+++ b/modules/team_planner/config/locales/crowdin/be.yml
@@ -6,12 +6,16 @@ be:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/bg.yml b/modules/team_planner/config/locales/crowdin/bg.yml
index 509770de05d..e049c826303 100644
--- a/modules/team_planner/config/locales/crowdin/bg.yml
+++ b/modules/team_planner/config/locales/crowdin/bg.yml
@@ -6,12 +6,16 @@ bg:
permission_view_team_planner: "Вижте екипния плановик"
permission_manage_team_planner: "Управлявайте екипния плановик"
project_module_team_planner_view: "Екипни плановици"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Екипен плановик"
label_new_team_planner: "Нов екипен плановик"
label_create_new_team_planner: "Създайте нов екипен плановик"
label_team_planner_plural: "Екипни плановици"
label_assignees: "Правоприемници"
- upsale:
- title: "Екипен плановик"
- description: "Получете пълен преглед на планирането на вашия екип с Team Planner. Разтягайте, съкращавайте и плъзгайте и пускайте работни пакети, за да промените датите, да ги преместите или да промените възложителите."
diff --git a/modules/team_planner/config/locales/crowdin/ca.yml b/modules/team_planner/config/locales/crowdin/ca.yml
index 4c03756f764..3097a0629ab 100644
--- a/modules/team_planner/config/locales/crowdin/ca.yml
+++ b/modules/team_planner/config/locales/crowdin/ca.yml
@@ -6,12 +6,16 @@ ca:
permission_view_team_planner: "Veure planificador d'equips"
permission_manage_team_planner: "Administrar planificador d'equips"
project_module_team_planner_view: "Planificadors d'equips"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Planificador d'equips"
label_new_team_planner: "Nou planificador d'equips"
label_create_new_team_planner: "Crea un nou planificador d'equips"
label_team_planner_plural: "Planificadors d'equips"
label_assignees: "Usuaris assignats"
- upsale:
- title: "Planificador d'equips"
- description: "Obté una visió general completa del teu planificador d'equips amb el Planificador d'Equips. Estira, escurça i arrossega paquets de treball per modificar dates, moure-les o canviar l'usuari assignat. "
diff --git a/modules/team_planner/config/locales/crowdin/ckb-IR.yml b/modules/team_planner/config/locales/crowdin/ckb-IR.yml
index 5b23223d258..a5179784496 100644
--- a/modules/team_planner/config/locales/crowdin/ckb-IR.yml
+++ b/modules/team_planner/config/locales/crowdin/ckb-IR.yml
@@ -6,12 +6,16 @@ ckb-IR:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/cs.yml b/modules/team_planner/config/locales/crowdin/cs.yml
index e3358031d5b..49c45c9d53f 100644
--- a/modules/team_planner/config/locales/crowdin/cs.yml
+++ b/modules/team_planner/config/locales/crowdin/cs.yml
@@ -6,12 +6,16 @@ cs:
permission_view_team_planner: "Zobrazit plánovač týmu"
permission_manage_team_planner: "Spravovat plánovač týmu"
project_module_team_planner_view: "Týmové plánovače"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Týmový plánovač"
label_new_team_planner: "Nový plánovač týmu"
label_create_new_team_planner: "Vytvořit nový týmový plánovač"
label_team_planner_plural: "Týmové plánovače"
label_assignees: "Řešitelé"
- upsale:
- title: "Týmový plánovač"
- description: "Získejte kompletní přehled plánování týmu s týmovým plánovačem. Roztáhněte, zkrátte a přetáhněte pracovní balíčky pro úpravu datumů, přesuňte je nebo změňte řešitele."
diff --git a/modules/team_planner/config/locales/crowdin/da.yml b/modules/team_planner/config/locales/crowdin/da.yml
index f8b5433a61f..5abd919bd25 100644
--- a/modules/team_planner/config/locales/crowdin/da.yml
+++ b/modules/team_planner/config/locales/crowdin/da.yml
@@ -6,12 +6,16 @@ da:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/de.yml b/modules/team_planner/config/locales/crowdin/de.yml
index 8605a136e35..d13b3baa060 100644
--- a/modules/team_planner/config/locales/crowdin/de.yml
+++ b/modules/team_planner/config/locales/crowdin/de.yml
@@ -6,12 +6,16 @@ de:
permission_view_team_planner: "Teamplaner anzeigen"
permission_manage_team_planner: "Teamplaner verwalten"
project_module_team_planner_view: "Teamplaner"
+ ee:
+ feature_names:
+ team_planner_view: "Teamplaner"
+ upsale:
+ team_planner_view:
+ title: "Teamplaner"
+ description: "Erhalten Sie einen kompletten Überblick über die Planung Ihres Teams mit dem Teamplaner. Strecken, verkürzen und ziehen Sie Arbeitspakete zur Änderung von Terminen. Verschieben Sie Arbeitspakete oder ändern Sie die zugewiesenen Benutzer."
team_planner:
label_team_planner: "Teamplaner"
label_new_team_planner: "Neuer Teamplaner"
label_create_new_team_planner: "Neuen Teamplaner erstellen"
label_team_planner_plural: "Teamplaner"
label_assignees: "Zugewiesene Benutzer"
- upsale:
- title: "Teamplaner"
- description: "Erhalten Sie einen kompletten Überblick über die Planung Ihres Teams mit dem Teamplaner. Strecken, verkürzen und ziehen Sie Arbeitspakete zur Änderung von Terminen, Verschieben Sie Arbeitspakete oder Ändern Sie die zugewiesenen Benutzer."
diff --git a/modules/team_planner/config/locales/crowdin/el.yml b/modules/team_planner/config/locales/crowdin/el.yml
index a64c05f1b55..359ebc9c439 100644
--- a/modules/team_planner/config/locales/crowdin/el.yml
+++ b/modules/team_planner/config/locales/crowdin/el.yml
@@ -6,12 +6,16 @@ el:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/eo.yml b/modules/team_planner/config/locales/crowdin/eo.yml
index cd3ad5690b3..7763ba33dbf 100644
--- a/modules/team_planner/config/locales/crowdin/eo.yml
+++ b/modules/team_planner/config/locales/crowdin/eo.yml
@@ -6,12 +6,16 @@ eo:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/es.yml b/modules/team_planner/config/locales/crowdin/es.yml
index aec978d650e..ba7a138a43e 100644
--- a/modules/team_planner/config/locales/crowdin/es.yml
+++ b/modules/team_planner/config/locales/crowdin/es.yml
@@ -6,12 +6,16 @@ es:
permission_view_team_planner: "Ver planificador del equipo"
permission_manage_team_planner: "Gestionar planificador del equipo"
project_module_team_planner_view: "Planificadores de equipos"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Planificador de equipo"
label_new_team_planner: "Nuevo planificador de equipos"
label_create_new_team_planner: "Crear planificador de equipo"
label_team_planner_plural: "Planificadores de equipos"
label_assignees: "Asignado a"
- upsale:
- title: "Planificador de equipo"
- description: "Obtén una visión completa de la planificación de tu equipo con el Planificador de Equipo. Alargar, acortar y mover paquetes de trabajo para modificar fechas o cambiar asignados."
diff --git a/modules/team_planner/config/locales/crowdin/et.yml b/modules/team_planner/config/locales/crowdin/et.yml
index c7bf66f3b59..0316e587a79 100644
--- a/modules/team_planner/config/locales/crowdin/et.yml
+++ b/modules/team_planner/config/locales/crowdin/et.yml
@@ -6,12 +6,16 @@ et:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/eu.yml b/modules/team_planner/config/locales/crowdin/eu.yml
index e6510830d73..30855a00fd6 100644
--- a/modules/team_planner/config/locales/crowdin/eu.yml
+++ b/modules/team_planner/config/locales/crowdin/eu.yml
@@ -6,12 +6,16 @@ eu:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/fa.yml b/modules/team_planner/config/locales/crowdin/fa.yml
index 6a01f18b547..7f33d8e5727 100644
--- a/modules/team_planner/config/locales/crowdin/fa.yml
+++ b/modules/team_planner/config/locales/crowdin/fa.yml
@@ -6,12 +6,16 @@ fa:
permission_view_team_planner: "مشاهده برنامه ریز تیم"
permission_manage_team_planner: "مدیریت برنامه ریز تیم"
project_module_team_planner_view: "برنامه ریزهای تیم"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "برنامه ریز تیم"
label_new_team_planner: "برنامه ریز تیم جدید"
label_create_new_team_planner: "ایجاد برنامه ریز جدید تیم"
label_team_planner_plural: "برنامه ریزهای تیم"
label_assignees: "نمایندگان"
- upsale:
- title: "برنامه ریز تیم"
- description: "یک نمای کلی از برنامه ریزی تیم با استفاده از برنامه ریز تیم دریافت کنید. به منظور اصلاح تاریخ، جابه جا کردن بسته های کاری و یا تغییر نمایندگان از بسط دادن، کوتاه کردن و یا کشیدن و رها کردن آنها استفاده کنید."
diff --git a/modules/team_planner/config/locales/crowdin/fi.yml b/modules/team_planner/config/locales/crowdin/fi.yml
index 04aa440bfcf..d96f093b414 100644
--- a/modules/team_planner/config/locales/crowdin/fi.yml
+++ b/modules/team_planner/config/locales/crowdin/fi.yml
@@ -6,12 +6,16 @@ fi:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/fil.yml b/modules/team_planner/config/locales/crowdin/fil.yml
index 90c8fee95bd..2037e022b0c 100644
--- a/modules/team_planner/config/locales/crowdin/fil.yml
+++ b/modules/team_planner/config/locales/crowdin/fil.yml
@@ -6,12 +6,16 @@ fil:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/fr.yml b/modules/team_planner/config/locales/crowdin/fr.yml
index a9bd27c7593..0017080a3be 100644
--- a/modules/team_planner/config/locales/crowdin/fr.yml
+++ b/modules/team_planner/config/locales/crowdin/fr.yml
@@ -6,12 +6,16 @@ fr:
permission_view_team_planner: "Voir le planificateur d'équipe"
permission_manage_team_planner: "Gérer le planificateur d'équipe"
project_module_team_planner_view: "Planificateurs d'équipe"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Planificateur d'équipe"
label_new_team_planner: "Nouveau planificateur d'équipe"
label_create_new_team_planner: "Créer un nouveau planificateur d'équipe"
label_team_planner_plural: "Planificateurs d'équipe"
label_assignees: "Responsables"
- upsale:
- title: "Planificateur d'équipe"
- description: "Obtenez un aperçu complet du planning de votre équipe avec le planificateur d'équipe. Allongez, raccourcissez et faites glisser les paquets de travail pour modifier les dates, les déplacer ou changer les assignés."
diff --git a/modules/team_planner/config/locales/crowdin/he.yml b/modules/team_planner/config/locales/crowdin/he.yml
index cd8c9c551a0..65dd0fa14ef 100644
--- a/modules/team_planner/config/locales/crowdin/he.yml
+++ b/modules/team_planner/config/locales/crowdin/he.yml
@@ -6,12 +6,16 @@ he:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "מתכנן צוות"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "משויך אל"
- upsale:
- title: "מתכנן צוות"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/hi.yml b/modules/team_planner/config/locales/crowdin/hi.yml
index 7ee822bd6be..a2428ccacfc 100644
--- a/modules/team_planner/config/locales/crowdin/hi.yml
+++ b/modules/team_planner/config/locales/crowdin/hi.yml
@@ -6,12 +6,16 @@ hi:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/hr.yml b/modules/team_planner/config/locales/crowdin/hr.yml
index 2c3e89110a3..caaf53c40fb 100644
--- a/modules/team_planner/config/locales/crowdin/hr.yml
+++ b/modules/team_planner/config/locales/crowdin/hr.yml
@@ -6,12 +6,16 @@ hr:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/hu.yml b/modules/team_planner/config/locales/crowdin/hu.yml
index b23388e04fd..65191489a3f 100644
--- a/modules/team_planner/config/locales/crowdin/hu.yml
+++ b/modules/team_planner/config/locales/crowdin/hu.yml
@@ -6,12 +6,16 @@ hu:
permission_view_team_planner: "Csoport tervező megtekintése"
permission_manage_team_planner: "Csoport tervező kezelése"
project_module_team_planner_view: "Csoport tervezők"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Csoport tervező"
label_new_team_planner: "Új csapattervező"
label_create_new_team_planner: "Új csoport tervező létrehozása"
label_team_planner_plural: "Csoport tervezők"
label_assignees: "Megbízottak"
- upsale:
- title: "Csoport tervező"
- description: "Szerezz áttekintést arról, hogy a csapatod mit tervez a Csapattervezővel. Nyújtsd, rövidítsd, fogd és dobd a munkacsomagokat, hogy módosítsd az időpontokat, áthelyezd őket vagy megváltoztasd a hozzárendelt felhasználót."
diff --git a/modules/team_planner/config/locales/crowdin/id.yml b/modules/team_planner/config/locales/crowdin/id.yml
index 84c9838dfa2..d51bf3363a8 100644
--- a/modules/team_planner/config/locales/crowdin/id.yml
+++ b/modules/team_planner/config/locales/crowdin/id.yml
@@ -6,12 +6,16 @@ id:
permission_view_team_planner: "Lihat rencana tim"
permission_manage_team_planner: "Kelola rencana tim"
project_module_team_planner_view: "Rencana tim"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Rencana tim"
label_new_team_planner: "Perencana tim baru"
label_create_new_team_planner: "Buat rencana tim baru"
label_team_planner_plural: "Rencana tim"
label_assignees: "Penerima tugas"
- upsale:
- title: "Rencana tim"
- description: "Dapatkan ikhtisar lengkap tentang perencanaan tim Anda dengan Team Planner. Rentangkan, perpendek, dan seret dan lepas paket kerja untuk mengubah tanggal, memindahkannya, atau mengubah penerima tugas."
diff --git a/modules/team_planner/config/locales/crowdin/it.yml b/modules/team_planner/config/locales/crowdin/it.yml
index 47acdd08f40..c070ee68781 100644
--- a/modules/team_planner/config/locales/crowdin/it.yml
+++ b/modules/team_planner/config/locales/crowdin/it.yml
@@ -6,12 +6,16 @@ it:
permission_view_team_planner: "Visualizza il pianificatore di team"
permission_manage_team_planner: "Gestisci il pianificatore di team"
project_module_team_planner_view: "Organizzatori di gruppo"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Pianificatore di team"
label_new_team_planner: "Nuovo pianificatore di team"
label_create_new_team_planner: "Crea nuovo pianificatore di team"
label_team_planner_plural: "Organizzatori di gruppo"
label_assignees: "Assegnatari"
- upsale:
- title: "Pianificatore di team"
- description: "Ottieni una panoramica completa della pianificazione del tuo team con Team Planner. Allunga, accorcia e sposta le macro-attività per modificare le date, riprogrammarle o cambiare gli assegnatari."
diff --git a/modules/team_planner/config/locales/crowdin/ja.yml b/modules/team_planner/config/locales/crowdin/ja.yml
index e127814312a..497857532a3 100644
--- a/modules/team_planner/config/locales/crowdin/ja.yml
+++ b/modules/team_planner/config/locales/crowdin/ja.yml
@@ -6,12 +6,16 @@ ja:
permission_view_team_planner: "チームプランナーを表示"
permission_manage_team_planner: "チームプランナーの管理"
project_module_team_planner_view: "チーム プランナー"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "チーム プランナー"
label_new_team_planner: "新しいチームプランナー"
label_create_new_team_planner: "新しいチームプランナーを作成"
label_team_planner_plural: "チーム プランナー"
label_assignees: "担当者"
- upsale:
- title: "チーム プランナー"
- description: "Team Plannerでチームの計画の概要を把握しましょう。クリックやドラッグで作業項目を編集したり、日付を変更したり、担当者を変更したりできます。"
diff --git a/modules/team_planner/config/locales/crowdin/js-fr.yml b/modules/team_planner/config/locales/crowdin/js-fr.yml
index 695779007b4..b0450fae0c8 100644
--- a/modules/team_planner/config/locales/crowdin/js-fr.yml
+++ b/modules/team_planner/config/locales/crowdin/js-fr.yml
@@ -18,7 +18,7 @@ fr:
today: 'Aujourd''hui'
drag_here_to_remove: 'Faites glisser ici pour supprimer le responsable et les dates de début et de fin.'
cannot_drag_here: 'Impossible de supprimer le lot de travaux en raison des autorisations ou des restrictions d''édition.'
- cannot_drag_to_non_working_day: 'Ce lot de travaux ne peut pas démarrer/terminer sur un jour non ouvré.'
+ cannot_drag_to_non_working_day: 'Ce lot de travail ne peut pas démarrer/terminer sur un jour non ouvré.'
quick_add:
empty_state: 'Utilisez le champ de recherche pour trouver des lots de travaux et faites-les glisser vers le planificateur pour l''assigner à quelqu''un et définir des dates de début et de fin.'
search_placeholder: 'Rechercher...'
diff --git a/modules/team_planner/config/locales/crowdin/ka.yml b/modules/team_planner/config/locales/crowdin/ka.yml
index 35c66428490..b9171c67f70 100644
--- a/modules/team_planner/config/locales/crowdin/ka.yml
+++ b/modules/team_planner/config/locales/crowdin/ka.yml
@@ -6,12 +6,16 @@ ka:
permission_view_team_planner: "გუნდის მგეგმავის ნახვა"
permission_manage_team_planner: "გუნდის მგეგმავის მართვა"
project_module_team_planner_view: "გუნდის მგეგმავები"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "გუნდის მგეგმავი"
label_new_team_planner: "ახალი გუნდის მგეგმავი"
label_create_new_team_planner: "ახალი გუნდის მგეგმავის შექმნა"
label_team_planner_plural: "გუნდის მგეგმავები"
label_assignees: "მიმნიჭებლები"
- upsale:
- title: "გუნდის მგეგმავი"
- description: "მიიღეთ მთელი თქვენი გუნდის გეგმების მიმოხილვა მგეგმავთან ერთად. გაწელეთ, შეამოკლეთ და გადაათრიეთ სამუშაოს პაკეტები, მათი თარიღების შესაცვლელად, გადასაადგილებლად ან მიმნიჭებლის შესაცვლელად."
diff --git a/modules/team_planner/config/locales/crowdin/kk.yml b/modules/team_planner/config/locales/crowdin/kk.yml
index 1b7fb3bb702..269e0f7c232 100644
--- a/modules/team_planner/config/locales/crowdin/kk.yml
+++ b/modules/team_planner/config/locales/crowdin/kk.yml
@@ -6,12 +6,16 @@ kk:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/ko.yml b/modules/team_planner/config/locales/crowdin/ko.yml
index 97a7c335943..91d5ba5b105 100644
--- a/modules/team_planner/config/locales/crowdin/ko.yml
+++ b/modules/team_planner/config/locales/crowdin/ko.yml
@@ -6,12 +6,16 @@ ko:
permission_view_team_planner: "팀 플래너 보기"
permission_manage_team_planner: "팀 플래너 관리"
project_module_team_planner_view: "팀 플래너"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "팀 플래너"
label_new_team_planner: "새 팀 플래너"
label_create_new_team_planner: "새로운 팀 플래너 만들기"
label_team_planner_plural: "팀 플래너"
label_assignees: "담당자"
- upsale:
- title: "팀 플래너"
- description: "팀 플래너를 사용하여 팀 계획에 대한 전체 개요를 확인하세요. 작업 패키지를 늘리거나 줄이고 끌어다 놓아 날짜를 수정하거나 이동하거나 담당자를 변경하세요."
diff --git a/modules/team_planner/config/locales/crowdin/lt.yml b/modules/team_planner/config/locales/crowdin/lt.yml
index 02c372926e4..bc396be33da 100644
--- a/modules/team_planner/config/locales/crowdin/lt.yml
+++ b/modules/team_planner/config/locales/crowdin/lt.yml
@@ -6,12 +6,16 @@ lt:
permission_view_team_planner: "Žiūrėti komandos planą"
permission_manage_team_planner: "Tvarkyti komandos planą"
project_module_team_planner_view: "Komandos planai"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Komandos planas"
label_new_team_planner: "Naujas komandos planas"
label_create_new_team_planner: "Kurti naują komandos planą"
label_team_planner_plural: "Komandos planai"
label_assignees: "Paskirtieji"
- upsale:
- title: "Komandos planas"
- description: "Gaukite pilną jūsų komandos plano vaizdą su Team Planner. Pakeiskite datas prailgindami, sutrumpindami ar nuvilkdami darbo paketus, perkelkite juos ar pakeiskite paskirtuosius."
diff --git a/modules/team_planner/config/locales/crowdin/lv.yml b/modules/team_planner/config/locales/crowdin/lv.yml
index a60592e31a0..bebef64e75b 100644
--- a/modules/team_planner/config/locales/crowdin/lv.yml
+++ b/modules/team_planner/config/locales/crowdin/lv.yml
@@ -6,12 +6,16 @@ lv:
permission_view_team_planner: "Apskatīt komandas plānotāju"
permission_manage_team_planner: "Pārvaldīt komandas plānotāju"
project_module_team_planner_view: "Komandas plānotāji"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Komandas plānotājs"
label_new_team_planner: "Jauns komandas plānotājs"
label_create_new_team_planner: "Izveidot jaunu komandas plānotāju"
label_team_planner_plural: "Komandas plānotāji"
label_assignees: "Atbildīgais"
- upsale:
- title: "Komandas plānotājs"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/mn.yml b/modules/team_planner/config/locales/crowdin/mn.yml
index 5365c7f03c7..355a64e6183 100644
--- a/modules/team_planner/config/locales/crowdin/mn.yml
+++ b/modules/team_planner/config/locales/crowdin/mn.yml
@@ -6,12 +6,16 @@ mn:
permission_view_team_planner: "Багийн төлөвлөгчийг харах"
permission_manage_team_planner: "Багийн төлөвлөгчийг удирдах"
project_module_team_planner_view: "Багийн төлөвлөгчид"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Багийн төлөвлөгч"
label_new_team_planner: "Шинэ багийн төлөвлөгч"
label_create_new_team_planner: "Шинэ баг төлөвлөгч үүсгэх"
label_team_planner_plural: "Багийн төлөвлөгчид"
label_assignees: "Томилогдсон хүмүүс"
- upsale:
- title: "Багийн төлөвлөгч"
- description: "Багийн төлөвлөгч-ийн тусламжтайгаар багийнхаа төлөвлөлтийн талаарх бүрэн танилцуулгатай танилцаарай. Огноог өөрчлөх, зөөх эсвэл томилогдсон хүмүүсийг өөрчлөхийн тулд ажлын багцуудыг сунгаж, богиносгож, чирэх, буулгах боломжтой."
diff --git a/modules/team_planner/config/locales/crowdin/ms.yml b/modules/team_planner/config/locales/crowdin/ms.yml
index a8d068ff59e..c5c833cf3e9 100644
--- a/modules/team_planner/config/locales/crowdin/ms.yml
+++ b/modules/team_planner/config/locales/crowdin/ms.yml
@@ -6,12 +6,16 @@ ms:
permission_view_team_planner: "Lihat perancang pasukan"
permission_manage_team_planner: "Urus perancang pasukan"
project_module_team_planner_view: "Perancang pasukan"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Perancang pasukan"
label_new_team_planner: "Perancang pasukan baharu"
label_create_new_team_planner: "Cipta perancang pasukan baharu"
label_team_planner_plural: "Perancang pasukan"
label_assignees: "Penerima tugasan"
- upsale:
- title: "Perancang pasukan"
- description: "Dapatkan gambaran keseluruhan lengkap perancangan pasukan anda dengan Perancang Pasukan. Renggangkan, pendekkan, dan tarik dan lepas pakej kerja untuk mengubah tarikh, memindahkan mereka, atau menukar penerima tugasan."
diff --git a/modules/team_planner/config/locales/crowdin/ne.yml b/modules/team_planner/config/locales/crowdin/ne.yml
index ae0db63a872..da76e2ac8cb 100644
--- a/modules/team_planner/config/locales/crowdin/ne.yml
+++ b/modules/team_planner/config/locales/crowdin/ne.yml
@@ -6,12 +6,16 @@ ne:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/nl.yml b/modules/team_planner/config/locales/crowdin/nl.yml
index a3b50c75f29..a7de8b4212b 100644
--- a/modules/team_planner/config/locales/crowdin/nl.yml
+++ b/modules/team_planner/config/locales/crowdin/nl.yml
@@ -6,12 +6,16 @@ nl:
permission_view_team_planner: "Bekijk team planner"
permission_manage_team_planner: "Teamplanner beheren"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "Nieuwe teamplanner"
label_create_new_team_planner: "Maak een nieuwe teamplanner"
label_team_planner_plural: "Team planners"
label_assignees: "Toegewezen personen"
- upsale:
- title: "Team planner"
- description: "Krijg een compleet overzicht van de planning van je team met Team Planner. Verleng, verkort en sleep-en-plaats werkpakketten om data te wijzigen, ze te verplaatsen of taakontvangers te veranderen."
diff --git a/modules/team_planner/config/locales/crowdin/no.yml b/modules/team_planner/config/locales/crowdin/no.yml
index ef969fe7592..5413225251a 100644
--- a/modules/team_planner/config/locales/crowdin/no.yml
+++ b/modules/team_planner/config/locales/crowdin/no.yml
@@ -6,12 +6,16 @@
permission_view_team_planner: "Vis teamplanlegger"
permission_manage_team_planner: "Administrer teamplanlegger"
project_module_team_planner_view: "Teamplanleggere"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Teamplanlegger"
label_new_team_planner: "Ny teamplanlegger"
label_create_new_team_planner: "Opprett ny teamplanlegger"
label_team_planner_plural: "Teamplanleggere"
label_assignees: "Tildelt"
- upsale:
- title: "Teamplanlegger"
- description: "Få en fullstendig oversikt over ditt teams planlegging med Team Planner. Utvid, forkort og dra-og-slipp arbeidspakker for å endre datoer, flytte dem eller endre deltakere."
diff --git a/modules/team_planner/config/locales/crowdin/pl.yml b/modules/team_planner/config/locales/crowdin/pl.yml
index 62f6e277d62..223ba93433a 100644
--- a/modules/team_planner/config/locales/crowdin/pl.yml
+++ b/modules/team_planner/config/locales/crowdin/pl.yml
@@ -6,12 +6,16 @@ pl:
permission_view_team_planner: "Wyświetlanie planisty zespołu"
permission_manage_team_planner: "Zarządzanie planistą zespołu"
project_module_team_planner_view: "Planiści zespołu"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Planista zespołu"
label_new_team_planner: "Nowy planista zespołu"
label_create_new_team_planner: "Utwórz nowego planistę zespołu"
label_team_planner_plural: "Planiści zespołu"
label_assignees: "Przypisane osoby"
- upsale:
- title: "Planista zespołu"
- description: "Zdobądź pełny przegląd planowania swojej drużyny za pomocą Planisty zespołu. Rozciągnij, skróć i przeciągnij pakiety robocze, aby modyfikować daty, przenosić je lub zmieniać przypisane osoby."
diff --git a/modules/team_planner/config/locales/crowdin/pt-BR.yml b/modules/team_planner/config/locales/crowdin/pt-BR.yml
index 7206c4ba8df..92e9d5f2bd1 100644
--- a/modules/team_planner/config/locales/crowdin/pt-BR.yml
+++ b/modules/team_planner/config/locales/crowdin/pt-BR.yml
@@ -6,12 +6,16 @@ pt-BR:
permission_view_team_planner: "Visualizar planejador de equipe"
permission_manage_team_planner: "Gerenciar planejador de equipe"
project_module_team_planner_view: "Planejador de equipe"
+ ee:
+ feature_names:
+ team_planner_view: "Planejador de equipe"
+ upsale:
+ team_planner_view:
+ title: "Planejador de equipe"
+ description: "Obtenha uma visão geral completa do planejamento da sua equipe com o Planejador de Equipe. Estique, encurte, e arraste e solte pacotes de trabalho para modificar datas, movê-los ou alterar os responsáveis."
team_planner:
label_team_planner: "Planejador de equipe"
label_new_team_planner: "Novo planejador de equipe"
label_create_new_team_planner: "Criar novo planejador de equipe"
label_team_planner_plural: "Planejador de equipe"
label_assignees: "Atribuídos para"
- upsale:
- title: "Planejador de equipe"
- description: "Obtenha uma visão geral completa do planejamento da sua equipe com o Planejamento de equipe. Estique, encurte, e arraste e solte pacotes de trabalho para modificar datas, movê-los ou alterar os responsáveis."
diff --git a/modules/team_planner/config/locales/crowdin/pt-PT.yml b/modules/team_planner/config/locales/crowdin/pt-PT.yml
index 4d1150b1f11..06e35b6a768 100644
--- a/modules/team_planner/config/locales/crowdin/pt-PT.yml
+++ b/modules/team_planner/config/locales/crowdin/pt-PT.yml
@@ -6,12 +6,16 @@ pt-PT:
permission_view_team_planner: "Ver planejador de equipe"
permission_manage_team_planner: "Gerir planeador de equipa"
project_module_team_planner_view: "Planeadores de equipa"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Planeador de equipa"
label_new_team_planner: "Novo planeador de equipa"
label_create_new_team_planner: "Criar novo planeador de equipa"
label_team_planner_plural: "Planeadores de equipa"
label_assignees: "Responsáveis"
- upsale:
- title: "Planeador de equipa"
- description: "Obtenha uma visão geral completa do planeamento da sua equipa com o Planeamento de Equipa. Encurte e arraste e solte pacotes de trabalho para modificar datas, mova-as ou altera os responsáveis."
diff --git a/modules/team_planner/config/locales/crowdin/ro.yml b/modules/team_planner/config/locales/crowdin/ro.yml
index 4fe36642f3f..2a5f0ba21de 100644
--- a/modules/team_planner/config/locales/crowdin/ro.yml
+++ b/modules/team_planner/config/locales/crowdin/ro.yml
@@ -6,12 +6,16 @@ ro:
permission_view_team_planner: "Vezi planificatorul echipei"
permission_manage_team_planner: "Gestionează planificatorul de echipe"
project_module_team_planner_view: "Planificare echipă"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Planificator echipă"
label_new_team_planner: "Planificator echipă nou"
label_create_new_team_planner: "Creează planificator echipă nou"
label_team_planner_plural: "Planificare echipă"
label_assignees: "Responsabili"
- upsale:
- title: "Planificator echipă"
- description: "Obțineți o imagine de ansamblu completă a planificării echipei dumneavoastră cu Team Planner. Întindeți, scurtați și glisați pachetele de lucru pentru a modifica datele, pentru a le muta sau pentru a schimba destinatarii."
diff --git a/modules/team_planner/config/locales/crowdin/ru.yml b/modules/team_planner/config/locales/crowdin/ru.yml
index 81340f15395..ff78c094481 100644
--- a/modules/team_planner/config/locales/crowdin/ru.yml
+++ b/modules/team_planner/config/locales/crowdin/ru.yml
@@ -6,12 +6,16 @@ ru:
permission_view_team_planner: "Просмотр планировщика команды"
permission_manage_team_planner: "Управление планировщиком команды"
project_module_team_planner_view: "Командные планировщики"
+ ee:
+ feature_names:
+ team_planner_view: "Командный планировщик"
+ upsale:
+ team_planner_view:
+ title: "Командный планировщик"
+ description: "Получите полный обзор планирования работы вашей команды с помощью Командного планировщика. Растягивайте, сокращайте и переносите пакеты работ, чтобы изменять даты, перемещать их или менять назначенных."
team_planner:
label_team_planner: "Командный планировщик"
label_new_team_planner: "Новый командный планировщик"
label_create_new_team_planner: "Создать новый командный планировщик"
label_team_planner_plural: "Командные планировщики"
label_assignees: "Исполнители"
- upsale:
- title: "Командный планировщик"
- description: "Получите полный обзор планирования работы вашей команды с помощью Team Planner. Растягивайте, сокращайте и переносите пакеты работ, чтобы изменять даты, перемещать их или менять назначенных."
diff --git a/modules/team_planner/config/locales/crowdin/rw.yml b/modules/team_planner/config/locales/crowdin/rw.yml
index 600dc817cde..c674d1976a6 100644
--- a/modules/team_planner/config/locales/crowdin/rw.yml
+++ b/modules/team_planner/config/locales/crowdin/rw.yml
@@ -6,12 +6,16 @@ rw:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/si.yml b/modules/team_planner/config/locales/crowdin/si.yml
index 60ed7c4ed1c..54bbdd8ad55 100644
--- a/modules/team_planner/config/locales/crowdin/si.yml
+++ b/modules/team_planner/config/locales/crowdin/si.yml
@@ -6,12 +6,16 @@ si:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/sk.yml b/modules/team_planner/config/locales/crowdin/sk.yml
index 1e6aa2d1254..3ada7e70e7d 100644
--- a/modules/team_planner/config/locales/crowdin/sk.yml
+++ b/modules/team_planner/config/locales/crowdin/sk.yml
@@ -6,12 +6,16 @@ sk:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/sl.yml b/modules/team_planner/config/locales/crowdin/sl.yml
index 09b334d2115..dceb93d3c95 100644
--- a/modules/team_planner/config/locales/crowdin/sl.yml
+++ b/modules/team_planner/config/locales/crowdin/sl.yml
@@ -6,12 +6,16 @@ sl:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Prevzemniki"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/sr.yml b/modules/team_planner/config/locales/crowdin/sr.yml
index c7d872f4e27..357093efe8f 100644
--- a/modules/team_planner/config/locales/crowdin/sr.yml
+++ b/modules/team_planner/config/locales/crowdin/sr.yml
@@ -6,12 +6,16 @@ sr:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/sv.yml b/modules/team_planner/config/locales/crowdin/sv.yml
index 6f7cd5fd687..356c019a2ae 100644
--- a/modules/team_planner/config/locales/crowdin/sv.yml
+++ b/modules/team_planner/config/locales/crowdin/sv.yml
@@ -6,12 +6,16 @@ sv:
permission_view_team_planner: "Visa teamplanerare"
permission_manage_team_planner: "Hantera teamplanerare"
project_module_team_planner_view: "Team planerare"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planerare"
label_new_team_planner: "Ny teamplanerare"
label_create_new_team_planner: "Skapa ny teamplanerare"
label_team_planner_plural: "Team planerare"
label_assignees: "Utförare"
- upsale:
- title: "Team planerare"
- description: "Få en fullständig överblick över ditt teams planering med Team Planner. Tänj ut, förkorta och dra-och-släpp arbetspaket för att ändra datum, flytta dem eller ändra utförare."
diff --git a/modules/team_planner/config/locales/crowdin/th.yml b/modules/team_planner/config/locales/crowdin/th.yml
index 63c20f256a5..fe8310d33e3 100644
--- a/modules/team_planner/config/locales/crowdin/th.yml
+++ b/modules/team_planner/config/locales/crowdin/th.yml
@@ -6,12 +6,16 @@ th:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/tr.yml b/modules/team_planner/config/locales/crowdin/tr.yml
index 6ab6db15cf5..daea88772a7 100644
--- a/modules/team_planner/config/locales/crowdin/tr.yml
+++ b/modules/team_planner/config/locales/crowdin/tr.yml
@@ -6,12 +6,16 @@ tr:
permission_view_team_planner: "Takım planlayıcıyı görüntüle"
permission_manage_team_planner: "Takım planlayıcıyı yönet"
project_module_team_planner_view: "Takım planlayıcıları"
+ ee:
+ feature_names:
+ team_planner_view: "Takım planlayıcısı"
+ upsale:
+ team_planner_view:
+ title: "Takım planlayıcısı"
+ description: "Takım Planlayıcısı ile ekibinizin planlamasına ilişkin eksiksiz bir genel bakış elde edin. Tarihleri değiştirmek, taşımak veya atananları değiştirmek için iş paketlerini genişletin, kısaltın ve sürükleyip bırakın."
team_planner:
label_team_planner: "Takım planlayıcısı"
label_new_team_planner: "Yeni takım planlayıcı "
label_create_new_team_planner: "Yeni takım planlayıcısı oluştur"
label_team_planner_plural: "Takım planlayıcıları"
label_assignees: "Atananlar"
- upsale:
- title: "Takım planlayıcısı"
- description: "Takım Planlayıcısı ile ekibinizin planlamasına ilişkin eksiksiz bir genel bakış elde edin. Tarihleri değiştirmek, taşımak veya atananları değiştirmek için iş paketlerini genişletin, kısaltın ve sürükleyip bırakın."
diff --git a/modules/team_planner/config/locales/crowdin/uk.yml b/modules/team_planner/config/locales/crowdin/uk.yml
index fa4c9acff4a..78fc261e69c 100644
--- a/modules/team_planner/config/locales/crowdin/uk.yml
+++ b/modules/team_planner/config/locales/crowdin/uk.yml
@@ -6,12 +6,16 @@ uk:
permission_view_team_planner: "Перегляд командного планувальника"
permission_manage_team_planner: "Керування командним планувальником"
project_module_team_planner_view: "Командні планувальники"
+ ee:
+ feature_names:
+ team_planner_view: "Командний планувальник"
+ upsale:
+ team_planner_view:
+ title: "Командний планувальник"
+ description: "Командний планувальник надає повний огляд планування вашої команди. Розтягуйте, скорочуйте й перетягуйте пакети робіт, щоб переміщувати їх, змінювати дати або перепризначати виконавців."
team_planner:
label_team_planner: "Командний планувальник"
label_new_team_planner: "Новий командний планувальник"
label_create_new_team_planner: "Створити новий командний планувальник"
label_team_planner_plural: "Командні планувальники"
label_assignees: "Виконавці"
- upsale:
- title: "Командний планувальник"
- description: "Командний планувальник надає повний огляд планування вашої команди. Розтягуйте, скорочуйте та перетягуйте пакети робіт, щоб переміщувати їх, змінювати дати або перепризначати виконавців."
diff --git a/modules/team_planner/config/locales/crowdin/uz.yml b/modules/team_planner/config/locales/crowdin/uz.yml
index 6bf04b739f0..009cdf762bb 100644
--- a/modules/team_planner/config/locales/crowdin/uz.yml
+++ b/modules/team_planner/config/locales/crowdin/uz.yml
@@ -6,12 +6,16 @@ uz:
permission_view_team_planner: "View team planner"
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/config/locales/crowdin/vi.yml b/modules/team_planner/config/locales/crowdin/vi.yml
index af860dbd701..64ada7b243c 100644
--- a/modules/team_planner/config/locales/crowdin/vi.yml
+++ b/modules/team_planner/config/locales/crowdin/vi.yml
@@ -6,12 +6,16 @@ vi:
permission_view_team_planner: "Xem kế hoạch nhóm"
permission_manage_team_planner: "Quản lý kế hoạch nhóm"
project_module_team_planner_view: "Kế hoạch nhóm"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
team_planner:
label_team_planner: "Kế hoạch nhóm"
label_new_team_planner: "Kế hoạch nhóm mới"
label_create_new_team_planner: "Tạo kế hoạch nhóm mới"
label_team_planner_plural: "Các kế hoạch nhóm"
label_assignees: "Người được phân công"
- upsale:
- title: "Kế hoạch nhóm"
- description: "Nhận cái nhìn tổng quan về kế hoạch của nhóm bạn với Kế hoạch Nhóm. Kéo dài, rút ngắn và kéo-thả các gói công việc để thay đổi ngày, di chuyển chúng hoặc thay đổi người được phân công."
diff --git a/modules/team_planner/config/locales/crowdin/zh-CN.yml b/modules/team_planner/config/locales/crowdin/zh-CN.yml
index f1fff3c5273..418a64a14fd 100644
--- a/modules/team_planner/config/locales/crowdin/zh-CN.yml
+++ b/modules/team_planner/config/locales/crowdin/zh-CN.yml
@@ -6,12 +6,16 @@ zh-CN:
permission_view_team_planner: "查看工作组规划器"
permission_manage_team_planner: "管理工作组规划器"
project_module_team_planner_view: "工作组规划器"
+ ee:
+ feature_names:
+ team_planner_view: "工作组规划器"
+ upsale:
+ team_planner_view:
+ title: "工作组规划器"
+ description: "通过工作组规划器,您可以获得团队计划的完整概览。您可以拉伸、缩短和拖放工作包来修改日期、移动工作包或更改指定人。"
team_planner:
label_team_planner: "工作组规划器"
label_new_team_planner: "新的工作组规划器"
label_create_new_team_planner: "创建新的工作组规划器"
label_team_planner_plural: "工作组规划器"
label_assignees: "受理人"
- upsale:
- title: "工作组规划器"
- description: "通过工作组规划器,您可以获得团队计划的完整概览。您可以拉伸、缩短和拖放工作包来修改日期、移动工作包或更改受理人。"
diff --git a/modules/team_planner/config/locales/crowdin/zh-TW.yml b/modules/team_planner/config/locales/crowdin/zh-TW.yml
index 1c11b3fd052..dad5635ab97 100644
--- a/modules/team_planner/config/locales/crowdin/zh-TW.yml
+++ b/modules/team_planner/config/locales/crowdin/zh-TW.yml
@@ -6,12 +6,16 @@ zh-TW:
permission_view_team_planner: "查看團隊規劃"
permission_manage_team_planner: "管理「團隊規劃」"
project_module_team_planner_view: "查看團隊規劃"
+ ee:
+ feature_names:
+ team_planner_view: "團隊規劃"
+ upsale:
+ team_planner_view:
+ title: "團隊規劃"
+ description: "使用「團隊規劃器」全面掌握團隊的規劃狀況。您可以延長、縮短或拖放工作項目,以修改日期、移動位置或變更指派對象。"
team_planner:
label_team_planner: "團隊規劃"
label_new_team_planner: "新團隊規劃"
label_create_new_team_planner: "建立新的團隊規劃"
label_team_planner_plural: "團隊規劃"
label_assignees: "執行者"
- upsale:
- title: "團隊規劃"
- description: "使用「團隊規劃」全面閱覽了解您的小組工作內容。 縮放、拖曳工作項目以便調整日期、移動日期或更改執行者。"
diff --git a/modules/team_planner/config/locales/en.yml b/modules/team_planner/config/locales/en.yml
index 4056518b898..62005bf133c 100644
--- a/modules/team_planner/config/locales/en.yml
+++ b/modules/team_planner/config/locales/en.yml
@@ -8,12 +8,17 @@ en:
permission_manage_team_planner: "Manage team planner"
project_module_team_planner_view: "Team planners"
+ ee:
+ feature_names:
+ team_planner_view: "Team planner"
+ upsale:
+ team_planner_view:
+ title: "Team planner"
+ description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
+
team_planner:
label_team_planner: "Team planner"
label_new_team_planner: "New team planner"
label_create_new_team_planner: "Create new team planner"
label_team_planner_plural: "Team planners"
label_assignees: "Assignees"
- upsale:
- title: "Team planner"
- description: "Get a complete overview of your team’s planning with Team Planner. Stretch, shorten and drag-and-drop work packages to modify dates, move them or change assignees."
diff --git a/modules/team_planner/lib/open_project/team_planner/engine.rb b/modules/team_planner/lib/open_project/team_planner/engine.rb
index ec80443d031..b0208927ba0 100644
--- a/modules/team_planner/lib/open_project/team_planner/engine.rb
+++ b/modules/team_planner/lib/open_project/team_planner/engine.rb
@@ -36,7 +36,9 @@ module OpenProject::TeamPlanner
author_url: "https://www.openproject.org",
bundled: true,
settings: {} do
- project_module :team_planner_view, dependencies: :work_package_tracking, enterprise_feature: true do
+ project_module :team_planner_view,
+ dependencies: :work_package_tracking,
+ enterprise_feature: "team_planner_view" do
permission :view_team_planner,
{ "team_planner/team_planner": %i[index show upsale overview],
"team_planner/menus": %i[show] },
diff --git a/modules/team_planner/spec/features/onboarding/team_planner_onboarding_tour_spec.rb b/modules/team_planner/spec/features/onboarding/team_planner_onboarding_tour_spec.rb
index 711b96a5744..e62b29db25a 100644
--- a/modules/team_planner/spec/features/onboarding/team_planner_onboarding_tour_spec.rb
+++ b/modules/team_planner/spec/features/onboarding/team_planner_onboarding_tour_spec.rb
@@ -32,7 +32,7 @@ require_relative "../../support/onboarding/onboarding_steps"
RSpec.describe "team planner onboarding tour",
:js,
:selenium,
- with_ee: %i[team_planner_view],
+ with_ee: %i[team_planner_view board_view],
# We decrease the notification polling interval because some portions
# of the JS code rely on something triggering the Angular change detection.
# This is usually done by the notification polling, but we don't want to wait
diff --git a/modules/team_planner/spec/features/team_planner_upsale_spec.rb b/modules/team_planner/spec/features/team_planner_upsale_spec.rb
index f749e01aa91..d2b678084dc 100644
--- a/modules/team_planner/spec/features/team_planner_upsale_spec.rb
+++ b/modules/team_planner/spec/features/team_planner_upsale_spec.rb
@@ -43,18 +43,18 @@ RSpec.describe "Team planner index",
it "redirects routes to upsale" do
visit team_planners_path
- expect(page).to have_text "Upgrade now"
+ expect(page).to have_enterprise_upsale_page(:premium)
visit project_team_planners_path(project)
- expect(page).to have_text "Upgrade now"
+ expect(page).to have_enterprise_upsale_page(:premium)
visit new_project_team_planners_path(project)
- expect(page).to have_text "Upgrade now"
+ expect(page).to have_enterprise_upsale_page(:premium)
visit project_team_planner_path(project, id: "new")
- expect(page).to have_text "Upgrade now"
+ expect(page).to have_enterprise_upsale_page(:premium)
end
end
diff --git a/modules/two_factor_authentication/app/controllers/two_factor_authentication/base_controller.rb b/modules/two_factor_authentication/app/controllers/two_factor_authentication/base_controller.rb
index 194ac410309..8f0b56ed9ce 100644
--- a/modules/two_factor_authentication/app/controllers/two_factor_authentication/base_controller.rb
+++ b/modules/two_factor_authentication/app/controllers/two_factor_authentication/base_controller.rb
@@ -229,8 +229,6 @@ module ::TwoFactorAuthentication
def find_device
@device = target_user.otp_devices.find(params[:device_id])
- rescue ActiveRecord::RecordNotFound
- render_404
end
def find_user
diff --git a/modules/two_factor_authentication/app/controllers/two_factor_authentication/users/two_factor_devices_controller.rb b/modules/two_factor_authentication/app/controllers/two_factor_authentication/users/two_factor_devices_controller.rb
index 2f0d521ad25..f839e48c522 100644
--- a/modules/two_factor_authentication/app/controllers/two_factor_authentication/users/two_factor_devices_controller.rb
+++ b/modules/two_factor_authentication/app/controllers/two_factor_authentication/users/two_factor_devices_controller.rb
@@ -105,8 +105,6 @@ module ::TwoFactorAuthentication
def find_user
@user = User.find(params[:id])
- rescue ActiveRecord::RecordNotFound
- render_404
end
def target_user
diff --git a/modules/two_factor_authentication/app/views/two_factor_authentication/upsale.html.erb b/modules/two_factor_authentication/app/views/two_factor_authentication/upsale.html.erb
deleted file mode 100644
index 2998bbf3fd9..00000000000
--- a/modules/two_factor_authentication/app/views/two_factor_authentication/upsale.html.erb
+++ /dev/null
@@ -1,9 +0,0 @@
-<% html_title(t(:label_administration), t("two_factor_authentication.settings.title")) -%>
-
-<%= render template: "common/upsale",
- locals: {
- feature_title: t("two_factor_authentication.upsale.title"),
- feature_description: t("two_factor_authentication.upsale.description"),
- feature_reference: "2fa",
- feature_image: "enterprise/two-factor-authentication.jpg"
- } %>
diff --git a/modules/two_factor_authentication/config/locales/crowdin/ro.yml b/modules/two_factor_authentication/config/locales/crowdin/ro.yml
index 06a4a7bded1..ff3049144f8 100644
--- a/modules/two_factor_authentication/config/locales/crowdin/ro.yml
+++ b/modules/two_factor_authentication/config/locales/crowdin/ro.yml
@@ -178,7 +178,7 @@ ro:
label_expiration_hint: "%{date} sau la deconectare"
label_actions: "Acțiuni"
label_confirmed: "Confirmat"
- button_continue: "Continuă"
+ button_continue: "Continuaţi"
button_make_default: "Marcați ca implicit"
label_unverified_phone: "Telefonul mobil nu a fost încă verificat"
notice_phone_number_format: "Te rog să introduci numărul în următorul format: +XX XXXXXXXX."
diff --git a/modules/two_factor_authentication/config/locales/crowdin/ru.yml b/modules/two_factor_authentication/config/locales/crowdin/ru.yml
index a1bbf15b728..b03e8cd0695 100644
--- a/modules/two_factor_authentication/config/locales/crowdin/ru.yml
+++ b/modules/two_factor_authentication/config/locales/crowdin/ru.yml
@@ -178,7 +178,7 @@ ru:
label_expiration_hint: "%{date} или при выходе из системы"
label_actions: "Действия"
label_confirmed: "Подтвержден"
- button_continue: "Продолжить"
+ button_continue: "Далее"
button_make_default: "Задать по умолчанию"
label_unverified_phone: "Сотовый телефон еще не подтвержден"
notice_phone_number_format: "Введите номер в следующем формате: +XX XXXXXXXX."
diff --git a/modules/two_factor_authentication/config/locales/crowdin/uk.yml b/modules/two_factor_authentication/config/locales/crowdin/uk.yml
index 0c70cad71e7..3430ee3cac3 100644
--- a/modules/two_factor_authentication/config/locales/crowdin/uk.yml
+++ b/modules/two_factor_authentication/config/locales/crowdin/uk.yml
@@ -119,7 +119,7 @@ uk:
failed_to_delete: "Не вдалося видалити пристрій 2FA."
is_default_cannot_delete: "Пристрій позначено як типовий і його не можна видалити через активну політику безпеки. Перед видаленням позначте інший пристрій як стандартний."
not_existing: "Для вашого облікового запису не зареєстровано жодного пристрою 2FA."
- 2fa_from_input: Введіть код, отриманий на пристрій %{device_name}, щоб підтвердити свою особу.
+ 2fa_from_input: Введіть код, що надійшов на пристрій %{device_name}, щоб підтвердити свою особу.
2fa_from_webauthn: Укажіть пристрій WebAuthn %{device_name}. Якщо це USB-пристрій, переконайтеся, що його підключено, і торкніться його. Потім натисніть кнопку входу.
webauthn:
title: "WebAuthn"
diff --git a/modules/webhooks/app/controllers/webhooks/outgoing/admin_controller.rb b/modules/webhooks/app/controllers/webhooks/outgoing/admin_controller.rb
index 688c61eae9a..95bdb27ebd4 100644
--- a/modules/webhooks/app/controllers/webhooks/outgoing/admin_controller.rb
+++ b/modules/webhooks/app/controllers/webhooks/outgoing/admin_controller.rb
@@ -58,8 +58,6 @@ module Webhooks
def find_webhook
@webhook = webhook_class.find(params[:webhook_id])
- rescue ActiveRecord::RecordNotFound
- render_404
end
def webhook_class
diff --git a/modules/xls_export/config/locales/crowdin/zh-CN.yml b/modules/xls_export/config/locales/crowdin/zh-CN.yml
index 59230e603ec..31c8d3cdd71 100644
--- a/modules/xls_export/config/locales/crowdin/zh-CN.yml
+++ b/modules/xls_export/config/locales/crowdin/zh-CN.yml
@@ -13,4 +13,4 @@ zh-CN:
xls_with_relations: "带关系的 XLS"
xls_export:
child_of: 此项的子项
- parent_of: 此项的父级
+ parent_of: 此项的父项
diff --git a/publiccode.yml b/publiccode.yml
index f985dee2acc..732cd52c3a2 100644
--- a/publiccode.yml
+++ b/publiccode.yml
@@ -184,7 +184,7 @@ description:
Zusätzlich bietet der Hersteller in ihrer [Enterprise Cloud](https://www.openproject.org/de/enterprise-edition) ein Hosting in einem Rechenzentrum innerhalb der Europäischen Union an, das gemäß der Datenschutz-Grundverordnung (DS-GVO) erfolgt.
OpenProject entspricht den Anforderungen des [BSI
- IT-Grundschutzes](https://www.bsi.bund.de/DE/Themen/ITGrundschutz/itgrundschutz_node.html)
+ IT-Grundschutzes](https://www.bsi.bund.de/DE/Themen/Unternehmen-und-Organisationen/Standards-und-Zertifizierung/IT-Grundschutz/it-grundschutz_node.html)
und wird kontinuierlich auf Basis der WCAG 2.1-Richtlinien getestet, wie
im
[Prüfbericht](https://gitlab.opencode.de/bmi/opendesk/info/-/blob/main/24.03/Barrierefreiheit/Barrierefreiheitsbericht%20-%20Komponente%20Projekte.pdf)
diff --git a/script/github_pr_errors b/script/github_pr_errors
index 77c4343f447..63651a8f702 100755
--- a/script/github_pr_errors
+++ b/script/github_pr_errors
@@ -1,4 +1,5 @@
#!/usr/bin/env ruby
+# frozen_string_literal: true
require "rubygems"
require "bundler"
@@ -14,8 +15,8 @@ require "time"
require "yaml"
require "httpx"
-GITHUB_API_OPENPROJECT_PREFIX = "https://api.github.com/repos/opf/openproject".freeze
-GITHUB_HTML_OPENPROJECT_PREFIX = "https://github.com/opf/openproject".freeze
+GITHUB_API_OPENPROJECT_PREFIX = "https://api.github.com/repos/opf/openproject"
+GITHUB_HTML_OPENPROJECT_PREFIX = "https://github.com/opf/openproject"
RAILS_ROOT = Pathname.new(__dir__).dirname
EXCLUDED_JOB_NAMES = %w[eslint rubocop].freeze
@@ -33,17 +34,18 @@ class Options
failed_jobs_logs: [],
no_cache: false,
run_id: nil,
+ job_id: nil,
verbose: false
}.freeze
BANNER = <<~BANNER.freeze
Usage: #{$0} [options] [url]
- Fetches rspec failures from last completed GitHub actions on current
- branch, and outputs them on standard output, one by line.
+ Fetches rspec failures from last completed GitHub actions on current branch,
+ and outputs them on standard output, one by line.
- If given an url, it will fetch the failures from the given url instead of
- the tip of the current branch.
+ If given an url, it will fetch the failures from the given url using the workflow
+ id and the job id if present instead of the tip of the current branch.
Information is printed on standard error to preserve standard output.
@@ -129,10 +131,11 @@ class Options
def parse_url(url)
case url
- when %r{^https://github.com/opf/openproject/actions/runs/(\d+)(?:/job/\d+(?:\?.*)?)?$}
+ when %r{^https://github.com/opf/openproject/actions/runs/(\d+)(?:/job/(\d+)(?:\?.*)?)?$}
run_id = $1.to_i
- say_verbose("Extracted run id #{run_id} from #{url}")
- { run_id: }
+ job_id = $2&.to_i
+ say_verbose("Extracted run id #{run_id}#{" and job id #{job_id}" if job_id} from #{url}")
+ { run_id:, job_id: }
else
warn "Unrecognized url #{url}"
exit 1
@@ -192,7 +195,7 @@ def error_details(error)
parts << "Failed to perform API request GET #{response.uri}: #{error}"
parts << " #{response_body['message']}"
parts << " See #{response_body['documentation_url']}"
- parts += error.backtrace.map { " #{_1}" }
+ parts += error.backtrace.map { " #{it}" }
parts.join("\n")
end
@@ -269,14 +272,56 @@ def get_workflow_run(run_id)
end
end
-class Report
- attr_accessor :errors,
- :failures_explanation,
- :head_branch,
- :head_sha,
- :commit_message,
- :run_started_at,
- :merge_branch_sha
+class WorkflowRunDecorator
+ attr_reader :workflow_run
+
+ def initialize(workflow_run)
+ @workflow_run = workflow_run
+ end
+
+ def head_branch = workflow_run["head_branch"]
+ def head_sha = workflow_run["head_sha"]
+ def run_started_at = Time.parse(workflow_run["run_started_at"]).utc
+
+ def commit_message
+ workflow_run["head_commit"]
+ .then { |commit| commit["message"] }
+ .then { |message| message.split("\n", 2).first }
+ end
+end
+
+Report = Data.define(
+ :errors,
+ :failures_explanation,
+ :head_branch,
+ :head_sha,
+ :commit_message,
+ :run_started_at,
+ :run_status,
+ :failed_job_logs,
+ :merge_branch_sha
+) do
+ def initialize(errors: [],
+ failures_explanation: nil,
+ head_branch: nil,
+ head_sha: nil,
+ commit_message: nil,
+ run_started_at: nil,
+ run_status: nil,
+ failed_job_logs: nil,
+ merge_branch_sha: nil)
+ super
+ end
+
+ def with_workflow_run_info(workflow_run)
+ workflow_run = WorkflowRunDecorator.new(workflow_run)
+ with(
+ head_branch: workflow_run.head_branch,
+ head_sha: workflow_run.head_sha,
+ commit_message: workflow_run.commit_message,
+ run_started_at: workflow_run.run_started_at
+ )
+ end
end
class Error
@@ -319,10 +364,11 @@ class JobErrorsFinder
logs.each do |log|
finder.scan_log(log)
end
- report.errors = finder.errors
- report.failures_explanation = finder.failures_explanation
- report.merge_branch_sha = finder.merge_branch_sha
- report
+ report.with(
+ errors: finder.errors,
+ failures_explanation: finder.failures_explanation,
+ merge_branch_sha: finder.merge_branch_sha
+ )
end
def scan_log(log)
@@ -375,7 +421,7 @@ class JobErrorsFinder
explanations << line
end
end
- explanations.map! { _1[29..] } # Remove leading timestamp (like "2024-02-05T08:37:54.5175930Z")
+ explanations.map! { it[29..] } # Remove leading timestamp (like "2024-02-05T08:37:54.5175930Z")
explanations.reject! do |line|
line == "Failures:" ||
line == "Failed examples:" ||
@@ -398,7 +444,7 @@ class JobErrorsFinder
def find_screenshots(log)
log.scan(SCREENSHOT_PATTERN)
- .map { JSON.parse _1 }
+ .map { JSON.parse it }
.each do |screenshot_info|
id = screenshot_info["test_id"]
location = screenshot_info["test_location"]
@@ -413,10 +459,10 @@ class JobErrorsFinder
tests_groups = log
.scan(TESTS_GROUP_PATTERN)
.flatten
- .map { build_tests_group_from_command(_1) }
+ .map { build_tests_group_from_command(it) }
errors.each do |error|
- error.tests_group = tests_groups.find { _1.include_error?(error) }
+ error.tests_group = tests_groups.find { it.include_error?(error) }
end
end
@@ -451,19 +497,15 @@ class Formatter
@compact
end
- def display_workflow_run_info(report, workflow_run)
- report.head_branch = workflow_run["head_branch"]
- report.head_sha = workflow_run["head_sha"]
- report.commit_message = commit_message(workflow_run)
- report.run_started_at = Time.parse(workflow_run["run_started_at"]).utc
+ def display_workflow_run_info(report)
warn " Branch: #{report.head_branch.bold}"
warn " Commit SHA: #{report.head_sha.bold}"
warn " Commit message: #{report.commit_message.bold}"
- warn " Run started at: #{report.run_started_at.localtime.to_s.bold}"
- display_pull_request_info(workflow_run)
+ warn " Last attempted run started at: #{report.run_started_at.localtime.to_s.bold}"
end
def display_workflow_status(workflow_run)
+ warn " Run attempt: #{workflow_run['run_attempt']}"
warn " #{status_line(workflow_run)}"
end
@@ -471,10 +513,10 @@ class Formatter
warn " #{status_line(job)}"
end
- def display_report(report)
+ def display_errors_from_report(report)
display_failures_explanation(report.failures_explanation)
- display_checkout_test_commit_information(report) if Options.display_rerun_info
display_errors(report.errors)
+ display_checkout_test_commit_information(report) if Options.display_rerun_info
end
def display_failures_explanation(failures_explanation)
@@ -495,10 +537,23 @@ class Formatter
end
end
+ def display_pull_request_info(workflow_run)
+ return unless workflow_run["event"] == "pull_request"
+
+ if pr = workflow_run["pull_requests"].first
+ pr_number = "##{pr['number']}"
+ pr_html_url = "#{GITHUB_HTML_OPENPROJECT_PREFIX}/pull/#{pr['number']}"
+ pr_display_title = "#{workflow_run['display_title']} #{pr_number.white.dark} #{pr_html_url.white.dark}"
+ warn " Pull Request: #{pr_display_title} "
+ else
+ warn " Pull Request: not found; perhaps it is already merged or closed?"
+ end
+ end
+
private
def display_errors_compact(errors)
- puts errors.map { escaped_location(_1) }.join(" ")
+ puts errors.map { escaped_location(it) }.join(" ")
end
def display_errors_detailed(errors)
@@ -508,13 +563,13 @@ class Formatter
.group_by(&:tests_group)
.each do |tests_group, tests_group_errors|
display_tests_group_info(tests_group)
- tests_group_errors.each { display_error(_1) }
+ tests_group_errors.each { display_error(it) }
display_tests_group_rerun_commands(tests_group)
end
else
errors
.sort_by(&:location)
- .each { display_error(_1) }
+ .each { display_error(it) }
end
end
@@ -565,6 +620,7 @@ class Formatter
if report.merge_branch_sha
warn <<~INSTRUCTIONS.white.dark
GitHub Action run merged #{report.merge_branch_sha} into #{report.head_sha} before running.
+
To be with the exact same source files as CI, use these commands (warning: stash your modifications first):
git checkout -B repro_ci_failures #{report.head_sha}
git merge --no-edit --no-verify #{report.merge_branch_sha}
@@ -575,7 +631,9 @@ class Formatter
git checkout #{report.head_sha}
INSTRUCTIONS
end
- warn # empty line
+ warn <<~INSTRUCTIONS.white.dark
+ Then run a test group with the rspec command above.
+ INSTRUCTIONS
end
def display_tests_group_info(tests_group)
@@ -592,25 +650,6 @@ class Formatter
warn ""
end
- def display_pull_request_info(workflow_run)
- return unless workflow_run["event"] == "pull_request"
-
- if pr = workflow_run["pull_requests"].first
- pr_number = "##{pr['number']}"
- pr_html_url = "#{GITHUB_HTML_OPENPROJECT_PREFIX}/pull/#{pr['number']}"
- pr_display_title = "#{workflow_run['display_title']} #{pr_number.white.dark} #{pr_html_url.white.dark}"
- warn " Pull Request: #{pr_display_title} "
- else
- warn " Pull Request: not found; perhaps it is already merged or closed?"
- end
- end
-
- def commit_message(workflow_run)
- workflow_run["head_commit"]
- .then { |commit| commit["message"] }
- .then { |message| message.split("\n", 2).first }
- end
-
def status_icon(job)
case job["status"]
when "queued", "in_progress"
@@ -645,21 +684,46 @@ class Formatter
end
end
+def get_jobs_from_job_id(job_id)
+ warn " Looking for the job with id #{job_id.to_s.bold}"
+
+ # no need to fetch anything as we only use the job id to get the logs
+ # mock the same structure as the jobs response
+ [{ "id" => job_id }]
+end
+
+def get_failing_jobs_from_workflow_last_attempt(workflow_run, formatter)
+ get_jobs(workflow_run)
+ .then { |jobs_response| jobs_response["jobs"] }
+ .sort_by { it["name"] }
+ .each { |job| formatter.display_job_status(job) }
+ .select { it["conclusion"] == "failure" }
+ .reject { EXCLUDED_JOB_NAMES.include?(it["name"]) }
+end
+
+def get_relevant_jobs(job_id, workflow_run, formatter)
+ if job_id
+ get_jobs_from_job_id(job_id)
+ else
+ formatter.display_workflow_status(workflow_run)
+ get_failing_jobs_from_workflow_last_attempt(workflow_run, formatter)
+ end
+end
+
def get_failed_jobs_logs_from_github(report, formatter)
workflow_run = get_workflow_run(Options.run_id)
+ report = report.with_workflow_run_info(workflow_run)
- formatter.display_workflow_run_info(report, workflow_run)
+ formatter.display_workflow_run_info(report)
+ formatter.display_pull_request_info(workflow_run)
- formatter.display_workflow_status(workflow_run)
- job_logs = get_jobs(workflow_run)
- .then { |jobs_response| jobs_response["jobs"] }
- .sort_by { _1["name"] }
- .each { |job| formatter.display_job_status(job) }
- .select { _1["conclusion"] == "failure" }
- .reject { EXCLUDED_JOB_NAMES.include?(_1["name"]) }
+ failed_job_logs = get_relevant_jobs(Options.job_id, workflow_run, formatter)
.map { |job| get_log(job) }
- [job_logs, job_logs.any? ? "error" : workflow_run["status"]]
+ report.with(
+ failed_job_logs:,
+ run_status: failed_job_logs.any? ? "error" : workflow_run["status"]
+ )
end
def get_failed_jobs_logs_from_args
@@ -668,7 +732,11 @@ end
def get_failed_jobs_logs(report, formatter)
if Options.failed_jobs_logs.any?
- [get_failed_jobs_logs_from_args, failed_jobs_logs.none? ? "completed" : "error"]
+ failed_job_logs = get_failed_jobs_logs_from_args
+ report.with(
+ failed_job_logs:,
+ run_status: failed_job_logs.none? ? "completed" : "error"
+ )
else
get_failed_jobs_logs_from_github(report, formatter)
end
@@ -680,15 +748,15 @@ end
report = Report.new
formatter = Formatter.new(compact: Options.compact)
-failed_jobs_logs, status = get_failed_jobs_logs(report, formatter)
+report = get_failed_jobs_logs(report, formatter)
-JobErrorsFinder.scan_logs(report, failed_jobs_logs)
+report = JobErrorsFinder.scan_logs(report, report.failed_job_logs)
-case status
+case report.run_status
when "completed"
warn "All jobs successful 🎉"
when "in_progress"
# NOOP
when "error"
- formatter.display_report(report)
+ formatter.display_errors_from_report(report)
end
diff --git a/spec/components/enterprise_edition/banner_component_spec.rb b/spec/components/enterprise_edition/banner_component_spec.rb
index a72eb6fce55..621d6bb3755 100644
--- a/spec/components/enterprise_edition/banner_component_spec.rb
+++ b/spec/components/enterprise_edition/banner_component_spec.rb
@@ -32,32 +32,45 @@ require "rails_helper"
RSpec.describe EnterpriseEdition::BannerComponent, type: :component do
let(:title) { "Some title" }
+ let(:expected_title) { title }
let(:description) { "Some description" }
+ let(:expected_description) { description }
let(:href) { "https://www.example.org" }
- let(:link_title) { "Get more information" }
- let(:ee_show_banners) { true }
+ let(:component_test_selector) { "op-enterprise-banner" }
+ let(:features) { nil }
let(:enforce_available_locales) { I18n.config.enforce_available_locales }
let(:i18n_upsale) do
{
- title:,
- link_title:,
some_enterprise_feature: {
- description:
- }
+ title:,
+ description:,
+ features:
+ }.compact
}
end
let(:static_links) do
{
- enterprise_docs: {
+ enterprise_features: {
some_enterprise_feature: {
href:
}
}
}
end
+ let(:translations) do
+ {
+ ee: {
+ features: {
+ some_enterprise_feature: "Enterprise feature translation"
+ },
+ upsale: i18n_upsale
+ }
+ }
+ end
+ let(:component_args) { {} }
let(:render_component) do
- render_inline(described_class.new(:some_enterprise_feature))
+ render_inline(described_class.new(:some_enterprise_feature, **component_args))
end
let(:render_component_in_mo) do
@@ -67,9 +80,6 @@ RSpec.describe EnterpriseEdition::BannerComponent, type: :component do
end
before do
- allow(EnterpriseToken)
- .to receive(:show_banners?)
- .and_return(ee_show_banners)
allow(OpenProject::Static::Links)
.to receive(:links)
.and_return(static_links)
@@ -78,11 +88,7 @@ RSpec.describe EnterpriseEdition::BannerComponent, type: :component do
I18n.backend.store_translations(
:mo,
- {
- ee: {
- upsale: i18n_upsale
- }
- }
+ translations
)
end
@@ -95,25 +101,92 @@ RSpec.describe EnterpriseEdition::BannerComponent, type: :component do
it "renders the component" do
render_component_in_mo
- expect(page).to have_test_selector("op-ee-banner-some-enterprise-feature")
- expect(page).to have_css ".op-ee-banner--title-container", text: title
- expect(page).to have_css ".op-ee-banner--description-container", text: description
- expect(page).to have_link link_title, href:
+ component = find_test_selector(component_test_selector)
+
+ expect(component).to have_text(expected_title)
+ expect(component).to have_text(expected_description)
+ expect(component).to have_link("More information", href:)
+ end
+ end
+
+ shared_examples_for "does not render the component" do
+ it "does not render the component" do
+ render_component_in_mo
+
+ expect(page).not_to have_test_selector(component_test_selector)
+ expect(page).to have_no_text("Enterprise feature translation")
+ expect(page).to have_no_text(expected_title)
+ expect(page).to have_no_text(expected_description)
+ expect(page).to have_no_link(href:)
end
end
it_behaves_like "renders the component"
- context "with a description_html in the i18n file" do
+ context "when feature is available", with_ee: %i[some_enterprise_feature] do
+ it_behaves_like "does not render the component"
+ end
+
+ context "when banners are hidden" do
+ before do
+ allow(EnterpriseToken).to receive(:hide_banners?).and_return(true)
+ end
+
+ it_behaves_like "does not render the component"
+ end
+
+ context "when banner is dismissed" do
+ let(:preference) { build_stubbed(:user_preference) }
+ let(:user) { build_stubbed(:user, preference:) }
+ let(:dismiss_key) { :some_enterprise_feature }
+ let(:component_args) { { dismissable: true } }
+
+ before do
+ login_as(user)
+ allow(preference)
+ .to receive(:dismissed_banner?)
+ .with(dismiss_key)
+ .and_return(true)
+ end
+
+ it_behaves_like "does not render the component"
+
+ context "when not dismissable" do
+ let(:component_args) { { dismissable: false } }
+
+ it_behaves_like "renders the component"
+ end
+
+ context "when using a custom dismiss_key" do
+ let(:dismiss_key) { :foo }
+ let(:component_args) { { dismiss_key:, dismissable: true } }
+
+ it_behaves_like "does not render the component"
+ end
+ end
+
+ context "without a title, but a description_html" do
let(:i18n_upsale) do
{
- title:,
- link_title:,
some_enterprise_feature: {
description_html: description
}
}
end
+ let(:expected_title) { "Enterprise feature translation" }
+
+ it_behaves_like "renders the component"
+ end
+
+ context "without a title, but a description" do
+ let(:i18n_upsale) do
+ {
+ some_enterprise_feature: {
+ description:
+ }
+ }
+ end
+ let(:expected_title) { "Enterprise feature translation" }
it_behaves_like "renders the component"
end
@@ -121,8 +194,6 @@ RSpec.describe EnterpriseEdition::BannerComponent, type: :component do
context "with a more specific title in the i18n file" do
let(:i18n_upsale) do
{
- title: "The general title",
- link_title:,
some_enterprise_feature: {
title:,
description:
@@ -133,17 +204,27 @@ RSpec.describe EnterpriseEdition::BannerComponent, type: :component do
it_behaves_like "renders the component"
end
- context "with a more specific link title in the i18n file" do
- let(:i18n_upsale) do
+ context "with a custom i18n_scope" do
+ let(:translations) do
{
- title:,
- link_title: "The general link title",
- some_enterprise_feature: {
- link_title:,
- description:
+ my: {
+ custom: {
+ upsale: {
+ title: "Foo",
+ description: "Bar"
+ }
+ }
+ },
+ ee: {
+ features: {
+ some_enterprise_feature: "Enterprise feature translation"
+ }
}
}
end
+ let(:expected_title) { "Foo" }
+ let(:expected_description) { "Bar" }
+ let(:component_args) { { i18n_scope: "my.custom.upsale" } }
it_behaves_like "renders the component"
end
@@ -151,8 +232,6 @@ RSpec.describe EnterpriseEdition::BannerComponent, type: :component do
context "without a description key in the i18n file" do
let(:i18n_upsale) do
{
- title:,
- link_title:,
some_enterprise_feature: {}
}
end
@@ -165,34 +244,23 @@ RSpec.describe EnterpriseEdition::BannerComponent, type: :component do
context "without a link key in the static_link file" do
let(:static_links) do
{
- enterprise_docs: {
+ enterprise_features: {
+ default: {
+ href: "https://example.com"
+ },
some_enterprise_feature: {}
}
}
end
- it "raises an error" do
- expect { render_component_in_mo }.to raise_error(RuntimeError)
- end
- end
-
- context "if banners are hidden" do
- let(:ee_show_banners) { false }
-
- it "hides the component" do
+ it "uses the default" do
render_component_in_mo
- expect(page).to have_no_css ".op-ee-banner"
- end
- end
+ component = find_test_selector(component_test_selector)
- context "if banners are hidden but skip_render is overwritten" do
- let(:ee_show_banners) { false }
- let(:render_component) do
- render_inline(described_class.new(:some_enterprise_feature,
- skip_render: false))
+ expect(component).to have_text(expected_title)
+ expect(component).to have_text(expected_description)
+ expect(component).to have_link("More information", href: "https://example.com")
end
-
- it_behaves_like "renders the component"
end
end
diff --git a/spec/components/projects/phases/hover_card_component_spec.rb b/spec/components/projects/phases/hover_card_component_spec.rb
new file mode 100644
index 00000000000..117d67da0f7
--- /dev/null
+++ b/spec/components/projects/phases/hover_card_component_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+require "rails_helper"
+
+RSpec.describe Projects::Phases::HoverCardComponent, type: :component do
+ include Rails.application.routes.url_helpers
+
+ let(:phase) { build_stubbed(:project_phase, :with_gated_definition) }
+ let(:gate) { "start" }
+
+ subject { described_class.new(phase:, gate:) }
+
+ before do
+ render_inline(subject)
+ page.extend TestSelectorFinders
+ end
+
+ context "for start" do
+ it "renders successfully" do
+ page.find_test_selector("phase-gate-hover-card-name", text: phase.start_gate_name)
+ page.find_test_selector("phase-gate-hover-card-date", text: phase.start_date.strftime("%m/%d/%Y"))
+ end
+
+ context "without a definition" do
+ let(:phase) { create(:project_phase) }
+
+ it "renders, but has no content" do
+ page.find_test_selector("phase-gate-hover-card-name", text: "")
+ page.find_test_selector("phase-gate-hover-card-date", text: "")
+ end
+ end
+ end
+
+ context "for finish" do
+ let(:gate) { "finish" }
+
+ it "renders successfully" do
+ page.find_test_selector("phase-gate-hover-card-name", text: phase.finish_gate_name)
+ page.find_test_selector("phase-gate-hover-card-date", text: phase.finish_date.strftime("%m/%d/%Y"))
+ end
+
+ context "without a definition" do
+ let(:phase) { create(:project_phase) }
+
+ it "renders, but has no content" do
+ page.find_test_selector("phase-gate-hover-card-name", text: "")
+ page.find_test_selector("phase-gate-hover-card-date", text: "")
+ end
+ end
+ end
+end
diff --git a/spec/components/users/hover_card_component_spec.rb b/spec/components/users/hover_card_component_spec.rb
index b2d10036046..d3e95187d97 100644
--- a/spec/components/users/hover_card_component_spec.rb
+++ b/spec/components/users/hover_card_component_spec.rb
@@ -46,11 +46,10 @@ RSpec.describe Users::HoverCardComponent, type: :component do
groups
login_as(current_user)
render_inline(subject)
- page.extend TestSelectorFinders
end
it "renders successfully" do
- page.find_test_selector("user-hover-card-name", text: user.name)
+ find_test_selector("user-hover-card-name", text: user.name)
end
context "when the user does not exist" do
@@ -71,14 +70,14 @@ RSpec.describe Users::HoverCardComponent, type: :component do
let(:current_user) { build(:admin) }
it "shows the email address of a user" do
- page.find_test_selector("user-hover-card-email", text: user.mail)
+ find_test_selector("user-hover-card-email", text: user.mail)
end
end
end
context "when showing the group summary" do
it "shows a no results text for users without group memberships" do
- g = page.find_test_selector("user-hover-card-groups")
+ g = find_test_selector("user-hover-card-groups")
expect(g).to have_text(I18n.t("users.groups.no_results_title_text"))
end
@@ -88,7 +87,7 @@ RSpec.describe Users::HoverCardComponent, type: :component do
end
it "lists the group names for a user" do
- g = page.find_test_selector("user-hover-card-groups")
+ g = find_test_selector("user-hover-card-groups")
expect(g).to have_text("Member of #{groups.first.lastname}, #{groups.last.lastname}.")
end
@@ -98,7 +97,7 @@ RSpec.describe Users::HoverCardComponent, type: :component do
let(:another_user) { create(:user) }
it "does not show groups" do
- g = page.find_test_selector("user-hover-card-groups")
+ g = find_test_selector("user-hover-card-groups")
expect(g).to have_text(I18n.t("users.groups.no_results_title_text"))
end
@@ -111,7 +110,7 @@ RSpec.describe Users::HoverCardComponent, type: :component do
end
it "lists some group names with truncation" do
- g = page.find_test_selector("user-hover-card-groups")
+ g = find_test_selector("user-hover-card-groups")
expect(g).to have_text("Member of #{groups.slice(0, 4).map(&:lastname).join(', ')} and 4 more.")
end
@@ -120,7 +119,7 @@ RSpec.describe Users::HoverCardComponent, type: :component do
context "when clicking on the Open Profile button" do
it "leads to the users profile" do
- b = page.find_test_selector("user-hover-card-profile-btn")
+ b = find_test_selector("user-hover-card-profile-btn")
expect(b).to have_text(I18n.t("users.open_profile"))
expect(b["href"]).to eq(user_path(user))
@@ -130,7 +129,7 @@ RSpec.describe Users::HoverCardComponent, type: :component do
let(:current_user) { build(:admin) }
it "leads to editing the users profile" do
- b = page.find_test_selector("user-hover-card-profile-btn")
+ b = find_test_selector("user-hover-card-profile-btn")
expect(b).to have_text(I18n.t("users.open_profile"))
expect(b["href"]).to eq(edit_user_path(user))
diff --git a/spec/components/work_packages/types/subject_configuration_component_spec.rb b/spec/components/work_packages/types/subject_configuration_component_spec.rb
index 18b2900cee1..f4db7176f78 100644
--- a/spec/components/work_packages/types/subject_configuration_component_spec.rb
+++ b/spec/components/work_packages/types/subject_configuration_component_spec.rb
@@ -42,7 +42,7 @@ RSpec.describe WorkPackages::Types::SubjectConfigurationComponent, type: :compon
it "shows no enterprise banner" do
render_component
- expect(page).not_to have_test_selector("op-ee-banner-automatic-subject-generation")
+ expect(page).not_to have_test_selector("op-enterprise-banner-work-package-subject-generation")
end
it "enables mode selectors", :aggregate_failures do
@@ -109,7 +109,7 @@ RSpec.describe WorkPackages::Types::SubjectConfigurationComponent, type: :compon
it "shows the enterprise banner" do
render_component
- expect(page).to have_test_selector("op-ee-banner-automatic-subject-generation")
+ expect(page).to have_enterprise_banner
end
it "disables only automatic mode selector", :aggregate_failures do
@@ -125,7 +125,7 @@ RSpec.describe WorkPackages::Types::SubjectConfigurationComponent, type: :compon
it "shows the enterprise banner" do
render_component
- expect(page).to have_test_selector("op-ee-banner-automatic-subject-generation")
+ expect(page).to have_enterprise_banner
end
it "enables mode selectors", :aggregate_failures do
diff --git a/spec/contracts/project_life_cycle_steps/base_contract_spec.rb b/spec/contracts/project_life_cycle_steps/base_contract_spec.rb
index 36f32dfba27..86c64cd7a72 100644
--- a/spec/contracts/project_life_cycle_steps/base_contract_spec.rb
+++ b/spec/contracts/project_life_cycle_steps/base_contract_spec.rb
@@ -34,14 +34,12 @@ require "contracts/shared/model_contract_shared_context"
RSpec.describe ProjectLifeCycleSteps::BaseContract do
include_context "ModelContract shared context"
- let(:contract) { described_class.new(project, user) }
+ let(:contract) { described_class.new(phase, user) }
+ let(:user) { build_stubbed(:user) }
let(:project) { build_stubbed(:project) }
+ let(:phase) { build_stubbed(:project_phase, project:) }
context "with authorized user" do
- let(:user) { build_stubbed(:user) }
- let(:project) { build_stubbed(:project, available_phases: phases) }
- let(:phases) { [] }
-
before do
mock_permissions_for(user) do |mock|
mock.allow_in_project(:edit_project_phases, project:)
@@ -50,136 +48,9 @@ RSpec.describe ProjectLifeCycleSteps::BaseContract do
it_behaves_like "contract is valid"
include_examples "contract reuses the model errors"
-
- describe "validations" do
- describe "#consecutive_phases_have_increasing_dates" do
- let(:phase1) { build_stubbed(:project_phase, start_date: Date.new(2024, 1, 1), finish_date: Date.new(2024, 1, 1)) }
- let(:phase2) { build_stubbed(:project_phase, start_date: Date.new(2024, 2, 1), finish_date: Date.new(2024, 2, 28)) }
- let(:phase3) { build_stubbed(:project_phase, start_date: Date.new(2024, 3, 1), finish_date: Date.new(2024, 3, 15)) }
- let(:phases) { [phase1, phase2, phase3] }
-
- context "when no phases are present" do
- let(:phases) { [] }
-
- it_behaves_like "contract is valid"
- end
-
- context "when only one step is present" do
- let(:phases) { [phase1] }
-
- it_behaves_like "contract is valid"
- end
-
- context "when phases have valid and increasing dates" do
- let(:phases) { [phase1, phase2, phase3] }
-
- it_behaves_like "contract is valid"
- end
-
- context "when phases have decreasing dates" do
- context "and the erroneous step is Phase 1" do
- let(:phases) { [phase3, phase1] }
-
- it_behaves_like "contract is invalid",
- "available_phases.date_range": :non_continuous_dates
-
- it "adds an error to the decreasing step" do
- contract.validate
- expect(phase1.errors.symbols_for(:date_range)).to include(:non_continuous_dates)
- end
- end
- end
-
- context "when phases with identical dates" do
- let(:phase2) { build_stubbed(:project_phase, start_date: Date.new(2024, 1, 1), finish_date: Date.new(2024, 1, 1)) }
-
- it_behaves_like "contract is invalid",
- "available_phases.date_range": :non_continuous_dates
- end
-
- context "when phases have touching start and end dates" do
- context "when 2 Phases are touching" do
- let(:phase2) { build_stubbed(:project_phase, start_date: Date.new(2024, 1, 1), finish_date: Date.new(2024, 1, 1)) }
-
- it_behaves_like "contract is invalid",
- "available_phases.date_range": :non_continuous_dates
-
- context "when having an empty step in between" do
- let(:step_missing_dates) { build_stubbed(:project_phase, start_date: nil, finish_date: nil) }
- let(:phases) { [phase1, step_missing_dates, phase2] }
-
- it_behaves_like "contract is invalid",
- "available_phases.date_range": :non_continuous_dates
- end
- end
-
- context "when 3 Phases are touching" do
- let(:phase2) { build_stubbed(:project_phase, start_date: Date.new(2024, 1, 1), finish_date: Date.new(2024, 1, 1)) }
- let(:phase3) { build_stubbed(:project_phase, start_date: Date.new(2024, 1, 1), finish_date: Date.new(2024, 1, 2)) }
-
- it_behaves_like "contract is invalid",
- "available_phases.date_range": :non_continuous_dates
-
- it "adds error to Phase 2 and 3" do
- contract.validate
- expect(phase2.errors.symbols_for(:date_range)).to include(:non_continuous_dates)
- expect(phase3.errors.symbols_for(:date_range)).to include(:non_continuous_dates)
- end
-
- context "when having an empty step in between" do
- let(:step_missing_dates) { build_stubbed(:project_phase, start_date: nil, finish_date: nil) }
- let(:phases) { [phase1, phase2, step_missing_dates, phase3] }
-
- it_behaves_like "contract is invalid",
- "available_phases.date_range": :non_continuous_dates
-
- it "adds error to Phase 2 and 3" do
- contract.validate
- expect(phase2.errors.symbols_for(:date_range)).to include(:non_continuous_dates)
- expect(phase3.errors.symbols_for(:date_range)).to include(:non_continuous_dates)
- expect(step_missing_dates.errors.symbols_for(:date_range)).to be_empty
- end
- end
- end
- end
-
- context "when a step has missing start dates" do
- let(:step_missing_dates) { build_stubbed(:project_phase, start_date: nil, finish_date: nil) }
-
- context "and the other phases have increasing dates" do
- let(:phases) { [phase1, step_missing_dates, phase2] }
-
- it_behaves_like "contract is valid"
- end
-
- context "and the other phases have decreasing dates" do
- let(:phases) { [phase2, step_missing_dates, phase1] }
-
- it_behaves_like "contract is invalid",
- "available_phases.date_range": :non_continuous_dates
-
- it "adds an error to the decreasing step" do
- contract.validate
- expect(phase1.errors.symbols_for(:date_range)).to include(:non_continuous_dates)
- end
- end
- end
- end
-
- describe "triggering validations on the model" do
- it "sets the :saving_phases validation context" do
- allow(project).to receive(:valid?)
-
- contract.validate
- expect(project).to have_received(:valid?).with(:saving_phases)
- end
- end
- end
end
context "with unauthorized user" do
- let(:user) { build_stubbed(:user) }
-
it_behaves_like "contract user is unauthorized"
end
end
diff --git a/spec/contracts/work_packages/create_note_contract_spec.rb b/spec/contracts/work_packages/create_note_contract_spec.rb
index eeacb2dabb7..91c7c9167da 100644
--- a/spec/contracts/work_packages/create_note_contract_spec.rb
+++ b/spec/contracts/work_packages/create_note_contract_spec.rb
@@ -28,11 +28,15 @@
# See COPYRIGHT and LICENSE files for more details.
require "spec_helper"
+require "contracts/shared/model_contract_shared_context"
RSpec.describe WorkPackages::CreateNoteContract do
+ include_context "ModelContract shared context"
+
+ let(:project) { build_stubbed(:project) }
let(:work_package) do
# As we only want to test the contract, we mock checking whether the work_package is valid
- wp = build_stubbed(:work_package)
+ wp = build_stubbed(:work_package, project:)
# we need to clear the changes information because otherwise the
# contract will complain about all the changes to read_only attributes
wp.send(:clear_changes_information)
@@ -41,99 +45,105 @@ RSpec.describe WorkPackages::CreateNoteContract do
wp
end
let(:user) { build_stubbed(:user) }
- let(:policy_instance) { instance_double(WorkPackagePolicy) }
+ let(:permissions) { %i[add_work_package_notes add_comments_with_restricted_visibility] }
+
+ before do
+ mock_permissions_for(user) do |mock|
+ mock.allow_in_project(*permissions, project:)
+ end
+ end
subject(:contract) do
- contract = described_class.new(work_package, user)
-
- contract.send(:"policy=", policy_instance)
-
- contract
+ described_class.new(work_package, user)
end
- describe "note" do
- before do
- work_package.journal_notes = "blubs"
- end
-
- context "if the user has the permissions" do
+ describe "validations" do
+ describe "journal_notes" do
before do
- allow(policy_instance).to receive(:allowed?).with(work_package, :comment).and_return true
-
- contract.validate
+ work_package.journal_notes = "blubs"
end
- it("is valid") { expect(contract.errors).to be_empty }
- end
+ context "if the user has only the add_work_package_notes permission" do
+ let(:permissions) { %i[add_work_package_notes] }
- context "if the user lacks the permissions" do
- before do
- allow(policy_instance).to receive(:allowed?).with(work_package, :comment).and_return false
-
- contract.validate
+ it_behaves_like "contract is valid"
end
- it "is invalid" do
- expect(contract.errors.symbols_for(:journal_notes))
- .to contain_exactly(:error_unauthorized)
+ context "if the user has only the edit_work_packages permission" do
+ let(:permissions) { %i[edit_work_packages] }
+
+ it_behaves_like "contract is valid"
+ end
+
+ context "if the user lacks the permissions" do
+ let(:permissions) { [] }
+
+ it_behaves_like "contract is invalid", journal_notes: :error_unauthorized
end
end
- end
- describe "journal_restricted" do
- before do
- allow(policy_instance).to receive(:allowed?).and_return(true)
- end
-
- context "with a blank note" do
+ describe "journal_restricted" do
before do
- work_package.journal_notes = ""
- work_package.journal_restricted = true
-
- contract.validate
+ # Setting the journal_notes to not trigger a :blank error
+ work_package.journal_notes = "blubs"
end
context "and journal_restricted is true, and comments_with_restricted_visibility_active? is disabled",
with_flag: { comments_with_restricted_visibility_active: false } do
- it "is invalid" do
- expect(contract.errors.full_messages).to eq(["Comment can't be blank.", "Restricted Journal is not available."])
+ before do
+ work_package.journal_restricted = true
end
+
+ it_behaves_like "contract is invalid", journal_restricted: :feature_disabled
end
context "and journal_restricted is true, and comments_with_restricted_visibility_active? is enabled",
with_flag: { comments_with_restricted_visibility_active: true } do
- it "is invalid" do
- expect(contract.errors.full_messages).to eq(["Comment can't be blank."])
- end
- end
- end
-
- context "with a note" do
- context "and journal_restricted is true", with_flag: { comments_with_restricted_visibility_active: true } do
before do
- work_package.journal_notes = "blubs"
work_package.journal_restricted = true
-
- contract.validate
end
- it("is valid") { expect(contract.errors).to be_empty }
+ it_behaves_like "contract is valid"
+ end
+
+ context "and journal_restricted is false, and comments_with_restricted_visibility_active? is disabled",
+ with_flag: { comments_with_restricted_visibility_active: false } do
+ before do
+ work_package.journal_restricted = false
+ end
+
+ it_behaves_like "contract is valid"
+ end
+
+ context "with journal_restricted is true, comments_with_restricted_visibility_active? is active but lacking permissions",
+ with_flag: { comments_with_restricted_visibility_active: true } do
+ let(:permissions) { super() - [:add_comments_with_restricted_visibility] }
+
+ before do
+ work_package.journal_restricted = true
+ end
+
+ it_behaves_like "contract is invalid", journal_restricted: :error_unauthorized
+ end
+
+ context "with journal_restricted is false, comments_with_restricted_visibility_active? is active and lacking permissions",
+ with_flag: { comments_with_restricted_visibility_active: true } do
+ let(:permissions) { super() - [:add_comments_with_restricted_visibility] }
+
+ before do
+ work_package.journal_restricted = false
+ end
+
+ it_behaves_like "contract is valid"
end
end
- end
- describe "subject" do
- before do
- work_package.subject = "blubs"
+ describe "another attribute of work package" do
+ before do
+ work_package.subject = "blubs"
+ end
- allow(policy_instance).to receive(:allowed?).and_return true
-
- contract.validate
- end
-
- it "is invalid" do
- expect(contract.errors.symbols_for(:subject))
- .to contain_exactly(:error_readonly)
+ it_behaves_like "contract is invalid", subject: :error_readonly
end
end
end
diff --git a/spec/contracts/work_packages/shared_base_contract.rb b/spec/contracts/work_packages/shared_base_contract.rb
index bd987abaa10..7e02af9c51c 100644
--- a/spec/contracts/work_packages/shared_base_contract.rb
+++ b/spec/contracts/work_packages/shared_base_contract.rb
@@ -29,7 +29,6 @@
RSpec.shared_examples_for "work package contract" do
let(:user) { build_stubbed(:user) }
let(:other_user) { build_stubbed(:user) }
- let(:policy) { double(WorkPackagePolicy, allowed?: true) }
subject(:contract) { described_class.new(work_package, user) }
@@ -39,12 +38,6 @@ RSpec.shared_examples_for "work package contract" do
contract
end
- before do
- allow(WorkPackagePolicy)
- .to receive(:new)
- .and_return(policy)
- end
-
let(:possible_assignees) { [] }
let!(:assignable_assignees_scope) do
scope = double "assignable assignees scope"
diff --git a/spec/controllers/custom_actions_controller_spec.rb b/spec/controllers/custom_actions_controller_spec.rb
index da4a1ec678f..ee28b05c1c7 100644
--- a/spec/controllers/custom_actions_controller_spec.rb
+++ b/spec/controllers/custom_actions_controller_spec.rb
@@ -46,8 +46,7 @@ RSpec.describe CustomActionsController, with_ee: %i[custom_actions] do
end
it "renders enterprise_token" do
- expect(response)
- .to render_template "common/upsale"
+ expect(response).to render_template "custom_actions/upsale"
end
end
end
diff --git a/spec/controllers/enterprises_controller_spec.rb b/spec/controllers/enterprises_controller_spec.rb
index 041f08e892b..b0a45d0b404 100644
--- a/spec/controllers/enterprises_controller_spec.rb
+++ b/spec/controllers/enterprises_controller_spec.rb
@@ -36,6 +36,7 @@ RSpec.describe EnterprisesController do
subscriber: "Foobar",
mail: "foo@example.org",
starts_at: Date.today,
+ version: "1",
expires_at: nil
}
end
diff --git a/spec/controllers/shares_controller_spec.rb b/spec/controllers/shares_controller_spec.rb
index cad2d376502..2ac3a4dc8c8 100644
--- a/spec/controllers/shares_controller_spec.rb
+++ b/spec/controllers/shares_controller_spec.rb
@@ -57,8 +57,9 @@ RSpec.describe SharesController do
mock_permissions_for(user, &:forbid_everything)
end
- it "raises a RecordNotFound error" do
- expect { make_request }.to raise_error(ActiveRecord::RecordNotFound)
+ it "returns a 404 status" do
+ make_request
+ expect(response).to have_http_status(:not_found)
end
end
@@ -87,8 +88,9 @@ RSpec.describe SharesController do
mock_permissions_for(user, &:forbid_everything)
end
- it "raises a RecordNotFound error" do
- expect { make_request }.to raise_error(ActiveRecord::RecordNotFound)
+ it "returns a 404 status" do
+ make_request
+ expect(response).to have_http_status(:not_found)
end
end
diff --git a/spec/controllers/work_packages_controller_spec.rb b/spec/controllers/work_packages_controller_spec.rb
index 16093d4d61a..5032ca9e077 100644
--- a/spec/controllers/work_packages_controller_spec.rb
+++ b/spec/controllers/work_packages_controller_spec.rb
@@ -309,10 +309,46 @@ RSpec.describe WorkPackagesController do
requires_permission_in_project do
it "render the journal/index template" do
call_action
-
expect(response).to render_template("journals/index")
end
end
+
+ context "when there are internal comments", with_flag: { comments_with_restricted_visibility: true } do
+ render_views
+
+ let(:admin) { create(:admin) }
+ let(:project) do
+ create(:project, identifier: "test_project", public: false, enabled_comments_with_restricted_visibility: true)
+ end
+
+ before do
+ work_package = create(:work_package, id: 5173, project:)
+ create(:work_package_journal,
+ journable: work_package,
+ user: admin,
+ notes: "internal comment",
+ restricted: true,
+ version: 2)
+ end
+
+ context "and the user does not have permission to see such comments" do
+ it "does not include internal comments" do
+ get("show", params: { format: "atom", id: 5173 })
+ expect(response.body).not_to include("internal comment")
+ end
+ end
+
+ context "and the user has permission to see such comments" do
+ before do
+ login_as admin
+ end
+
+ it "includes internal comments" do
+ get("show", params: { format: "atom", id: 5173 })
+ expect(response.body).to include("internal comment")
+ end
+ end
+ end
end
describe "redirect deep link", with_settings: { login_required?: true } do
diff --git a/spec/features/activities/work_package/activities_spec.rb b/spec/features/activities/work_package/activities_spec.rb
index 73ad5d2fd03..80801d5c147 100644
--- a/spec/features/activities/work_package/activities_spec.rb
+++ b/spec/features/activities/work_package/activities_spec.rb
@@ -34,7 +34,7 @@ require "support/flash/expectations"
RSpec.describe "Work package activity", :js, :with_cuprite do
include Flash::Expectations
- let(:project) { create(:project) }
+ let(:project) { create(:project, enabled_comments_with_restricted_visibility: true) }
let(:admin) { create(:admin) }
let(:member_role) do
create(:project_role,
@@ -419,57 +419,117 @@ RSpec.describe "Work package activity", :js, :with_cuprite do
end
context "when multiple users are commenting on a workpackage" do
- current_user { admin }
- let(:work_package) { create(:work_package, project:, author: admin) }
+ context "when the user has permissions to see restricted comments" do
+ current_user { admin }
+ let(:work_package) { create(:work_package, project:, author: admin) }
- before do
- # set WORK_PACKAGES_ACTIVITIES_TAB_POLLING_INTERVAL_IN_MS to 1000
- # to speed up the polling interval for test duration
- ENV["WORK_PACKAGES_ACTIVITIES_TAB_POLLING_INTERVAL_IN_MS"] = "1000"
+ before do
+ # set WORK_PACKAGES_ACTIVITIES_TAB_POLLING_INTERVAL_IN_MS to 1000
+ # to speed up the polling interval for test duration
+ ENV["WORK_PACKAGES_ACTIVITIES_TAB_POLLING_INTERVAL_IN_MS"] = "1000"
- # for some reason the journal is set to the "Anonymous"
- # although the work_package is created by the admin
- # so we need to update the journal to the admin manually to simulate the real world case
- work_package.journals.first.update!(user: admin)
+ # for some reason the journal is set to the "Anonymous"
+ # although the work_package is created by the admin
+ # so we need to update the journal to the admin manually to simulate the real world case
+ work_package.journals.first.update!(user: admin)
- wp_page.visit!
- wp_page.wait_for_activity_tab
+ wp_page.visit!
+ wp_page.wait_for_activity_tab
+ end
+
+ after do
+ ENV.delete("WORK_PACKAGES_ACTIVITIES_TAB_POLLING_INTERVAL_IN_MS")
+ end
+
+ it "shows the comment of another user without browser reload" do
+ # simulate member creating a comment
+ first_journal = create(:work_package_journal,
+ user: member,
+ notes: "First comment by member",
+ journable: work_package,
+ version: 2)
+
+ # the comment is shown without browser reload
+ activity_tab.expect_journal_notes(text: "First comment by member")
+
+ # simulate comments made within the polling interval
+ create(:work_package_journal, user: member, notes: "Second comment by member", journable: work_package, version: 3)
+ create(:work_package_journal, user: member, notes: "Third comment by member", journable: work_package, version: 4)
+
+ activity_tab.add_comment(text: "First comment by admin")
+
+ activity_tab.expect_comments_order(
+ [
+ "First comment by member",
+ "Second comment by member",
+ "Third comment by member",
+ "First comment by admin"
+ ]
+ )
+
+ first_journal.update!(notes: "First comment by member updated")
+
+ # properly updates the comment when the comment is updated
+ activity_tab.expect_journal_notes(text: "First comment by member updated")
+ end
end
- after do
- ENV.delete("WORK_PACKAGES_ACTIVITIES_TAB_POLLING_INTERVAL_IN_MS")
- end
+ context "when the user does not have permissions to see restricted comments" do
+ current_user { member }
+ let(:work_package) { create(:work_package, project:, author: admin) }
- it "shows the comment of another user without browser reload" do
- # simulate member creating a comment
- first_journal = create(:work_package_journal,
- user: member,
- notes: "First comment by member",
- journable: work_package,
- version: 2)
+ before do
+ # set WORK_PACKAGES_ACTIVITIES_TAB_POLLING_INTERVAL_IN_MS to 1000
+ # to speed up the polling interval for test duration
+ ENV["WORK_PACKAGES_ACTIVITIES_TAB_POLLING_INTERVAL_IN_MS"] = "1000"
- # the comment is shown without browser reload
- activity_tab.expect_journal_notes(text: "First comment by member")
+ # for some reason the journal is set to the "Anonymous"
+ # although the work_package is created by the admin
+ # so we need to update the journal to the admin manually to simulate the real world case
+ work_package.journals.first.update!(user: admin)
- # simulate comments made within the polling interval
- create(:work_package_journal, user: member, notes: "Second comment by member", journable: work_package, version: 3)
- create(:work_package_journal, user: member, notes: "Third comment by member", journable: work_package, version: 4)
+ wp_page.visit!
+ wp_page.wait_for_activity_tab
+ end
- activity_tab.add_comment(text: "First comment by admin")
+ after do
+ ENV.delete("WORK_PACKAGES_ACTIVITIES_TAB_POLLING_INTERVAL_IN_MS")
+ end
- activity_tab.expect_comments_order(
- [
- "First comment by member",
- "Second comment by member",
- "Third comment by member",
- "First comment by admin"
- ]
- )
+ it "does not show the comment of another user if they don't have permissions to see it" do
+ # simulate member creating a comment
+ create(:work_package_journal,
+ user: admin,
+ notes: "First comment by admin",
+ journable: work_package,
+ version: 2)
- first_journal.update!(notes: "First comment by member updated")
+ # the comment is shown without browser reload
+ activity_tab.expect_journal_notes(text: "First comment by admin")
- # properly updates the comment when the comment is updated
- activity_tab.expect_journal_notes(text: "First comment by member updated")
+ # simulate comments made within the polling interval
+ create(:work_package_journal,
+ user: admin,
+ notes: "Second comment by admin",
+ restricted: true,
+ journable: work_package,
+ version: 3)
+ create(:work_package_journal,
+ user: admin,
+ notes: "Third comment by admin",
+ restricted: true,
+ journable: work_package,
+ version: 4)
+
+ activity_tab.add_comment(text: "First comment by member")
+
+ activity_tab.expect_comments_order(
+ [
+ "First comment by admin",
+ "First comment by member"
+ ]
+ )
+ end
end
end
diff --git a/spec/features/activities/work_package/restricted_visibility_comments_spec.rb b/spec/features/activities/work_package/restricted_visibility_comments_spec.rb
index 1b9fbecfc41..b5ed478bace 100644
--- a/spec/features/activities/work_package/restricted_visibility_comments_spec.rb
+++ b/spec/features/activities/work_package/restricted_visibility_comments_spec.rb
@@ -32,16 +32,17 @@ require "spec_helper"
RSpec.describe "Work package comments with restricted visibility",
:js,
- :with_cuprite,
with_flag: { comments_with_restricted_visibility: true } do
- let(:project) { create(:project) }
- let(:admin) { create(:admin) }
- let(:viewer) { create_user_with_restricted_comments_view_permissions }
- let(:viewer_with_commenting_permission) { create_user_with_restricted_comments_view_and_write_permissions }
- let(:project_admin) { create_user_as_project_admin }
+ include RestrictedVisibilityCommentsHelpers
- let(:work_package) { create(:work_package, project:, author: admin) }
- let(:first_comment) do
+ shared_let(:project) { create(:project, enabled_comments_with_restricted_visibility: true) }
+ shared_let(:admin) { create(:admin) }
+ shared_let(:viewer) { create_user_with_restricted_comments_view_permissions }
+ shared_let(:viewer_with_commenting_permission) { create_user_with_restricted_comments_view_and_write_permissions }
+ shared_let(:project_admin) { create_user_as_project_admin }
+
+ shared_let(:work_package) { create(:work_package, project:, author: admin) }
+ shared_let(:first_comment) do
create(:work_package_journal, user: admin, notes: "A (restricted) comment by admin",
journable: work_package, version: 2, restricted: true)
end
@@ -52,17 +53,37 @@ RSpec.describe "Work package comments with restricted visibility",
context "with an admin user" do
current_user { admin }
- before do
- wp_page.visit!
- wp_page.wait_for_activity_tab
+ context "when the feature is enabled for the project" do
+ before do
+ wp_page.visit!
+ wp_page.wait_for_activity_tab
+ end
+
+ it "allows adding a comment with restricted visibility" do
+ activity_tab.expect_input_field
+
+ activity_tab.add_comment(text: "First (restricted) comment by admin", restricted: true)
+
+ activity_tab.expect_journal_notes(text: "First (restricted) comment by admin")
+ end
end
- it "allows adding a comment with restricted visibility" do
- activity_tab.expect_input_field
+ context "when the feature is not enabled for the project" do
+ before do
+ project.enabled_comments_with_restricted_visibility = false
+ project.save!
- activity_tab.add_comment(text: "First (restricted) comment by admin", restricted: true)
+ wp_page.visit!
+ wp_page.wait_for_activity_tab
+ end
- activity_tab.expect_journal_notes(text: "First (restricted) comment by admin")
+ it "allows adding a comment with restricted visibility" do
+ activity_tab.expect_input_field
+
+ activity_tab.type_comment("This comment cannot be restricted")
+
+ expect(page).not_to have_test_selector("op-work-package-journal-restricted-comment-checkbox")
+ end
end
end
@@ -173,36 +194,97 @@ RSpec.describe "Work package comments with restricted visibility",
end
end
- def create_user_as_project_admin
- member_role = create(:project_role,
- permissions: %i[view_work_packages add_work_package_notes
- edit_own_work_package_notes
- view_comments_with_restricted_visibility
- add_comments_with_restricted_visibility
- edit_own_comments_with_restricted_visibility
- edit_others_comments_with_restricted_visibility])
- create(:user, firstname: "Project", lastname: "Admin",
- member_with_roles: { project => member_role })
- end
+ describe "mentioning users in comments" do
+ current_user { project_admin }
- def create_user_with_restricted_comments_view_permissions
- viewer_role = create(:project_role, permissions: %i[view_work_packages view_comments_with_restricted_visibility])
- create(:user,
- firstname: "A",
- lastname: "Viewer",
- member_with_roles: { project => viewer_role })
- end
+ shared_let(:user_without_restricted_comments_view_permissions) do
+ create_user_without_restricted_comments_view_permissions
+ end
- def create_user_with_restricted_comments_view_and_write_permissions
- viewer_role_with_commenting_permission = create(:project_role,
- permissions: %i[view_work_packages add_work_package_notes
- edit_own_work_package_notes
- view_comments_with_restricted_visibility
- add_comments_with_restricted_visibility
- edit_own_comments_with_restricted_visibility])
- create(:user,
- firstname: "A",
- lastname: "Viewer",
- member_with_roles: { project => viewer_role_with_commenting_permission })
+ shared_let(:group) { create(:group, firstname: "A", lastname: "Group") }
+ shared_let(:group_member) do
+ group_role = create(:project_role)
+ create(:member,
+ principal: group,
+ project:,
+ roles: [group_role])
+ end
+
+ before do
+ wp_page.visit!
+ wait_for_reload
+ expect_angular_frontend_initialized
+ wp_page.wait_for_activity_tab
+ end
+
+ context "with restricted comments initially enabled" do
+ it "restricts mentions to project members with view comments with restricted visibility permission" do
+ activity_tab.open_new_comment_editor
+ expect(page).to have_test_selector("op-work-package-journal-form-element")
+
+ activity_tab.check_restricted_visibility_comment_checkbox
+ activity_tab.refocus_editor
+ activity_tab.type_comment("@")
+
+ expect(page.all(".mention-list-item").map(&:text))
+ .to contain_exactly("Project Admin", "Restricted Viewer", "Restricted ViewerCommenter")
+ end
+ end
+
+ context "with restricted comments initially disabled" do
+ it "allows mentioning project members but they are sanitized when the checkbox is checked" do
+ activity_tab.type_comment("@")
+
+ expect(page.all(".mention-list-item").map(&:text))
+ .to contain_exactly("A Viewer", "Group", "Project Admin", "Restricted Viewer", "Restricted ViewerCommenter")
+
+ page.find(".mention-list-item", text: "A Viewer").click
+ activity_tab.type_comment("@Restricted")
+ page.first(".mention-list-item", text: "Restricted Viewer").click
+ activity_tab.type_comment("@Group")
+ page.find(".mention-list-item", text: "Group").click
+
+ activity_tab.check_restricted_visibility_comment_checkbox
+
+ page.within_test_selector("op-work-package-journal-form-element") do
+ expect(page.all("a.mention").map(&:text))
+ .to contain_exactly("@Restricted Viewer")
+
+ expect(page).to have_text("@A Viewer") & have_text("@Group")
+ end
+ end
+ end
+
+ context "when editing a restricted comment" do
+ it "honors mentionable principals" do
+ activity_tab.within_journal_entry(first_comment) do
+ page.find_test_selector("op-wp-journal-#{first_comment.id}-action-menu").click
+ page.find_test_selector("op-wp-journal-#{first_comment.id}-edit").click
+
+ activity_tab.type_comment(" @")
+ end
+
+ expect(page.all(".mention-list-item").map(&:text))
+ .to contain_exactly("Project Admin", "Restricted Viewer", "Restricted ViewerCommenter")
+ end
+ end
+
+ context "with a server error" do
+ before do
+ allow(WorkPackages::ActivitiesTab::RestrictedMentionsSanitizer).to receive(:sanitize)
+ .and_raise(RuntimeError, "Something went wrong!!!")
+ end
+
+ it "shows an error message" do
+ activity_tab.type_comment("@Restricted")
+ page.first(".mention-list-item", text: "Restricted Viewer").click
+
+ activity_tab.check_restricted_visibility_comment_checkbox
+
+ page.within_test_selector("op-primer-flash-message") do
+ expect(page).to have_text("Something went wrong!!!")
+ end
+ end
+ end
end
end
diff --git a/spec/features/custom_styles/tabs_navigation_spec.rb b/spec/features/custom_styles/tabs_navigation_spec.rb
index 40dc6ea58ba..007e1daa509 100644
--- a/spec/features/custom_styles/tabs_navigation_spec.rb
+++ b/spec/features/custom_styles/tabs_navigation_spec.rb
@@ -38,7 +38,8 @@ RSpec.describe "Tabs navigation and content switching on the admin/design page"
end
it "redirects to upsale page" do
- expect(page).to have_css(".upsale-notification")
+ expect(page).to have_enterprise_upsale_page
+ expect(page).to have_text "Available only through the Basic enterprise plan"
end
end
diff --git a/spec/features/menu_items/help_menu_spec.rb b/spec/features/menu_items/help_menu_spec.rb
index d4d27b0bde4..e6e81372760 100644
--- a/spec/features/menu_items/help_menu_spec.rb
+++ b/spec/features/menu_items/help_menu_spec.rb
@@ -68,7 +68,6 @@ RSpec.describe "Help menu items", :js do
include_context "support links"
def fake_an_enterprise_token
- allow(EnterpriseToken).to receive(:show_banners?).and_return(false)
allow(EnterpriseToken).to receive(:active?).and_return(true)
end
diff --git a/spec/features/notifications/notification_center/notification_center_date_alerts_spec.rb b/spec/features/notifications/notification_center/notification_center_date_alerts_spec.rb
index 1aa2c64900a..185e8704a06 100644
--- a/spec/features/notifications/notification_center/notification_center_date_alerts_spec.rb
+++ b/spec/features/notifications/notification_center/notification_center_date_alerts_spec.rb
@@ -195,11 +195,13 @@ RSpec.describe "Notification center date alerts", :js, with_settings: { journal_
side_menu.click_item "Date alert"
expect(page).to have_current_path(/notifications\/date_alerts/)
- expect(page).to have_text "Date alerts is an Enterprise"
- expect(page).to have_text "Please upgrade to a paid plan "
+ expect(page).to have_enterprise_upsale_page(:basic)
# It does not allows direct url access
visit notifications_center_path(filter: "reason", name: "dateAlert")
+
+ expect(page).to have_current_path(/notifications\/date_alerts/)
+ expect(page).to have_enterprise_upsale_page(:basic)
end
end
diff --git a/spec/features/notifications/notification_center/split_screen_spec.rb b/spec/features/notifications/notification_center/split_screen_spec.rb
index dc216f68abc..2b375b3e37e 100644
--- a/spec/features/notifications/notification_center/split_screen_spec.rb
+++ b/spec/features/notifications/notification_center/split_screen_spec.rb
@@ -67,7 +67,7 @@ RSpec.describe "Split screen in the notification center", :js do
split_screen.switch_to_tab tab: "relations"
split_screen.expect_tab :relations
relations_tab = Components::WorkPackages::Relations.new(work_package)
- relations_tab.expect_no_relation work_package
+ relations_tab.expect_no_relations
# Navigate to full view and back
wp_full = split_screen.switch_to_fullscreen
diff --git a/spec/features/projects/life_cycle/overview_page/dialog/permission_spec.rb b/spec/features/projects/life_cycle/overview_page/dialog/permission_spec.rb
index 982c83ea190..912b1b58cee 100644
--- a/spec/features/projects/life_cycle/overview_page/dialog/permission_spec.rb
+++ b/spec/features/projects/life_cycle/overview_page/dialog/permission_spec.rb
@@ -38,6 +38,10 @@ RSpec.describe "Edit project phases on project overview page", :js, with_flag: {
current_user { user }
before do
+ # Mocking the Project::Phase.visible scope
+ allow(Project).to receive(:allowed_to).and_call_original
+ allow(Project).to receive(:allowed_to).with(user, :view_project_phases).and_return(project)
+
mock_permissions_for(user) do |mock|
mock.allow_in_project(*permissions, project:) # any project
end
@@ -67,7 +71,9 @@ RSpec.describe "Edit project phases on project overview page", :js, with_flag: {
it "does not show the edit buttons" do
overview_page.within_life_cycles_sidebar do
- expect(page).to have_no_css("[data-test-selector='project-life-cycles-edit-button']")
+ project_life_cycles.each do |lc|
+ expect(page).to have_no_link(href: edit_project_phase_path(lc))
+ end
end
end
end
@@ -77,7 +83,9 @@ RSpec.describe "Edit project phases on project overview page", :js, with_flag: {
it "shows the edit buttons" do
overview_page.within_life_cycles_sidebar do
- expect(page).to have_css("[data-test-selector='project-life-cycles-edit-button']")
+ project_life_cycles.each do |lc|
+ expect(page).to have_link(href: edit_project_phase_path(lc))
+ end
end
end
end
diff --git a/spec/features/projects/life_cycle/overview_page/dialog/update_spec.rb b/spec/features/projects/life_cycle/overview_page/dialog/update_spec.rb
index b41cf2793a7..e50c89747af 100644
--- a/spec/features/projects/life_cycle/overview_page/dialog/update_spec.rb
+++ b/spec/features/projects/life_cycle/overview_page/dialog/update_spec.rb
@@ -53,18 +53,14 @@ RSpec.describe "Edit project phases on project overview page", :js, with_flag: {
end
it "shows all the Project::Phases without a value" do
- dialog = overview_page.open_edit_dialog_for_life_cycles
+ project_life_cycles.each do |life_cycle|
+ dialog = overview_page.open_edit_dialog_for_life_cycle(life_cycle)
+ dialog.expect_input(life_cycle.name, value: "")
- dialog.expect_input("Initiating", value: "", position: 1)
- dialog.expect_input("Planning", value: "", position: 2)
- dialog.expect_input("Executing", value: "", position: 3)
- dialog.expect_input("Closing", value: "", position: 4)
+ dialog.submit # Saving the dialog is successful
+ dialog.expect_closed
+ end
- # Saving the dialog is successful
- dialog.submit
- dialog.expect_closed
-
- # Sidebar displays the same empty values
project_life_cycles.each do |life_cycle|
overview_page.within_life_cycle_container(life_cycle) do
expect(page).to have_text "-"
@@ -75,31 +71,28 @@ RSpec.describe "Edit project phases on project overview page", :js, with_flag: {
context "when all LifeCycleSteps have a value" do
it "shows all the Project::Phases and updates them correctly" do
- dialog = overview_page.open_edit_dialog_for_life_cycles
+ # Set a value for life_cycle_initiating
+ dialog = overview_page.open_edit_dialog_for_life_cycle(life_cycle_initiating)
expect_angular_frontend_initialized
- project.available_phases.each do |step|
- dialog.expect_input_for(step)
- end
+ dialog.expect_input_for(life_cycle_initiating)
initiating_dates = [start_date - 1.week, start_date]
retry_block do
# Retrying due to a race condition between filling the input vs submitting the form preview.
original_dates = [life_cycle_initiating.start_date, life_cycle_initiating.finish_date]
- dialog.set_date_for(life_cycle_initiating, values: original_dates)
+ dialog.set_date_for(values: original_dates)
page.driver.clear_network_traffic
- dialog.set_date_for(life_cycle_initiating, values: initiating_dates)
+ dialog.set_date_for(values: initiating_dates)
- dialog.expect_caption(life_cycle_initiating, text: "Duration: 8 working days")
+ dialog.expect_caption(text: "Duration: 8 working days")
# Ensure that only 1 ajax request is triggered after setting the date range.
expect(page.driver.browser.network.traffic.size).to eq(1)
end
- dialog.clear_date_for(life_cycle_planning)
-
# Saving the dialog is successful
dialog.submit
dialog.expect_closed
@@ -109,6 +102,18 @@ RSpec.describe "Edit project phases on project overview page", :js, with_flag: {
expect(page).to have_text initiating_dates.map { I18n.l(it) }.join("\n-\n")
end
+ # Clear the value of life_cycle_planning
+ dialog = overview_page.open_edit_dialog_for_life_cycle(life_cycle_planning)
+ expect_angular_frontend_initialized
+
+ dialog.expect_input_for(life_cycle_planning)
+ dialog.clear_date
+
+ # Saving the dialog is successful
+ dialog.submit
+ dialog.expect_closed
+
+ # Sidebar is refreshed with the updated values
ready_for_planning_dates = [
life_cycle_planning.start_date,
life_cycle_planning.finish_date
@@ -140,66 +145,6 @@ RSpec.describe "Edit project phases on project overview page", :js, with_flag: {
"#{I18n.l life_cycle_planning_was.finish_date}")
end
end
-
- it "shows the validation errors", with_settings: { journal_aggregation_time_minutes: 0 } do
- expect_angular_frontend_initialized
- wait_for_network_idle
-
- dialog = overview_page.open_edit_dialog_for_life_cycles
-
- expected_text = "Date range can't be earlier than the previous phase's end date."
-
- # Cycling is required so we always select a different date on the datepicker,
- # making sure the change event is triggered.
- cycled_days = [0, 1].cycle
-
- # Retrying due to a race condition between filling the input vs submitting the form preview.
- retry_block do
- values = [start_date + cycled_days.next.days] * 2
- dialog.set_date_for(life_cycle_planning, values:)
-
- dialog.expect_validation_message(life_cycle_planning, text: expected_text)
- end
-
- # Saving the dialog fails
- dialog.submit
- dialog.expect_open
-
- # The validation message is kept after the unsuccessful save attempt
- dialog.expect_validation_message(life_cycle_planning, text: expected_text)
-
- retry_block do
- # The validation message is cleared when date is changed
- values = [start_date + 2.days + cycled_days.next.days] * 2
- dialog.set_date_for(life_cycle_planning, values:)
- dialog.expect_no_validation_message(life_cycle_planning)
- end
-
- # Saving the dialog is successful
- dialog.submit
- dialog.expect_closed
-
- activity_page.visit!
-
- activity_page.show_details
-
- life_cycle_planning_was = life_cycle_planning.dup
- life_cycle_planning.reload
- activity_page.within_journal(number: 1) do
- expected_date_from = [
- I18n.l(life_cycle_planning_was.start_date),
- I18n.l(life_cycle_planning_was.finish_date)
- ].join(" - ")
-
- expected_date_to = [
- I18n.l(life_cycle_planning.start_date),
- I18n.l(life_cycle_planning.finish_date)
- ].join(" - ")
- activity_page.expect_activity("Planning changed from " \
- "#{expected_date_from} to " \
- "#{expected_date_to}")
- end
- end
end
context "when there is an invalid custom field on the project (Regression#60666)" do
@@ -211,7 +156,7 @@ RSpec.describe "Edit project phases on project overview page", :js, with_flag: {
end
it "allows saving and closing the dialog without the custom field validation to interfere" do
- dialog = overview_page.open_edit_dialog_for_life_cycles
+ dialog = overview_page.open_edit_dialog_for_life_cycle(life_cycle_initiating)
expect_angular_frontend_initialized
diff --git a/spec/features/projects/life_cycle/overview_page/sidebar_spec.rb b/spec/features/projects/life_cycle/overview_page/sidebar_spec.rb
index 5c47083dc77..009e410cb34 100644
--- a/spec/features/projects/life_cycle/overview_page/sidebar_spec.rb
+++ b/spec/features/projects/life_cycle/overview_page/sidebar_spec.rb
@@ -92,6 +92,22 @@ RSpec.describe "Show project life cycles on project overview page", :js, with_fl
end
end
+ it "shows a hover card when you hover over a gate" do
+ overview_page.visit_page
+
+ overview_page.within_life_cycles_sidebar do
+ page.find_test_selector("phase-#{life_cycle_planning.id}-start-gate").hover
+ end
+
+ expect(page).to have_test_selector("phase-gate-hover-card-name", text: life_cycle_planning.start_gate_name)
+
+ overview_page.within_life_cycles_sidebar do
+ page.find_test_selector("phase-#{life_cycle_planning.id}-finish-gate").hover
+ end
+
+ expect(page).to have_test_selector("phase-gate-hover-card-name", text: life_cycle_planning.finish_gate_name)
+ end
+
it "does not show phases not enabled for this project in a sidebar" do
life_cycle_executing.toggle!(:active)
diff --git a/spec/features/projects/persisted_lists_spec.rb b/spec/features/projects/persisted_lists_spec.rb
index f6b740e44f3..e3283e25e08 100644
--- a/spec/features/projects/persisted_lists_spec.rb
+++ b/spec/features/projects/persisted_lists_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -39,6 +41,7 @@ RSpec.describe "Persisted lists on projects index page",
shared_let(:custom_field) { create(:text_project_custom_field) }
shared_let(:invisible_custom_field) { create(:project_custom_field, admin_only: true) }
+ shared_let(:list_custom_field) { create(:list_project_custom_field) }
shared_let(:project) do
create(:project,
@@ -52,7 +55,8 @@ RSpec.describe "Persisted lists on projects index page",
public: true)
project.custom_field_values = {
invisible_custom_field.id => "Secret CF",
- custom_field.id => "Visible CF"
+ custom_field.id => "Visible CF",
+ list_custom_field.id => list_custom_field.possible_values.first.id
}
project.save
project
@@ -450,7 +454,6 @@ RSpec.describe "Persisted lists on projects index page",
it "keep the query active when applying orders, page and column changes" do
projects_page.visit!
-
# The user can select the list but cannot see another user's list
projects_page.set_sidebar_filter(my_projects_list.name)
projects_page.expect_no_sidebar_filter(another_users_projects_list.name)
@@ -551,6 +554,32 @@ RSpec.describe "Persisted lists on projects index page",
development_project) # Because it is on the second page
end
+ it "shows the saved filter values in the filter section" do
+ my_projects_list.where("project_status_code", "=", Project.status_codes["on_track"])
+ my_projects_list.save!
+
+ projects_page.visit!
+ projects_page.set_sidebar_filter(my_projects_list.name)
+ projects_page.expect_filter_set("project_status_code", value: "On track")
+
+ projects_page.open_filters
+ projects_page.set_filter(list_custom_field.column_name,
+ list_custom_field.name,
+ "is (OR)",
+ [list_custom_field.possible_values.first.value])
+
+ wait_for_reload
+ projects_page.save_query
+
+ projects_page.set_sidebar_filter(my_projects_list.name)
+ projects_page.expect_filter_set("project_status_code", value: "On track")
+
+ projects_page.expect_filter_set(
+ list_custom_field.column_name,
+ value: list_custom_field.possible_values.first.value
+ )
+ end
+
it "cannot access another user`s list" do
visit projects_path(query_id: another_users_projects_list.id)
diff --git a/modules/meeting/spec/requests/api/v3/attachments/meeting_minutes_spec.rb b/spec/features/projects/settings/activities_settings_spec.rb
similarity index 55%
rename from modules/meeting/spec/requests/api/v3/attachments/meeting_minutes_spec.rb
rename to spec/features/projects/settings/activities_settings_spec.rb
index 2bc8640fdea..965cb437f49 100644
--- a/modules/meeting/spec/requests/api/v3/attachments/meeting_minutes_spec.rb
+++ b/spec/features/projects/settings/activities_settings_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -28,17 +29,29 @@
#++
require "spec_helper"
-require "requests/api/v3/attachments/attachment_resource_shared_examples"
-RSpec.describe "meeting minutes attachments" do
- it_behaves_like "an APIv3 attachment resource" do
- let(:attachment_type) { :meeting_content }
+RSpec.describe "WorkPackages-Settings-Activities", :js do
+ let!(:project) { create(:project) }
+ let(:activities_settings_page) { Pages::Projects::Settings::Activities.new(project) }
- let(:create_permission) { :create_meetings }
- let(:read_permission) { :view_meetings }
- let(:update_permission) { :edit_meetings }
+ current_user { create(:admin) }
- let(:meeting_content) { create(:meeting_minutes, meeting:) }
- let(:meeting) { create(:meeting, project:) }
+ it "enables and disables the settings for the project", with_ee: %i[comments_with_restricted_visibility] do
+ activities_settings_page.visit!
+ expect(page).to have_css("#activities-form")
+
+ expect(page).to have_field(:project_enabled_comments_with_restricted_visibility, checked: false)
+
+ check("Enable restricted visibility comments")
+ click_link_or_button "Save"
+
+ expect_and_dismiss_flash(message: "Successful update.")
+ expect(page).to have_field(:project_enabled_comments_with_restricted_visibility, checked: true)
+
+ uncheck("Enable restricted visibility comments")
+ click_link_or_button "Save"
+
+ expect_and_dismiss_flash(message: "Successful update.")
+ expect(page).to have_field(:project_enabled_comments_with_restricted_visibility, checked: false)
end
end
diff --git a/spec/features/projects/work_packages_settings_spec.rb b/spec/features/projects/settings/work_packages_settings_spec.rb
similarity index 79%
rename from spec/features/projects/work_packages_settings_spec.rb
rename to spec/features/projects/settings/work_packages_settings_spec.rb
index c3cf8bd111c..bb22f9bfeb8 100644
--- a/spec/features/projects/work_packages_settings_spec.rb
+++ b/spec/features/projects/settings/work_packages_settings_spec.rb
@@ -72,11 +72,34 @@ RSpec.describe "Projects", "work packages settings menu", :js do
end
end
- context "when the user does not have access to any tabs" do
+ context "when the user has access to the activities tab", with_ee: %i[comments_with_restricted_visibility] do
let(:permissions) { %i(edit_project view_work_packages) }
current_user { create(:user, member_with_permissions: { project => permissions }) }
+ it "displays the custom fields tab" do
+ work_packages_settings_page.visit!
+ expect(page).to have_css(".tabnav-tab", text: "Activity")
+ expect(page).to have_css("#activities-form")
+ end
+ end
+
+ context "when the user has access to activities tab but not to enterprise" do
+ let(:permissions) { %i(edit_project view_work_packages) }
+
+ current_user { create(:user, member_with_permissions: { project => permissions }) }
+
+ it "shows the enterprise banner" do
+ work_packages_settings_page.visit!
+ expect(page).to have_test_selector("op-enterprise-banner")
+ end
+ end
+
+ context "when the user does not have access to any tabs" do
+ let(:permissions) { %i(view_work_packages) }
+
+ current_user { create(:user, member_with_permissions: { project => permissions }) }
+
it "does not display the menu entry" do
general_settings_page.visit!
expect(page).to have_no_css(".op-menu--item-title", text: "Work packages")
diff --git a/spec/features/types/form_configuration_query_spec.rb b/spec/features/types/form_configuration_query_spec.rb
index ffb752241c4..f259596ad40 100644
--- a/spec/features/types/form_configuration_query_spec.rb
+++ b/spec/features/types/form_configuration_query_spec.rb
@@ -226,6 +226,7 @@ RSpec.describe "form query configuration", :js do
expect(column_names).to eq %i[id]
form.edit_query_group("Second query")
+
modal.switch_to "Columns"
columns.expect_checked "ID"
columns.apply
diff --git a/spec/features/work_packages/custom_actions/custom_actions_spec.rb b/spec/features/work_packages/custom_actions/custom_actions_spec.rb
index 88e100809b3..6422839b1bf 100644
--- a/spec/features/work_packages/custom_actions/custom_actions_spec.rb
+++ b/spec/features/work_packages/custom_actions/custom_actions_spec.rb
@@ -312,9 +312,8 @@ RSpec.describe "Custom actions", :js, with_ee: %i[custom_actions] do
within(".custom-actions") do
# When hovering over the button, the description is displayed
- button = find(".custom-action--button", text: "Unassign")
- expect(button["title"])
- .to eql "Removes the assignee"
+ expect(page)
+ .to have_button("Unassign", title: "Removes the assignee")
end
wp_page.click_custom_action("Unassign")
diff --git a/spec/features/work_packages/details/milestones_spec.rb b/spec/features/work_packages/details/milestones_spec.rb
index c50de5a0a4e..3fd234840a1 100644
--- a/spec/features/work_packages/details/milestones_spec.rb
+++ b/spec/features/work_packages/details/milestones_spec.rb
@@ -11,7 +11,6 @@ RSpec.describe "Milestones full screen v iew", :js do
end
let(:wp_page) { Pages::FullWorkPackage.new(work_package, project) }
- let(:button) { find(".add-work-package", wait: 5) }
before do
login_as(user)
@@ -28,9 +27,7 @@ RSpec.describe "Milestones full screen v iew", :js do
end
it "shows the button as enabled" do
- expect(button).not_to be_disabled
-
- button.click
+ click_button("Create", class: "add-work-package")
expect(page).to have_css(".menu-item", text: type.name.upcase)
end
end
@@ -45,7 +42,7 @@ RSpec.describe "Milestones full screen v iew", :js do
end
it "shows the button as correctly disabled" do
- expect(button["disabled"]).to be_truthy
+ expect(page).to have_button("Create", class: "add-work-package", disabled: true)
end
end
end
diff --git a/spec/features/work_packages/display_representations/switch_display_representations_on_mobile_spec.rb b/spec/features/work_packages/display_representations/switch_display_representations_on_mobile_spec.rb
index 6b430f9fbf7..378829a4ce8 100644
--- a/spec/features/work_packages/display_representations/switch_display_representations_on_mobile_spec.rb
+++ b/spec/features/work_packages/display_representations/switch_display_representations_on_mobile_spec.rb
@@ -46,7 +46,6 @@ RSpec.describe "Switching work package view on mobile", :js, :selenium do
before do
wp_1
wp_2
- allow(EnterpriseToken).to receive(:show_banners?).and_return(false)
login_as(user)
wp_table.visit!
diff --git a/spec/features/work_packages/export_spec.rb b/spec/features/work_packages/export_spec.rb
index dbbd962b71e..010db9763a1 100644
--- a/spec/features/work_packages/export_spec.rb
+++ b/spec/features/work_packages/export_spec.rb
@@ -102,10 +102,19 @@ RSpec.describe "work package export", :js, :selenium do
end
end
- def open_export_dialog!
- wp_table.visit_query query
+ def open_export_dialog!(query_target = :query)
+ case query_target
+ when :query
+ wp_table.visit_query query
+ when :default
+ wp_table.visit_with_params ""
+ else
+ raise ArgumentError, "query_target must be :query or :default"
+ end
+
work_packages_page.ensure_loaded
settings_menu.open_and_choose I18n.t("js.toolbar.settings.export")
+ expect(page).to have_css("#op-work-packages-export-dialog", wait: 5)
click_on export_type
sleep 0.1
end
@@ -115,6 +124,26 @@ RSpec.describe "work package export", :js, :selenium do
expect(page).to have_no_button(I18n.t("export.dialog.submit"), wait: 1)
end
+ def close_export_dialog!
+ expect(page).to have_button(I18n.t("js.button_close"))
+ click_on I18n.t("js.button_close")
+ expect(page).to have_no_button(I18n.t("js.button_close"))
+ end
+
+ def export_and_reopen_dialog!(query_target = :query)
+ export!
+ close_export_dialog!
+ open_export_dialog!(query_target)
+ end
+
+ def expect_selected_columns(columns = %w[])
+ selected_columns = page.within(".op-draggable-autocomplete--selected") do
+ all(".op-draggable-autocomplete--item-text").map(&:text)
+ end
+
+ expect(selected_columns).to eq(columns)
+ end
+
context "with Query options" do
let(:export_type) { I18n.t("export.dialog.format.options.pdf.label") }
let(:expected_mime_type) { :pdf }
@@ -182,6 +211,7 @@ RSpec.describe "work package export", :js, :selenium do
let(:expected_params) { default_expected_params }
before do
+ query.export_settings.delete_all
open_export_dialog!
end
@@ -204,6 +234,91 @@ RSpec.describe "work package export", :js, :selenium do
end
end
+ context "with a saved query" do
+ let!(:query) { create(:query, name: "saved settings query", user: current_user, project:) }
+ let(:export_type) { I18n.t("export.dialog.format.options.csv.label") }
+ let(:expected_mime_type) { :csv }
+ let(:expected_columns) { %w[ID Subject Type Status Assignee Priority] }
+ let(:expected_params) do
+ default_expected_params.merge(
+ {
+ title: "saved settings query",
+ show_descriptions: "true",
+ columns: %w[subject type status assigned_to priority]
+ }
+ )
+ end
+ let(:query_columns) do
+ query.displayable_columns.filter_map { |c| c.name.to_s if expected_columns.include?(c.name.to_s) }
+ end
+
+ before do
+ open_export_dialog!
+ end
+
+ it "saves the export settings" do
+ # Ensure that the option to save export settings is there and both checkboxes are unchecked
+ expect(page.find_test_selector("op-work-packages-export-dialog-form-save-export-settings")).not_to be_checked
+ expect(page.find_test_selector("show-descriptions-csv")).not_to be_checked
+ expect_selected_columns(expected_columns)
+
+ # Save settings and include descriptions
+ check I18n.t("export.dialog.save_export_settings.label")
+ check I18n.t("export.dialog.xls.include_descriptions.label")
+ # Remove the first column
+ page.within(".op-draggable-autocomplete--selected") do
+ first(".op-draggable-autocomplete--remove-item").click
+ end
+
+ export_and_reopen_dialog!
+ # Last settings are remembered
+ expect(page.find_test_selector("show-descriptions-csv")).to be_checked
+ expect(page.find_test_selector("op-work-packages-export-dialog-form-save-export-settings")).to be_checked
+ expect_selected_columns(expected_columns - ["ID"])
+
+ # Uncheck both checkboxes again (do not include descriptions, do not save changes)
+ uncheck I18n.t("export.dialog.save_export_settings.label")
+ uncheck I18n.t("export.dialog.xls.include_descriptions.label")
+ # Remove the last column
+ page.within(".op-draggable-autocomplete--selected") do
+ all(".op-draggable-autocomplete--remove-item").last.click
+ end
+ # Adjust expectation and export
+ expected_params[:show_descriptions] = "false"
+ expected_params[:columns].pop
+ export_and_reopen_dialog!
+
+ # Last saved settings are restored
+ expect(page.find_test_selector("show-descriptions-csv")).to be_checked
+ expect(page.find_test_selector("op-work-packages-export-dialog-form-save-export-settings")).to be_checked
+ expect_selected_columns(expected_columns - ["ID"])
+ end
+ end
+
+ context "with an unsaved query" do
+ let(:export_type) { I18n.t("export.dialog.format.options.csv.label") }
+ let(:expected_mime_type) { :csv }
+ let(:expected_params) { default_expected_params.merge({ title: "All open", show_descriptions: "true" }) }
+
+ before do
+ open_export_dialog!(:default)
+ end
+
+ it "does not offer to save export settings" do
+ # There is no save option
+ expect(page).not_to have_test_selector("op-work-packages-export-dialog-form-save-export-settings")
+ # show_descriptions is unchecked by default
+ expect(page.find_test_selector("show-descriptions-csv")).not_to be_checked
+
+ # Check show_descriptions and export, then reopen dialog
+ check I18n.t("export.dialog.xls.include_descriptions.label")
+ export_and_reopen_dialog!(:default)
+
+ # show_descriptions is still unchecked
+ expect(page.find_test_selector("show-descriptions-csv")).not_to be_checked
+ end
+ end
+
context "with XLS export" do
let(:export_type) { I18n.t("export.dialog.format.options.xls.label") }
let(:expected_mime_type) { :xls }
@@ -299,12 +414,25 @@ RSpec.describe "work package export", :js, :selenium do
end
context "with long text fields selection" do
- let(:expected_params) { default_params_report.merge({ long_text_fields: "description 43" }) }
+ let(:expected_params) { default_params_report.merge({ long_text_fields: "description #{cf_text_b.id}" }) }
it "exports a pdf report with all remaining custom fields" do
- find("span.op-draggable-autocomplete--item-text", text: "Long text custom field")
- .sibling(".op-draggable-autocomplete--remove-item").click
- export!
+ # Remove one custom field
+ page.within(".op-angular-component[data-id='\"ltf-select-export-pdf-report\"']") do
+ find(".op-draggable-autocomplete--item-text", text: "Long text custom field")
+ .sibling(".op-draggable-autocomplete--remove-item").click
+ end
+
+ # Save export settings, export and reopen dialog
+ check I18n.t("export.dialog.save_export_settings.label")
+ export_and_reopen_dialog!
+ choose export_sub_type
+
+ selected_long_fields = page.within(".op-angular-component[data-id='\"ltf-select-export-pdf-report\"']") do
+ all(".op-draggable-autocomplete--item-text").map(&:text)
+ end
+ # The removed field has been saved
+ expect(selected_long_fields).to contain_exactly("Description", cf_text_b.name)
end
end
diff --git a/spec/features/work_packages/highlighting_spec.rb b/spec/features/work_packages/highlighting_spec.rb
index 41763c187a9..da9902953c6 100644
--- a/spec/features/work_packages/highlighting_spec.rb
+++ b/spec/features/work_packages/highlighting_spec.rb
@@ -53,7 +53,6 @@ RSpec.describe "Work Package highlighting fields",
# Ensure Rails and Capybara caches are cleared
Rails.cache.clear
Capybara.reset!
- allow(EnterpriseToken).to receive(:show_banners?).and_return(false)
login_as(user)
wp_table.visit_query query
wp_table.expect_work_package_listed wp_1, wp_2
diff --git a/spec/features/work_packages/table/baseline/baseline_rendering_spec.rb b/spec/features/work_packages/table/baseline/baseline_rendering_spec.rb
index fca8da623db..29d1584c42a 100644
--- a/spec/features/work_packages/table/baseline/baseline_rendering_spec.rb
+++ b/spec/features/work_packages/table/baseline/baseline_rendering_spec.rb
@@ -426,7 +426,7 @@ RSpec.describe "baseline rendering",
baseline_modal.expect_closed
baseline_modal.toggle_drop_modal
baseline_modal.expect_open
- expect(page).to have_css(".op-baseline--enterprise-title")
+ expect(page).to have_enterprise_banner
# only yesterday is selectable
page.select("a specific date", from: "op-baseline-filter")
expect(page).to have_no_select("op-baseline-filter", selected: "a specific date")
diff --git a/spec/helpers/enterprise_helper_spec.rb b/spec/helpers/enterprise_helper_spec.rb
index dd7f0b7093e..ba768e1ea37 100644
--- a/spec/helpers/enterprise_helper_spec.rb
+++ b/spec/helpers/enterprise_helper_spec.rb
@@ -52,7 +52,7 @@ RSpec.describe EnterpriseHelper do
let(:token) { instance_double(EnterpriseToken, features: %i[baseline_comparison virus_scanning]) }
it "returns translated names of features" do
- expect(helper.enterprise_plan_additional_features(token)).to eq("Attachment Virus Scanning, Baseline Comparison")
+ expect(helper.enterprise_plan_additional_features(token)).to eq("Antivirus Scanning, Baseline Comparisons")
end
end
@@ -63,7 +63,7 @@ RSpec.describe EnterpriseHelper do
end
it "returns translated names of features and removes unknowns" do
- expect(helper.enterprise_plan_additional_features(token)).to eq("Work Package Sharing")
+ expect(helper.enterprise_plan_additional_features(token)).to eq("Share work packages with external users")
end
end
end
diff --git a/spec/lib/api/v3/activities/activity_eager_loading_wrapper_spec.rb b/spec/lib/api/v3/activities/activity_eager_loading_wrapper_spec.rb
index c8d21fde89e..0b35102cb5b 100644
--- a/spec/lib/api/v3/activities/activity_eager_loading_wrapper_spec.rb
+++ b/spec/lib/api/v3/activities/activity_eager_loading_wrapper_spec.rb
@@ -70,16 +70,6 @@ RSpec.describe API::V3::Activities::ActivityEagerLoadingWrapper, with_settings:
expect(meeting.journals).to be_wrappable
end
- it "can wrap MeetingAgenda journals" do
- meeting_agenda = create(:meeting_agenda, meeting:)
- expect(meeting_agenda.journals).to be_wrappable
- end
-
- it "can wrap MeetingMinutes journals" do
- meeting_minutes = create(:meeting_minutes, meeting:)
- expect(meeting_minutes.journals).to be_wrappable
- end
-
it "can wrap Budget journals" do
budget = create(:budget, project:, author: user)
expect(budget.journals).to be_wrappable
diff --git a/spec/lib/api/v3/configuration/configuration_representer_spec.rb b/spec/lib/api/v3/configuration/configuration_representer_spec.rb
index 4ca2e451442..8e96007a38b 100644
--- a/spec/lib/api/v3/configuration/configuration_representer_spec.rb
+++ b/spec/lib/api/v3/configuration/configuration_representer_spec.rb
@@ -245,6 +245,24 @@ RSpec.describe API::V3::Configuration::ConfigurationRepresenter do
end
end
end
+
+ describe "availableFeatures" do
+ context "without any features" do
+ it "is an empty array" do
+ expect(subject)
+ .to be_json_eql([].to_json)
+ .at_path("availableFeatures")
+ end
+ end
+
+ context "with certain features allowed", with_ee: %i[some_value foobar] do
+ it "is an array of strings of those flags" do
+ expect(subject)
+ .to be_json_eql(%w(some_value foobar).to_json)
+ .at_path("availableFeatures")
+ end
+ end
+ end
end
describe "_embedded" do
diff --git a/spec/models/enterprise_token_spec.rb b/spec/models/enterprise_token_spec.rb
index 1475a548211..c2e121627bf 100644
--- a/spec/models/enterprise_token_spec.rb
+++ b/spec/models/enterprise_token_spec.rb
@@ -1,14 +1,44 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
require "spec_helper"
RSpec.describe EnterpriseToken do
let(:object) { OpenProject::Token.new domain: Setting.host_name }
- let(:ee_manager_visible) { true }
+ let(:ee_hide_banners) { true }
- subject { EnterpriseToken.new(encoded_token: "foo") }
+ subject { described_class.new(encoded_token: "foo") }
before do
RequestStore.delete :current_ee_token
- allow(OpenProject::Configuration).to receive(:ee_manager_visible?).and_return(ee_manager_visible)
+ allow(OpenProject::Configuration).to receive(:ee_hide_banners?).and_return(ee_hide_banners)
end
describe ".active?" do
@@ -39,81 +69,20 @@ RSpec.describe EnterpriseToken do
end
end
- describe ".show_banners?" do
- before do
- allow(described_class).to receive(:allows_to?).with(:active_feature).and_return(true)
- allow(described_class).to receive(:allows_to?).with(:inactive_feature).and_return(false)
- end
-
+ describe ".hide_banners?" do
context "when ee manager is visible" do
- let(:ee_manager_visible) { true }
+ let(:ee_hide_banners) { true }
- context "when token is active" do
- before { allow(described_class).to receive(:active?).and_return(true) }
-
- it "returns false when requesting without a feature" do
- expect(described_class).not_to be_show_banners
- end
-
- it "returns false when requesting with a feature that is enabled in the token" do
- expect(described_class).not_to be_show_banners(feature: :active_feature)
- end
-
- it "returns true when requesting with a feature that is disabled in the token" do
- expect(described_class).to be_show_banners(feature: :inactive_feature)
- end
- end
-
- context "when token is inactive" do
- before { allow(described_class).to receive(:active?).and_return(false) }
-
- it "returns true when requesting without a feature" do
- expect(described_class).to be_show_banners
- end
-
- it "returns true when requesting with a feature that is enabled in the token" do
- expect(described_class).to be_show_banners(feature: :active_feature)
- end
-
- it "returns true when requesting with a feature that is disabled in the token" do
- expect(described_class).to be_show_banners(feature: :inactive_feature)
- end
+ it "returns true" do
+ expect(described_class).to be_hide_banners
end
end
context "when ee manager is not visible" do
- let(:ee_manager_visible) { false }
+ let(:ee_hide_banners) { false }
- context "when token is active" do
- before { allow(described_class).to receive(:active?).and_return(true) }
-
- it "returns false when requesting without a feature" do
- expect(described_class).not_to be_show_banners
- end
-
- it "returns false when requesting with a feature that is enabled in the token" do
- expect(described_class).not_to be_show_banners(feature: :active_feature)
- end
-
- it "returns false when requesting with a feature that is disabled in the token" do
- expect(described_class).not_to be_show_banners(feature: :inactive_feature)
- end
- end
-
- context "when token is inactive" do
- before { allow(described_class).to receive(:active?).and_return(false) }
-
- it "returns false when requesting without a feature" do
- expect(described_class).not_to be_show_banners
- end
-
- it "returns false when requesting with a feature that is enabled in the token" do
- expect(described_class).not_to be_show_banners(feature: :active_feature)
- end
-
- it "returns false when requesting with a feature that is disabled in the token" do
- expect(described_class).not_to be_show_banners(feature: :inactive_feature)
- end
+ it "returns false" do
+ expect(described_class).not_to be_hide_banners
end
end
end
@@ -157,39 +126,38 @@ RSpec.describe EnterpriseToken do
describe "existing token" do
before do
- allow_any_instance_of(EnterpriseToken).to receive(:token_object).and_return(object)
+ allow_any_instance_of(described_class).to receive(:token_object).and_return(object) # rubocop:disable RSpec/AnyInstance
subject.save!(validate: false)
end
context "when inner token is active" do
it "has an active token" do
- expect(object).to receive(:expired?).and_return(false)
- expect(EnterpriseToken.count).to eq(1)
- expect(EnterpriseToken.current).to eq(subject)
- expect(EnterpriseToken.current.encoded_token).to eq("foo")
- expect(EnterpriseToken.show_banners?).to be(false)
+ allow(object).to receive(:expired?).and_return(false)
+ expect(described_class.count).to eq(1)
+ expect(described_class.current).to eq(subject)
+ expect(described_class.current.encoded_token).to eq("foo")
# Deleting it updates the current token
- EnterpriseToken.current.destroy!
+ described_class.current.destroy!
- expect(EnterpriseToken.count).to eq(0)
- expect(EnterpriseToken.current).to be_nil
+ expect(described_class.count).to eq(0)
+ expect(described_class.current).to be_nil
end
it "delegates to the token object" do
allow(object).to receive_messages(
subscriber: "foo",
mail: "bar",
- starts_at: Date.today,
- issued_at: Date.today,
+ starts_at: Time.zone.today,
+ issued_at: Time.zone.today,
expires_at: "never",
restrictions: { foo: :bar }
)
expect(subject.subscriber).to eq("foo")
expect(subject.mail).to eq("bar")
- expect(subject.starts_at).to eq(Date.today)
- expect(subject.issued_at).to eq(Date.today)
+ expect(subject.starts_at).to eq(Time.zone.today)
+ expect(subject.issued_at).to eq(Time.zone.today)
expect(subject.expires_at).to eq("never")
expect(subject.restrictions).to eq(foo: :bar)
end
@@ -198,38 +166,40 @@ RSpec.describe EnterpriseToken do
let(:service_double) { Authorization::EnterpriseService.new(subject) }
before do
- expect(Authorization::EnterpriseService)
- .to receive(:new).twice.with(subject).and_return(service_double)
+ allow(Authorization::EnterpriseService)
+ .to receive(:new)
+ .with(subject)
+ .and_return(service_double)
end
it "forwards to EnterpriseTokenService for checks" do
- expect(service_double)
+ allow(service_double)
.to receive(:call)
.with(:forbidden_action)
- .and_return double("ServiceResult", result: false)
- expect(service_double)
+ .and_return ServiceResult.success(result: false)
+ allow(service_double)
.to receive(:call)
.with(:allowed_action)
- .and_return double("ServiceResult", result: true)
+ .and_return ServiceResult.success(result: true)
- expect(EnterpriseToken.allows_to?(:forbidden_action)).to be false
- expect(EnterpriseToken.allows_to?(:allowed_action)).to be true
+ expect(described_class.allows_to?(:forbidden_action)).to be false
+ expect(described_class.allows_to?(:allowed_action)).to be true
end
end
end
context "when inner token is expired" do
before do
- expect(object).to receive(:expired?).and_return(true)
+ allow(object).to receive(:expired?).and_return(true)
end
it "has an expired token" do
- expect(EnterpriseToken.current).to eq(subject)
- expect(EnterpriseToken.show_banners?).to be(true)
+ expect(described_class.current).to eq(subject)
+ expect(described_class).not_to be_active
end
end
- context "updating it with an invalid token" do
+ context "when updating it with an invalid token" do
it "fails validations" do
subject.encoded_token = "bar"
expect(subject.save).to be_falsey
@@ -239,22 +209,22 @@ RSpec.describe EnterpriseToken do
describe "no token" do
it do
- expect(EnterpriseToken.current).to be_nil
- expect(EnterpriseToken.show_banners?).to be(true)
+ expect(described_class.current).to be_nil
+ expect(described_class).not_to be_active
end
end
describe "invalid token" do
it "appears as if no token is shown" do
- expect(EnterpriseToken.current).to be_nil
- expect(EnterpriseToken.show_banners?).to be(true)
+ expect(described_class.current).to be_nil
+ expect(described_class).not_to be_active
end
end
- describe "Configuration file has `ee_manager_visible` set to false" do
+ describe "Configuration file has `ee_hide_banners` set to false" do
it "does not show banners promoting EE" do
- expect(OpenProject::Configuration).to receive(:ee_manager_visible?).and_return(false)
- expect(EnterpriseToken.show_banners?).to be_falsey
+ allow(OpenProject::Configuration).to receive(:ee_hide_banners?).and_return(false)
+ expect(described_class).not_to be_hide_banners
end
end
end
diff --git a/spec/models/export_settings/export_setting_spec.rb b/spec/models/export_settings/export_setting_spec.rb
new file mode 100644
index 00000000000..504767d11a5
--- /dev/null
+++ b/spec/models/export_settings/export_setting_spec.rb
@@ -0,0 +1,147 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+require "spec_helper"
+
+RSpec.shared_examples "a successful creation" do |format|
+ it "creates successfully with format #{format}" do
+ expect(described_class.create(query:, settings:, format:)).to be_persisted
+ end
+end
+
+RSpec.describe ExportSetting do
+ let(:query) { create(:query, name: "some query") }
+ let(:format) { "csv" }
+ let(:settings) { { "columns" => %w[id subject], "show_descriptions" => "true" } }
+
+ it "returns its settings with symbol keys" do
+ export_settings = described_class.create(query:, settings:, format:)
+
+ expect(export_settings.settings).to eq({ columns: %w[id subject], show_descriptions: "true" })
+
+ export_settings.settings = { "foo" => "bar" }
+ expect(export_settings.settings).to eq({ foo: "bar" })
+ end
+
+ describe "creating" do
+ formats = %w[csv xls pdf_report pdf_table pdf_gantt]
+
+ formats.each do |format|
+ include_examples "a successful creation", format
+ end
+
+ it "fails with an invalid format" do
+ expect(described_class.create(query:, settings:, format: "invalid_format")).not_to be_persisted
+ end
+
+ it "fails without format" do
+ expect(described_class.create(query:, settings:)).not_to be_persisted
+ end
+
+ it "fails without settings" do
+ expect(described_class.create(query:, format:)).not_to be_persisted
+ end
+
+ it "fails without query" do
+ expect(described_class.create(settings:, format:)).not_to be_persisted
+ end
+
+ it "fails when there already is this query/format combination" do
+ expect(described_class.create(query:, settings:, format: "csv")).to be_persisted
+
+ duplicate = described_class.create(query:, settings:, format: "csv")
+ expect(duplicate).not_to be_persisted
+ expect(duplicate.errors[:format]).to include("there already is an export setting for this query with this format")
+
+ new_query = create(:query, name: "another query")
+ expect(described_class.create(query: new_query, settings:, format: "csv")).to be_persisted
+ end
+ end
+
+ describe "#true?" do
+ let(:settings) do
+ {
+ string_true: "true",
+ real_true: true,
+ string_one_true: "1",
+ one_true: 1,
+ string_false: "false",
+ real_false: false,
+ string_zero_false: "0",
+ zero_false: 0,
+ truthy_string: "any string is truthy",
+ truthy_array: []
+ }
+ end
+ let(:instance) { described_class.new(query:, settings:, format:) }
+
+ it "returns true for a value that looks like it should be true" do
+ true_keys = %i[
+ string_true
+ real_true
+ string_one_true
+ one_true
+ ]
+
+ true_keys.each do |key|
+ expect(instance.true?(key)).to be true
+ end
+ end
+
+ it "returns false for a value that looks like it should be false" do
+ false_keys = %i[
+ string_false
+ real_false
+ string_zero_false
+ zero_false
+ ]
+
+ false_keys.each do |key|
+ expect(instance.true?(key)).to be false
+ end
+ end
+
+ it "returns false for values that are truthy in Ruby" do
+ truthy_keys = %i[truthy_string truthy_array]
+
+ truthy_keys.each do |key|
+ expect(instance.true?(key)).to be false
+ end
+ end
+
+ it "returns false for keys that do not exist" do
+ expect(instance.true?(:non_existent_key)).to be false
+ end
+
+ it "returns a default value for non existent keys" do
+ expect(instance.true?(:non_existent_key, default: true)).to be true
+ end
+ end
+end
diff --git a/spec/models/queries/principals/filters/restricted_mentionable_on_work_package_filter_spec.rb b/spec/models/queries/principals/filters/restricted_mentionable_on_work_package_filter_spec.rb
new file mode 100644
index 00000000000..98b52ab5c23
--- /dev/null
+++ b/spec/models/queries/principals/filters/restricted_mentionable_on_work_package_filter_spec.rb
@@ -0,0 +1,128 @@
+# frozen_string_literal: true
+
+# -- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+# ++
+
+require "spec_helper"
+
+RSpec.describe Queries::Principals::Filters::RestrictedMentionableOnWorkPackageFilter do
+ it_behaves_like "basic query filter" do
+ include RestrictedVisibilityCommentsHelpers
+
+ let(:class_key) { :restricted_mentionable_on_work_package }
+ let(:type) { :list_optional }
+ let(:human_name) { "restricted mentionable" }
+
+ shared_let(:project) { create(:project) }
+ shared_let(:work_package) { create(:work_package, project:) }
+ shared_let(:other_work_package) { create(:work_package, project:) }
+
+ shared_let(:user_without_restricted_comments_view_permissions) { create_user_without_restricted_comments_view_permissions }
+ shared_let(:user_with_restricted_comments_view_permissions) { create_user_with_restricted_comments_view_permissions }
+ shared_let(:user_with_restricted_comments_view_and_write_permissions) do
+ create_user_with_restricted_comments_view_and_write_permissions
+ end
+
+ let(:user) { user_with_restricted_comments_view_permissions }
+
+ before { allow(User).to receive(:current).and_return(user) }
+
+ describe "#validate" do
+ it "is valid with a single work package id" do
+ instance.values = [work_package.id.to_s]
+ expect(instance).to be_valid
+ end
+
+ it "is invalid with multiple work package ids" do
+ instance.values = [work_package.id.to_s, other_work_package.id.to_s]
+ expect(instance).not_to be_valid
+ expect(instance.errors.messages).to eq(values: ["must be a single work package"])
+ end
+
+ it "is invalid with no work package id" do
+ instance.values = []
+ expect(instance).not_to be_valid
+ expect(instance.errors.messages).to eq(values: ["can't be blank.", "filter has invalid values."])
+ end
+ end
+
+ describe "#scope" do
+ subject { instance.apply_to(Principal.visible(user)) }
+
+ let(:values) { [work_package.id.to_s] }
+
+ let(:instance) do
+ described_class.create!.tap do |filter|
+ filter.values = values
+ filter.operator = operator
+ end
+ end
+
+ context "with an = operator" do
+ let(:operator) { "=" }
+
+ it "returns all mentionable principals on the work package and its project" do
+ expect(subject)
+ .to contain_exactly(user_with_restricted_comments_view_permissions,
+ user_with_restricted_comments_view_and_write_permissions)
+ end
+
+ context "with users and groups" do
+ let(:group_member1) { create(:user) }
+ let(:group_member2) { create(:user) }
+ let(:group_role) { create(:project_role, permissions: %i[view_work_packages view_comments_with_restricted_visibility]) }
+ let(:group) do
+ create(:group, members: [group_member1, group_member2]) do |group|
+ Members::CreateService
+ .new(user: User.system, contract_class: EmptyContract)
+ .call(project:, principal: group, roles: [group_role])
+ end
+ end
+
+ it "returns all mentionable principals including group and group members" do
+ expect(subject)
+ .to contain_exactly(user_with_restricted_comments_view_permissions,
+ user_with_restricted_comments_view_and_write_permissions,
+ group,
+ group_member1,
+ group_member2)
+ end
+ end
+ end
+
+ context "with a ! operator" do
+ let(:operator) { "!" }
+
+ it "returns all non-mentionable users on the work package and its project" do
+ expect(subject)
+ .to contain_exactly(user_without_restricted_comments_view_permissions)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/query/query_export_settings_spec.rb b/spec/models/query/query_export_settings_spec.rb
new file mode 100644
index 00000000000..01a20f10afc
--- /dev/null
+++ b/spec/models/query/query_export_settings_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+require "spec_helper"
+
+RSpec.describe "query export settings" do # rubocop:disable RSpec/DescribeClass
+ let(:query) { create(:query) }
+
+ describe "export_settings_for" do
+ it "creates a new export setting for the given format" do
+ setting = query.export_settings_for("csv")
+
+ expect(setting).to be_a_new_record
+ end
+
+ context "with an existing export setting" do
+ let!(:export_setting) { ExportSetting.create(query_id: query.id, settings: { some_key: :some_value }, format: "xls") }
+
+ it "is returned" do
+ setting = query.export_settings_for("xls")
+
+ expect(setting).to be_persisted
+ expect(setting.settings).to eq({ some_key: "some_value" })
+ end
+
+ it "is not returned for a different format" do
+ setting = query.export_settings_for("pdf_report")
+
+ expect(setting).to be_a_new_record
+ end
+ end
+ end
+end
diff --git a/spec/models/types/pattern_resolver_spec.rb b/spec/models/types/pattern_resolver_spec.rb
index a1bd36834e6..2a9d5dccfcb 100644
--- a/spec/models/types/pattern_resolver_spec.rb
+++ b/spec/models/types/pattern_resolver_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe Types::PatternResolver do
it "resolves the pattern" do
expect(subject.resolve(work_package))
- .to eq("#{work_package.id} | NA | #{work_package.created_at.to_date.iso8601}")
+ .to eq("#{work_package.id} | N/A | #{work_package.created_at.to_date.iso8601}")
end
end
diff --git a/spec/models/types/patterns/token_property_mapper_spec.rb b/spec/models/types/patterns/token_property_mapper_spec.rb
index 8a4c9b8b318..0001ad606c5 100644
--- a/spec/models/types/patterns/token_property_mapper_spec.rb
+++ b/spec/models/types/patterns/token_property_mapper_spec.rb
@@ -41,12 +41,13 @@ RSpec.describe Types::Patterns::TokenPropertyMapper do
shared_let(:work_package_parent) do
create(:work_package, project:, category:, start_date: Date.yesterday, estimated_hours: 120,
- due_date: 3.months.from_now, assigned_to: parent_assignee)
+ remaining_hours: 80, due_date: 3.months.from_now, assigned_to: parent_assignee)
end
shared_let(:work_package) do
create(:work_package, responsible:, project:, category:, due_date: 1.month.from_now, assigned_to: assignee,
- parent: work_package_parent, start_date: Time.zone.today, estimated_hours: 30)
+ parent: work_package_parent, start_date: Time.zone.today, estimated_hours: 30,
+ remaining_hours: 25)
end
shared_let(:string_custom_field) do
@@ -66,6 +67,12 @@ RSpec.describe Types::Patterns::TokenPropertyMapper do
end
end
+ shared_let(:not_activated_custom_field) do
+ create(:boolean_wp_custom_field).tap do |custom_field|
+ work_package.type.custom_fields << custom_field
+ end
+ end
+
described_class::TOKEN_PROPERTY_MAP.each_pair do |key, details|
it "the token named #{key} resolves successfully" do
expect { details[:fn].call(work_package) }.not_to raise_error
@@ -78,6 +85,12 @@ RSpec.describe Types::Patterns::TokenPropertyMapper do
expect(function.call(work_package)).to eq(%w[A B])
end
+ it "must return nil if custom field is not activated in project" do
+ function = described_class.new.fetch :"custom_field_#{not_activated_custom_field.id}"
+ expect { function.call(work_package) }.not_to raise_error
+ expect(function.call(work_package)).to be_nil
+ end
+
it "returns all possible tokens" do
cf = string_custom_field
tokens = described_class.new.tokens_for_type(work_package.type)
diff --git a/spec/models/work_package/work_package_acts_as_journalized_spec.rb b/spec/models/work_package/work_package_acts_as_journalized_spec.rb
index 9a211518c20..1ba39f407cc 100644
--- a/spec/models/work_package/work_package_acts_as_journalized_spec.rb
+++ b/spec/models/work_package/work_package_acts_as_journalized_spec.rb
@@ -903,10 +903,45 @@ RSpec.describe WorkPackage do
end
context "when comments_with_restricted_visibility is enabled", with_flag: { comments_with_restricted_visibility: true } do
- context "when the user cannot see restricted journals" do
+ context "and setting is enabled for the project" do
before do
+ work_package.project.enabled_comments_with_restricted_visibility = true
+ work_package.project.save!
+ end
+
+ context "when the user cannot see restricted journals" do
+ before do
+ mock_permissions_for(user) do |mock|
+ mock.allow_in_work_package :view_work_packages, work_package:
+ end
+ end
+
+ it "does not return the restricted journal" do
+ expect(journals.map(&:id)).not_to include(restricted_note.id)
+ expect(journals.map(&:id)).to include(unrestricted_note.id)
+ end
+ end
+
+ context "when the user can see restricted journals" do
+ before do
+ mock_permissions_for(user) do |mock|
+ mock.allow_in_project(:view_comments_with_restricted_visibility, project: work_package.project)
+ end
+ end
+
+ it "returns all journals" do
+ expect(journals.map(&:id)).to include(restricted_note.id, unrestricted_note.id)
+ end
+ end
+ end
+
+ context "and setting is disabled for the project" do
+ before do
+ work_package.project.enabled_comments_with_restricted_visibility = false
+ work_package.project.save!
+
mock_permissions_for(user) do |mock|
- mock.allow_in_work_package :view_work_packages, work_package:
+ mock.allow_in_project(:view_comments_with_restricted_visibility, project: work_package.project)
end
end
@@ -915,18 +950,6 @@ RSpec.describe WorkPackage do
expect(journals.map(&:id)).to include(unrestricted_note.id)
end
end
-
- context "when the user can see restricted journals" do
- before do
- mock_permissions_for(user) do |mock|
- mock.allow_in_project(:view_comments_with_restricted_visibility, project: work_package.project)
- end
- end
-
- it "returns all journals" do
- expect(journals.map(&:id)).to include(restricted_note.id, unrestricted_note.id)
- end
- end
end
context "when comments_with_restricted_visibility is disabled", with_flag: { comments_with_restricted_visibility: false } do
diff --git a/spec/models/work_packages/pdf_export/work_package_to_pdf_spec.rb b/spec/models/work_packages/pdf_export/work_package_to_pdf_spec.rb
index 723b141eedf..a5e2f5b4e1e 100644
--- a/spec/models/work_packages/pdf_export/work_package_to_pdf_spec.rb
+++ b/spec/models/work_packages/pdf_export/work_package_to_pdf_spec.rb
@@ -99,6 +99,7 @@ RSpec.describe WorkPackage::PDFExport::WorkPackageToPdf do
let(:image_path) { Rails.root.join("spec/fixtures/files/image.png") }
let(:priority) { create(:priority_normal) }
let(:image_attachment) { Attachment.new author: user, file: File.open(image_path) }
+ let(:image_attachment_elsewhere) { Attachment.new author: user, file: File.open(image_path) }
let(:attachments) { [image_attachment] }
let(:cf_long_text_description) { "**foo** *faa*" }
let(:cf_empty_long_text_description) { "" }
@@ -137,6 +138,7 @@ RSpec.describe WorkPackage::PDFExport::WorkPackageToPdf do
Foo
+
DESCRIPTION
end
let(:work_package) do
@@ -258,6 +260,11 @@ RSpec.describe WorkPackage::PDFExport::WorkPackageToPdf do
images: }
end
+ before do
+ image_attachment.save
+ image_attachment_elsewhere.save
+ end
+
describe "with a request for a PDF" do
describe "with rich text and images" do
it "contains correct data" do
@@ -274,17 +281,16 @@ RSpec.describe WorkPackage::PDFExport::WorkPackageToPdf do
].flatten.join(" ")
expect(result).to eq(expected_result)
expect(result).not_to include("DisabledCustomField")
- expect(pdf[:images].length).to eq(2)
+ expect(pdf[:images].length).to eq(3)
end
end
- describe "with a faulty image" do
+ describe "with faulty images" do
before do
# simulate a null pointer exception
# https://appsignal.com/openproject-gmbh/sites/62a6d833d2a5e482c1ef825d/exceptions/incidents/2326/samples/62a6d833d2a5e482c1ef825d-848752493603098719217252846401
# where attachment data is in the database but the file is missing, corrupted or not accessible
- allow(image_attachment).to receive(:file)
- .and_return(nil)
+ allow_any_instance_of(Attachment).to receive(:file).and_return(nil) # rubocop:disable RSpec/AnyInstance
end
it "still finishes the export" do
diff --git a/spec/permissions/edit_project_life_cycles_spec.rb b/spec/permissions/edit_project_life_cycles_spec.rb
index b120ae757eb..ff923074d63 100644
--- a/spec/permissions/edit_project_life_cycles_spec.rb
+++ b/spec/permissions/edit_project_life_cycles_spec.rb
@@ -34,11 +34,11 @@ RSpec.describe Overviews::OverviewsController, "edit_project_life_cycles permiss
include PermissionSpecs
# render dialog with inputs for editing project attributes with edit_project permission
- check_permission_required_for("overviews/overviews#project_life_cycles_dialog", :edit_project_phases)
+ check_permission_required_for("overviews/project_phases#edit", :edit_project_phases)
# render form with inputs for editing project attributes with edit_project permission
- check_permission_required_for("overviews/overviews#project_life_cycles_form", :edit_project_phases)
+ check_permission_required_for("overviews/project_phases#preview", :edit_project_phases)
# update project attributes with edit_project permission, deeper permission check via contract in place
- check_permission_required_for("overviews/overviews#update_project_life_cycles", :edit_project_phases)
+ check_permission_required_for("overviews/project_phases#update", :edit_project_phases)
end
diff --git a/spec/policies/work_package_policy_spec.rb b/spec/policies/work_package_policy_spec.rb
deleted file mode 100644
index abc4a8d4f05..00000000000
--- a/spec/policies/work_package_policy_spec.rb
+++ /dev/null
@@ -1,127 +0,0 @@
-#-- copyright
-# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License version 3.
-#
-# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-# Copyright (C) 2010-2013 the ChiliProject Team
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# See COPYRIGHT and LICENSE files for more details.
-#++
-
-require "spec_helper"
-
-RSpec.describe WorkPackagePolicy, type: :controller do
- let(:user) { build_stubbed(:user) }
- let(:project) { build_stubbed(:project) }
- let(:work_package) { build_stubbed(:work_package, project:) }
-
- describe "#allowed?" do
- let(:subject) { described_class.new(user) }
-
- context "for edit" do
- it "is false if the user has no permissions" do
- mock_permissions_for(user, &:forbid_everything)
- expect(subject).not_to be_allowed(work_package, :edit)
- end
-
- it "is true if the user has the edit_work_package permission" do
- mock_permissions_for(user) do |mock|
- mock.allow_in_project :edit_work_packages, project:
- end
-
- expect(subject).to be_allowed(work_package, :edit)
- end
-
- it "is true if the user has the edit_work_package permission on the work packge" do
- mock_permissions_for(user) do |mock|
- mock.allow_in_work_package :edit_work_packages, work_package:
- end
-
- expect(subject).to be_allowed(work_package, :edit)
- end
-
- it "is false if the user has only the add_work_package_notes permission" do
- mock_permissions_for(user) do |mock|
- mock.allow_in_project :add_work_package_notes, project:
- end
-
- expect(subject).not_to be_allowed(work_package, :edit)
- end
-
- it "is false if the user has the permissions but the work package is unpersisted" do
- mock_permissions_for(user) do |mock|
- mock.allow_in_project :edit_work_packages, :add_work_package_notes, project:
- end
-
- allow(work_package).to receive(:persisted?).and_return false
-
- expect(subject).not_to be_allowed(work_package, :edit)
- end
- end
-
- context "for manage_subtasks" do
- it "is true if the user has the manage_subtasks permission in the project" do
- mock_permissions_for(user) do |mock|
- mock.allow_in_project :manage_subtasks, project:
- end
-
- expect(subject).to be_allowed(work_package, :manage_subtasks)
- end
- end
-
- context "for comment" do
- it "is false if the user lacks permission" do
- expect(subject).not_to be_allowed(work_package, :comment)
- end
-
- it "is true if the user has the add_work_package_notes permission" do
- mock_permissions_for(user) do |mock|
- mock.allow_in_project :add_work_package_notes, project:
- end
- expect(subject).to be_allowed(work_package, :comment)
- end
-
- it "is true if the user has the add_work_package_notes permission on the work package" do
- mock_permissions_for(user) do |mock|
- mock.allow_in_work_package :add_work_package_notes, work_package:
- end
- expect(subject).to be_allowed(work_package, :comment)
- end
-
- it "is true if the user has the edit_work_packages permission" do
- mock_permissions_for(user) do |mock|
- mock.allow_in_project :edit_work_packages, project:
- end
-
- expect(subject).to be_allowed(work_package, :comment)
- end
-
- it "is false if the user has the edit_work_packages permission but the work_package is unpersisted" do
- mock_permissions_for(user) do |mock|
- mock.allow_in_project :edit_work_packages, project:
- end
- allow(work_package).to receive(:persisted?).and_return false
-
- expect(subject).not_to be_allowed(work_package, :comment)
- end
- end
- end
-end
diff --git a/spec/requests/admin/attachments/virus_scanning_settings_spec.rb b/spec/requests/admin/attachments/virus_scanning_settings_spec.rb
index 354bf3b68ed..c218c170a57 100644
--- a/spec/requests/admin/attachments/virus_scanning_settings_spec.rb
+++ b/spec/requests/admin/attachments/virus_scanning_settings_spec.rb
@@ -130,9 +130,10 @@ RSpec.describe "Attachments virus scanning",
end
describe "without ee" do
- it "redirects to upsale" do
+ it "renders upsale" do
get "/admin/settings/virus_scanning"
- expect(response.body).to have_text "Virus scanning is an Enterprise add-on", normalize_ws: true
+ expect(response.body).to have_text "Virus scanning"
+ expect(response.body).to have_text "Available only through the Corporate enterprise plan."
end
end
end
diff --git a/spec/requests/api/v3/activities_by_work_package_resource_spec.rb b/spec/requests/api/v3/activities_by_work_package_resource_spec.rb
index 3695d192104..d777e6f70c5 100644
--- a/spec/requests/api/v3/activities_by_work_package_resource_spec.rb
+++ b/spec/requests/api/v3/activities_by_work_package_resource_spec.rb
@@ -80,6 +80,11 @@ RSpec.describe API::V3::Activities::ActivitiesByWorkPackageAPI do # rubocop:disa
version: 2)
end
+ before do
+ project.enabled_comments_with_restricted_visibility = true
+ project.save!
+ end
+
context "and user has the permission to see it" do
it "includes restricted activities" do
get api_v3_paths.work_package_activities work_package.id
diff --git a/spec/routing/project_settings_routing_spec.rb b/spec/routing/project_settings_routing_spec.rb
index a2587c92c29..88880864c68 100644
--- a/spec/routing/project_settings_routing_spec.rb
+++ b/spec/routing/project_settings_routing_spec.rb
@@ -106,5 +106,19 @@ RSpec.describe Projects::SettingsController do
controller: "projects/settings/work_packages/types", action: "update", project_id: "123"
)
end
+
+ it do
+ expect(get("/projects/123/settings/work_packages/activities"))
+ .to route_to(
+ controller: "projects/settings/work_packages/activities", action: "show", project_id: "123"
+ )
+ end
+
+ it do
+ expect(patch("/projects/123/settings/work_packages/activities"))
+ .to route_to(
+ controller: "projects/settings/work_packages/activities", action: "update", project_id: "123"
+ )
+ end
end
end
diff --git a/spec/seeders/root_seeder_standard_edition_spec.rb b/spec/seeders/root_seeder_standard_edition_spec.rb
index efdf57f0bce..8eec7722960 100644
--- a/spec/seeders/root_seeder_standard_edition_spec.rb
+++ b/spec/seeders/root_seeder_standard_edition_spec.rb
@@ -92,16 +92,18 @@ RSpec.describe RootSeeder,
expect(RecurringMeeting.count).to eq 1
# The template is created.
- expect(StructuredMeeting.where(template: true).count).to eq 1
- expect(StructuredMeeting.where(template: true).first.duration).to eq 1.0
- expect(StructuredMeeting.where(template: true).first.agenda_items.count).to eq 9
- expect(StructuredMeeting.where(template: true).first.agenda_items.sum(:duration_in_minutes)).to eq 60
+ expect(Meeting.templated.count).to eq 1
+ template = Meeting.templated.first
+ expect(template.duration).to eq 1.0
+ expect(template.agenda_items.count).to eq 9
+ expect(template.agenda_items.sum(:duration_in_minutes)).to eq 60
# The first instance from that template is also created with the same data.
- expect(StructuredMeeting.where(template: false).count).to eq 1
- expect(StructuredMeeting.where(template: false).first.duration).to eq 1.0
- expect(StructuredMeeting.where(template: false).first.agenda_items.count).to eq 9
- expect(StructuredMeeting.where(template: false).first.agenda_items.sum(:duration_in_minutes)).to eq 60
+ expect(Meeting.where(template: false).count).to eq 1
+ instance = Meeting.not_templated.first
+ expect(instance.duration).to eq 1.0
+ expect(instance.agenda_items.count).to eq 9
+ expect(instance.agenda_items.sum(:duration_in_minutes)).to eq 60
end
it "creates different types of queries" do
diff --git a/spec/services/notifications/create_from_model_service_work_package_spec.rb b/spec/services/notifications/create_from_model_service_work_package_spec.rb
index 483ec35e607..ee3e6ddaac0 100644
--- a/spec/services/notifications/create_from_model_service_work_package_spec.rb
+++ b/spec/services/notifications/create_from_model_service_work_package_spec.rb
@@ -38,6 +38,7 @@ RSpec.describe Notifications::CreateFromModelService,
include_context "with CreateFromJournalJob context"
let(:permissions) { [:view_work_packages] }
+ let(:admin) { create(:admin) }
let(:author) { user_property == :author ? recipient : other_user }
let(:user_property) { nil }
let(:work_package) do
@@ -51,7 +52,7 @@ RSpec.describe Notifications::CreateFromModelService,
if %i[responsible assigned_to].include?(user_property)
create(:work_package,
- **wp_attributes.merge(user_property => recipient))
+ **wp_attributes, user_property => recipient)
elsif user_property == :watcher
create(:work_package,
**wp_attributes) do |wp|
@@ -100,6 +101,11 @@ RSpec.describe Notifications::CreateFromModelService,
work_package.save(validate: false)
work_package.journals.last
end
+ let(:journal_2_with_internal_comment) do
+ work_package.add_journal(user: admin, notes: "need to know basis", restricted: true)
+ work_package.save(validate: false)
+ work_package.journals.last
+ end
before do
# make sure no other calls are made due to WP creation/update
@@ -112,7 +118,7 @@ RSpec.describe Notifications::CreateFromModelService,
let(:user_property) { :assigned_to }
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false.merge(assignee: true))
+ build(:notification_setting, **notification_settings_all_false, assignee: true)
]
end
@@ -159,7 +165,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "when assignee has all in app notifications enabled but only assignee for mail" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false.merge(assignee: true))
+ build(:notification_setting, **notification_settings_all_false, assignee: true)
]
end
@@ -197,13 +203,37 @@ RSpec.describe Notifications::CreateFromModelService,
it_behaves_like "creates no notification"
end
+
+ context "when assignee does not have access to restricted journal" do
+ let(:author) { admin }
+ let(:journal) { journal_2_with_internal_comment }
+
+ it_behaves_like "creates no notification"
+ end
+
+ context "when assignee has access to restricted journal" do
+ let(:author) { admin }
+ let(:permissions) { %i[view_work_packages view_comments_with_restricted_visibility] }
+ let(:journal) { journal_2_with_internal_comment }
+
+ it_behaves_like "creates notification" do
+ let(:notification_channel_reasons) do
+ {
+ read_ian: false,
+ reason: :assigned,
+ mail_alert_sent: nil,
+ mail_reminder_sent: false
+ }
+ end
+ end
+ end
end
context "when user is responsible" do
let(:user_property) { :responsible }
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false.merge(responsible: true))
+ build(:notification_setting, **notification_settings_all_false, responsible: true)
]
end
@@ -250,6 +280,30 @@ RSpec.describe Notifications::CreateFromModelService,
it_behaves_like "creates no notification"
end
+
+ context "when responsible does not have access to restricted journal" do
+ let(:author) { admin }
+ let(:journal) { journal_2_with_internal_comment }
+
+ it_behaves_like "creates no notification"
+ end
+
+ context "when responsible has access to restricted journal" do
+ let(:author) { admin }
+ let(:permissions) { %i[view_work_packages view_comments_with_restricted_visibility] }
+ let(:journal) { journal_2_with_internal_comment }
+
+ it_behaves_like "creates notification" do
+ let(:notification_channel_reasons) do
+ {
+ read_ian: false,
+ reason: :responsible,
+ mail_alert_sent: nil,
+ mail_reminder_sent: false
+ }
+ end
+ end
+ end
end
context "when user is watcher" do
@@ -274,7 +328,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "when watcher has in app notifications disabled" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false.merge(watched: true))
+ build(:notification_setting, **notification_settings_all_false, watched: true)
]
end
@@ -316,6 +370,30 @@ RSpec.describe Notifications::CreateFromModelService,
it_behaves_like "creates no notification"
end
+
+ context "when watcher does not have access to restricted journal" do
+ let(:author) { admin }
+ let(:journal) { journal_2_with_internal_comment }
+
+ it_behaves_like "creates no notification"
+ end
+
+ context "when watcher has access to restricted journal" do
+ let(:author) { admin }
+ let(:permissions) { %i[view_work_packages view_comments_with_restricted_visibility] }
+ let(:journal) { journal_2_with_internal_comment }
+
+ it_behaves_like "creates notification" do
+ let(:notification_channel_reasons) do
+ {
+ read_ian: false,
+ reason: :watched,
+ mail_alert_sent: nil,
+ mail_reminder_sent: false
+ }
+ end
+ end
+ end
end
context "when user is notified about everything" do
@@ -414,13 +492,37 @@ RSpec.describe Notifications::CreateFromModelService,
it_behaves_like "creates no notification"
end
+
+ context "when recipient does not have access to restricted journal" do
+ let(:author) { admin }
+ let(:journal) { journal_2_with_internal_comment }
+
+ it_behaves_like "creates no notification"
+ end
+
+ context "when recipient has access to restricted journal" do
+ let(:author) { admin }
+ let(:permissions) { %i[view_work_packages view_comments_with_restricted_visibility] }
+ let(:journal) { journal_2_with_internal_comment }
+
+ it_behaves_like "creates notification" do
+ let(:notification_channel_reasons) do
+ {
+ read_ian: false,
+ reason: :commented,
+ mail_alert_sent: nil,
+ mail_reminder_sent: false
+ }
+ end
+ end
+ end
end
context "when the work package is shared with the user" do
let(:user_property) { :shared }
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false.merge(shared: true))
+ build(:notification_setting, **notification_settings_all_false, shared: true)
]
end
@@ -461,14 +563,37 @@ RSpec.describe Notifications::CreateFromModelService,
it_behaves_like "creates no notification"
end
+
+ context "when the shared user does not have access to restricted journal" do
+ let(:author) { admin }
+ let(:journal) { journal_2_with_internal_comment }
+
+ it_behaves_like "creates no notification"
+ end
+
+ context "when the shared user has access to restricted journal" do
+ let(:author) { admin }
+ let(:permissions) { %i[view_work_packages view_comments_with_restricted_visibility] }
+ let(:journal) { journal_2_with_internal_comment }
+
+ it_behaves_like "creates notification" do
+ let(:notification_channel_reasons) do
+ {
+ read_ian: false,
+ reason: :shared,
+ mail_alert_sent: nil,
+ mail_reminder_sent: false
+ }
+ end
+ end
+ end
end
context "when a work package is created" do
context "when the user configured to be notified on work package creation" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false
- .merge(work_package_created: true))
+ build(:notification_setting, **notification_settings_all_false, work_package_created: true)
]
end
@@ -487,8 +612,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "when the user configured to be notified on work package status changes" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false
- .merge(work_package_processed: true))
+ build(:notification_setting, **notification_settings_all_false, work_package_processed: true)
]
end
@@ -498,8 +622,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "when the user configured to be notified on work package priority changes" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false
- .merge(work_package_prioritized: true))
+ build(:notification_setting, **notification_settings_all_false, work_package_prioritized: true)
]
end
@@ -523,8 +646,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "when the user has commented notifications activated" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false
- .merge(work_package_commented: true))
+ build(:notification_setting, **notification_settings_all_false, work_package_commented: true)
]
end
@@ -549,6 +671,30 @@ RSpec.describe Notifications::CreateFromModelService,
it_behaves_like "creates no notification"
end
+
+ context "when the user does not have access to restricted journal" do
+ let(:author) { admin }
+ let(:journal) { journal_2_with_internal_comment }
+
+ it_behaves_like "creates no notification"
+ end
+
+ context "when the user has access to restricted journal" do
+ let(:author) { admin }
+ let(:permissions) { %i[view_work_packages view_comments_with_restricted_visibility] }
+ let(:journal) { journal_2_with_internal_comment }
+
+ it_behaves_like "creates notification" do
+ let(:notification_channel_reasons) do
+ {
+ read_ian: false,
+ reason: :commented,
+ mail_alert_sent: nil,
+ mail_reminder_sent: false
+ }
+ end
+ end
+ end
end
context "when the journal has no note" do
@@ -557,8 +703,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "with the user having commented notifications activated" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false
- .merge(work_package_commented: true))
+ build(:notification_setting, **notification_settings_all_false, work_package_commented: true)
]
end
@@ -572,8 +717,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "when the user has processed notifications activated" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false
- .merge(work_package_processed: true))
+ build(:notification_setting, **notification_settings_all_false, work_package_processed: true)
]
end
@@ -606,8 +750,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "with the user having processed notifications activated" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false
- .merge(work_package_processed: true))
+ build(:notification_setting, **notification_settings_all_false, work_package_processed: true)
]
end
@@ -621,8 +764,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "when the user has prioritized notifications activated" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false
- .merge(work_package_prioritized: true))
+ build(:notification_setting, **notification_settings_all_false, work_package_prioritized: true)
]
end
@@ -655,8 +797,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "with the user having prioritized notifications activated" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false
- .merge(work_package_prioritized: true))
+ build(:notification_setting, **notification_settings_all_false, work_package_prioritized: true)
]
end
@@ -670,8 +811,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "when the user has scheduled notifications activated" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false
- .merge(work_package_scheduled: true))
+ build(:notification_setting, **notification_settings_all_false, work_package_scheduled: true)
]
end
@@ -704,8 +844,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "with the user having scheduled notifications activated" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false
- .merge(work_package_scheduled: true))
+ build(:notification_setting, **notification_settings_all_false, work_package_scheduled: true)
]
end
@@ -719,8 +858,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "when the user has scheduled notifications activated" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false
- .merge(work_package_scheduled: true))
+ build(:notification_setting, **notification_settings_all_false, work_package_scheduled: true)
]
end
@@ -772,7 +910,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "when user is mentioned" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false.merge(mentioned: true))
+ build(:notification_setting, **notification_settings_all_false, mentioned: true)
]
end
@@ -794,7 +932,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "when the user disabled mention notifications" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false.merge(mentioned: false))
+ build(:notification_setting, **notification_settings_all_false, mentioned: false)
]
end
@@ -965,7 +1103,7 @@ RSpec.describe Notifications::CreateFromModelService,
context "when the recipient turned off mention notifications" do
let(:recipient_notification_settings) do
[
- build(:notification_setting, **notification_settings_all_false.merge(mentioned: false))
+ build(:notification_setting, **notification_settings_all_false, mentioned: false)
]
end
diff --git a/spec/services/principals/replace_references_service_call_integration_spec.rb b/spec/services/principals/replace_references_service_call_integration_spec.rb
index 5daffad06e5..612e667b35b 100644
--- a/spec/services/principals/replace_references_service_call_integration_spec.rb
+++ b/spec/services/principals/replace_references_service_call_integration_spec.rb
@@ -195,28 +195,6 @@ RSpec.describe Principals::ReplaceReferencesService, "#call", type: :model do
end
end
- context "with MeetingContent" do
- it_behaves_like "rewritten record",
- :meeting_agenda,
- :author_id do
- let(:attributes) do
- { type: "'MeetingAgenda'" }
- end
- end
-
- it_behaves_like "rewritten record",
- :meeting_minutes,
- :author_id do
- let(:attributes) do
- { type: "'MeetingMinutes'" }
- end
- end
-
- it_behaves_like "rewritten record",
- :journal_meeting_content_journal,
- :author_id
- end
-
context "with MeetingParticipant" do
it_behaves_like "rewritten record",
:meeting_participant,
diff --git a/spec/services/users/register_user_service_spec.rb b/spec/services/users/register_user_service_spec.rb
index 9a8cfa2f145..24ef0a7d179 100644
--- a/spec/services/users/register_user_service_spec.rb
+++ b/spec/services/users/register_user_service_spec.rb
@@ -27,7 +27,7 @@
require "spec_helper"
-RSpec.describe Users::RegisterUserService do
+RSpec.describe Users::RegisterUserService, with_ee: %i[sso_auth_providers] do
let(:user) { build(:user) }
let(:instance) { described_class.new(user) }
let(:call) { instance.call }
@@ -83,9 +83,6 @@ RSpec.describe Users::RegisterUserService do
before do
allow(user).to receive(:activate)
allow(user).to receive(:save).and_return true
-
- # required so that the azure provider is visible (ee feature)
- allow(EnterpriseToken).to receive(:show_banners?).and_return false
end
it "tries to activate that user regardless of settings" do
diff --git a/spec/services/users/replace_mentions_service_integration_spec.rb b/spec/services/users/replace_mentions_service_integration_spec.rb
index 6256fb25a41..83a5c2e538a 100644
--- a/spec/services/users/replace_mentions_service_integration_spec.rb
+++ b/spec/services/users/replace_mentions_service_integration_spec.rb
@@ -404,14 +404,6 @@ RSpec.describe Users::ReplaceMentionsService, "integration" do
it_behaves_like "rewritten mention", :document, :description
end
- context "for meeting_contents text" do
- it_behaves_like "rewritten mention", :meeting_agenda, :text
- end
-
- context "for meeting_content_journals text" do
- it_behaves_like "rewritten mention", :journal_meeting_content_journal, :text
- end
-
context "for messages content" do
it_behaves_like "rewritten mention", :message, :content
end
diff --git a/spec/services/work_packages/activities_tab/restricted_mentions_sanitizer_spec.rb b/spec/services/work_packages/activities_tab/restricted_mentions_sanitizer_spec.rb
new file mode 100644
index 00000000000..cbf6473d41d
--- /dev/null
+++ b/spec/services/work_packages/activities_tab/restricted_mentions_sanitizer_spec.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+require "spec_helper"
+
+RSpec.describe WorkPackages::ActivitiesTab::RestrictedMentionsSanitizer do
+ include RestrictedVisibilityCommentsHelpers
+
+ shared_let(:project) { create(:project) }
+ shared_let(:admin_but_non_member) { create(:admin) }
+ shared_let(:viewer) { create_user_without_restricted_comments_view_permissions }
+ shared_let(:user_with_restricted_comments_view_and_write_permissions) do
+ create_user_with_restricted_comments_view_and_write_permissions
+ end
+ shared_let(:project_admin) { create_user_as_project_admin }
+ shared_let(:work_package) { create(:work_package, project:) }
+
+ let(:input) do
+ <<~HTML
+ @#{admin_but_non_member.firstname} wrote:
+
+ > Well Done!
+
+ @#{viewer.firstname} wrote:
+
+ > @#{user_with_restricted_comments_view_and_write_permissions.firstname} wrote:
+ >
+ > > @#{project_admin.firstname} wrote:
+ > >
+ > > > FooBar
+ > >
+ > > @Firstname wrote:
+ > >
+ > > > BooBar
+ HTML
+ end
+
+ let(:expected_output) do
+ <<~HTML
+ @#{admin_but_non_member.firstname} wrote:
+
+ > Well Done!
+
+ @#{viewer.firstname} wrote:
+
+ > @#{user_with_restricted_comments_view_and_write_permissions.firstname} wrote:
+ >
+ > > @#{project_admin.firstname} wrote:
+ > >
+ > > > FooBar
+ > >
+ > > @Firstname wrote:
+ > >
+ > > > BooBar
+ HTML
+ end
+
+ subject { described_class.new(work_package, input).call }
+
+ before { allow(User).to receive(:current).and_return(user_with_restricted_comments_view_and_write_permissions) }
+
+ it "sanitizes the notes" do
+ expect(subject).to eq(expected_output)
+ end
+
+ context "when the notes are empty" do
+ let(:input) { "" }
+
+ it "returns an empty string" do
+ expect(subject).to eq("")
+ end
+ end
+end
diff --git a/spec/support/components/admin/type_configuration_form.rb b/spec/support/components/admin/type_configuration_form.rb
index 15ac948bb4e..0ccceec8fe6 100644
--- a/spec/support/components/admin/type_configuration_form.rb
+++ b/spec/support/components/admin/type_configuration_form.rb
@@ -166,11 +166,16 @@ module Components
end
def edit_query_group(name)
- SeleniumHubWaiter.wait unless using_cuprite?
+ if using_cuprite?
+ wait_for_reload
+ else
+ SeleniumHubWaiter.wait
+ end
group = find_group(name)
group.find(".type-form-query-group--edit-button").click
- wait_for_reload if using_cuprite?
+ # Wait for the modal to appear.
+ expect(page).to have_css(".wp-table--configuration-modal")
end
def add_attribute_group(name, expect: true)
diff --git a/spec/support/components/common/filters.rb b/spec/support/components/common/filters.rb
index dd48c47b44b..f218f3585fa 100644
--- a/spec/support/components/common/filters.rb
+++ b/spec/support/components/common/filters.rb
@@ -39,11 +39,16 @@ module Components
expect(page).to have_css(".op-filters-form", visible: :hidden)
end
- def expect_filter_set(filter_name)
+ def expect_filter_set(filter_name, value: nil)
if filter_name == "name_and_identifier"
expect(page.find_by_id(filter_name).value).not_to be_empty
+ elsif value
+ within("li[data-filter-name='#{filter_name}']:not(.hidden)", visible: :hidden) do
+ expect(page).to have_css(".advanced-filters--filter-value", text: value, visible: :all)
+ end
else
- expect(page).to have_css("li[data-filter-name='#{filter_name}']:not(.hidden)", visible: :hidden)
+ expect(page)
+ .to have_css("li[data-filter-name='#{filter_name}']:not(.hidden)", visible: :hidden)
end
end
@@ -159,7 +164,7 @@ module Components
end
when "between"
if send_keys
- find_field("from_value").send_keysvalues.first
+ find_field("from_value").send_keys values.first
find_field("to_value").send_keys values.second
else
fill_in "from_value", with: values.first
@@ -217,6 +222,10 @@ module Components
filter[:"data-filter-type"] == "date"
end
+ def date_time_filter?(filter)
+ filter[:"data-filter-type"] == "datetime_past"
+ end
+
def boolean_filter?(_filter)
false
end
diff --git a/spec/support/components/projects/project_life_cycles/edit_dialog.rb b/spec/support/components/projects/project_life_cycles/edit_dialog.rb
index 17db68ba13b..3fd8484254e 100644
--- a/spec/support/components/projects/project_life_cycles/edit_dialog.rb
+++ b/spec/support/components/projects/project_life_cycles/edit_dialog.rb
@@ -49,17 +49,18 @@ module Components
close if close_after_yield
end
- def clear_date_for(step)
- find("input[id^='project_available_phases_attributes_#{step.position - 1}']").set ""
+ def clear_date
+ find("input[id^='project_phase_date_range']").set ""
+ find_by_id("edit-project-life-cycles-dialog-title").click
end
- def set_date_for(step, values:)
- dialog_selector = "##{::ProjectLifeCycles::Sections::EditDialogComponent::DIALOG_ID}"
+ def set_date_for(values:)
+ dialog_selector = "##{Overviews::ProjectPhases::EditDialogComponent::DIALOG_ID}"
datepicker = Components::RangeDatepicker.new(dialog_selector)
datepicker.open(
- "input[id^='project_available_phases_attributes_#{step.position - 1}']"
+ "input[id^='project_phase_date_range']"
)
values.each do |date|
@@ -98,12 +99,12 @@ module Components
expect(page).to have_css(async_content_container_css_selector)
end
- def expect_input(label, value:, position:)
+ def expect_input(label, value:)
within_async_content do
expect(page).to have_field(
label,
with: value,
- name: "project[available_phases_attributes][#{position - 1}][date_range]"
+ name: "project_phase[date_range]"
)
end
end
@@ -111,32 +112,32 @@ module Components
def expect_input_for(step)
value = "#{step.start_date.strftime('%Y-%m-%d')} - #{step.finish_date.strftime('%Y-%m-%d')}"
- expect_input(step.name, value:, position: step.position)
+ expect_input(step.name, value:)
end
- def expect_caption(step, text: nil, present: true)
+ def expect_caption(text: nil, present: true)
selector = 'span[id^="caption"]'
- expect_selector_for(step, selector:, text:, present:)
+ expect_selector_for(selector:, text:, present:)
end
- def expect_no_caption(step)
- expect_caption(step, present: false)
+ def expect_no_caption
+ expect_caption(present: false)
end
- def expect_validation_message(step, text: nil, present: true)
+ def expect_validation_message(text: nil, present: true)
selector = 'div[id^="validation"]'
- expect_selector_for(step, selector:, text:, present:)
+ expect_selector_for(selector:, text:, present:)
end
- def expect_no_validation_message(step)
- expect_validation_message(step, present: false)
+ def expect_no_validation_message
+ expect_validation_message(present: false)
end
private
- def expect_selector_for(step, selector:, text: nil, present: true)
+ def expect_selector_for(selector:, text: nil, present: true)
within_async_content do
- input_id = "#project_available_phases_attributes_#{step.position - 1}_date_range"
+ input_id = "#project_phase_date_range"
parent = find(input_id).ancestor("primer-datepicker-field")
if present
diff --git a/spec/support/components/work_packages/activities.rb b/spec/support/components/work_packages/activities.rb
index 57f49354219..7c050740762 100644
--- a/spec/support/components/work_packages/activities.rb
+++ b/spec/support/components/work_packages/activities.rb
@@ -183,9 +183,14 @@ module Components
page.find_test_selector("op-open-work-package-journal-form-trigger").click
end
+ def refocus_editor
+ ckeditor.refocus
+ expect_focus_on_editor
+ end
+
def expect_focus_on_editor
page.within_test_selector("op-work-package-journal-form-element") do
- expect(page).to have_css(".ck-content:focus")
+ expect(page).to have_css(".ck-content:focus", wait: 10)
end
end
@@ -201,7 +206,11 @@ module Components
end
def type_comment(text)
- open_new_comment_editor if page.find_test_selector("op-open-work-package-journal-form-trigger")
+ begin
+ open_new_comment_editor if page.find_test_selector("op-open-work-package-journal-form-trigger")
+ rescue Capybara::ElementNotFound
+ # If the editor is already open, we don't need to open it again
+ end
# Wait for the editor form to be present and ready
wait_for { page }.to have_test_selector("op-work-package-journal-form-element")
@@ -242,10 +251,7 @@ module Components
page.within_test_selector("op-work-package-journal-form-element") do
get_editor_form_field_element.set_value(text)
- if restricted
- expect(page).to have_test_selector("op-work-package-journal-restricted-comment-checkbox")
- page.check("Restrict visibility")
- end
+ check_restricted_visibility_comment_checkbox if restricted
page.find_test_selector("op-submit-work-package-journal-form").click if save
end
@@ -300,6 +306,11 @@ module Components
expect(page).to have_test_selector("op-work-package-journal-form-element")
end
+ def check_restricted_visibility_comment_checkbox
+ expect(page).to have_test_selector("op-work-package-journal-restricted-comment-checkbox")
+ page.check("Restrict visibility")
+ end
+
def dismiss_comment_editor_with_esc
page.find_test_selector("op-work-package-journal-form-element").send_keys(:escape)
end
diff --git a/spec/support/components/work_packages/relations.rb b/spec/support/components/work_packages/relations.rb
index 03f305c70f1..0c6cbc1e215 100644
--- a/spec/support/components/work_packages/relations.rb
+++ b/spec/support/components/work_packages/relations.rb
@@ -56,11 +56,21 @@ module Components
end
end
+ def expect_tab_is_loaded
+ # Search the current window in order to avoid within scope restrictions
+ within_window(page.current_window) do
+ within("wp-relations-tab") do
+ expect(page).to have_no_css("op-content-loader")
+ end
+ end
+ end
+
def expect_add_relation_button
expect(page).to have_test_selector("new-relation-action-menu")
end
def expect_no_add_relation_button
+ expect_tab_is_loaded # Make sure the tab is loaded before checking non existing elements
expect(page).not_to have_test_selector("new-relation-action-menu")
end
@@ -83,6 +93,7 @@ module Components
end
def expect_no_row(relatable)
+ expect_tab_is_loaded # Make sure the tab is loaded before checking non existing elements
actual_relatable = find_relatable(relatable)
expect(page).not_to have_test_selector("op-relation-row-visible-#{actual_relatable.id}"),
"expected no relation row for work package " \
@@ -282,6 +293,10 @@ module Components
expect_no_row(relatable)
end
+ def expect_no_relations
+ expect(page).to have_test_selector("no-relations-blankslate", text: "This work package does not have any relations yet.")
+ end
+
def add_parent(query, work_package)
# Open the parent edit
SeleniumHubWaiter.wait
@@ -303,6 +318,7 @@ module Components
end
def expect_no_parent
+ expect_tab_is_loaded # Make sure the tab is loaded before checking non existing elements
expect(page).not_to have_test_selector "op-wp-breadcrumb-parent", wait: 10
end
diff --git a/spec/support/finders/test_selector.rb b/spec/support/finders/test_selector_finders.rb
similarity index 89%
rename from spec/support/finders/test_selector.rb
rename to spec/support/finders/test_selector_finders.rb
index 775ce19151c..8aed726042e 100644
--- a/spec/support/finders/test_selector.rb
+++ b/spec/support/finders/test_selector_finders.rb
@@ -33,11 +33,13 @@ module TestSelectorFinders
end
def find_test_selector(value, **)
- find(:test_id, value, **)
+ target = respond_to?(:page) ? page : self
+ target.find(:test_id, value, **)
end
def within_test_selector(value, **, &)
- within(:test_id, value, **, &)
+ target = respond_to?(:page) ? page : self
+ target.within(:test_id, value, **, &)
end
# expect(page).to have_test_selector('foo')
@@ -57,4 +59,5 @@ RSpec.configure do |config|
Capybara::Session.include(TestSelectorFinders)
Capybara::DSL.extend(TestSelectorFinders)
config.include TestSelectorFinders, type: :feature
+ config.include TestSelectorFinders, type: :component
end
diff --git a/spec/support/matchers/has_enterprise_banner.rb b/spec/support/matchers/has_enterprise_banner.rb
index c2bcb78ed2c..fc0ef4fc8f2 100644
--- a/spec/support/matchers/has_enterprise_banner.rb
+++ b/spec/support/matchers/has_enterprise_banner.rb
@@ -32,7 +32,12 @@ RSpec::Matchers.define :have_enterprise_banner do |**args|
include TestSelectorFinders
match do |page|
- page.has_selector?(test_selector("op-enterprise-banner"), **args)
+ if args[:plan]
+ expected_text = I18n.t("ee.upsale.plan_text_html", plan: args[:plan].capitalize)
+ page.find(test_selector("op-enterprise-banner"), **args, text: expected_text)
+ else
+ page.find(test_selector("op-enterprise-banner"), **args)
+ end
end
match_when_negated do |page|
@@ -40,10 +45,16 @@ RSpec::Matchers.define :have_enterprise_banner do |**args|
end
failure_message do
- "expected page to have Enterprise edition banner"
+ <<~MESSAGE
+ Expected page to have Enterprise edition banner, but it does not:
+ #{@error}
+ MESSAGE
end
failure_message_when_negated do
- "expected page not to have Enterprise edition banner"
+ <<~MESSAGE
+ Expected page not to have Enterprise edition banner, but it does:
+ #{@error}
+ MESSAGE
end
end
diff --git a/modules/meeting/spec/models/meeting_minutes_spec.rb b/spec/support/matchers/has_enterprise_upsale_page.rb
similarity index 61%
rename from modules/meeting/spec/models/meeting_minutes_spec.rb
rename to spec/support/matchers/has_enterprise_upsale_page.rb
index 500657d1bf8..7d27f2bc601 100644
--- a/modules/meeting/spec/models/meeting_minutes_spec.rb
+++ b/spec/support/matchers/has_enterprise_upsale_page.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -27,40 +28,33 @@
# See COPYRIGHT and LICENSE files for more details.
#++
-require_relative "../spec_helper"
+RSpec::Matchers.define :have_enterprise_upsale_page do |**args|
+ include TestSelectorFinders
-RSpec.describe "MeetingMinutes" do
- before do
- @min = build(:meeting_minutes)
+ match do |page|
+ if args[:plan]
+ expected_text = I18n.t("ee.upsale.plan_text_html", plan: args[:plan].capitalize)
+ page.find(test_selector("op-enterprise-upsale-page"), **args, text: expected_text)
+ else
+ page.find(test_selector("op-enterprise-upsale-page"), **args)
+ end
end
- # meeting minutes are editable when the meeting agenda is locked
- describe "#editable?" do
- before do
- @mee = build(:meeting)
- @min.meeting = @mee
- end
+ match_when_negated do |page|
+ page.has_no_selector?(test_selector("op-enterprise-upsale-page"), **args)
+ end
- describe "with no agenda present" do
- it "is not editable" do
- expect(@min.editable?).to be_falsey
- end
- end
+ failure_message do
+ <<~MESSAGE
+ Expected page to have Enterprise edition upsale page, but it does not:
+ #{@error}
+ MESSAGE
+ end
- describe "with an agenda present" do
- before do
- @a = build(:meeting_agenda)
- @mee.agenda = @a
- end
-
- it "is not editable when the agenda is open" do
- expect(@min.editable?).to be_falsey
- end
-
- it "is editable when the agenda is closed" do
- @a.lock!
- expect(@min.editable?).to be_truthy
- end
- end
+ failure_message_when_negated do
+ <<~MESSAGE
+ Expected page not to have Enterprise edition upsale page, but it does:
+ #{@error}
+ MESSAGE
end
end
diff --git a/spec/support/pages/projects/index.rb b/spec/support/pages/projects/index.rb
index 6408756d4a2..010116f6945 100644
--- a/spec/support/pages/projects/index.rb
+++ b/spec/support/pages/projects/index.rb
@@ -158,26 +158,6 @@ module Pages
end
end
- def expect_filters_container_toggled
- expect(page).to have_css(".op-filters-form")
- end
-
- def expect_filters_container_hidden
- expect(page).to have_css(".op-filters-form", visible: :hidden)
- end
-
- def expect_filter_set(filter_name)
- if filter_name == "name_and_identifier"
- expect(page.find_by_id(filter_name).value).not_to be_empty
- else
- expect(page).to have_css("li[data-filter-name='#{filter_name}']:not(.hidden)", visible: :hidden)
- end
- end
-
- def expect_filter_count(count)
- expect(page).to have_css('[data-test-selector="filters-button-counter"]', text: count)
- end
-
def expect_filter_available(filter_name)
expect(page).to have_select("add_filter_select", with_options: [filter_name])
end
@@ -256,25 +236,12 @@ module Pages
wait_for_reload
end
- def set_filter(name, human_name, human_operator = nil, values = [], send_keys: false)
- if name == "name_and_identifier"
- set_simple_filter(name, values, send_keys:)
- else
- set_advanced_filter(name, human_name, human_operator, values, send_keys:)
- end
- end
-
- def set_simple_filter(_name, values, send_keys: false)
- return unless values.any?
-
- set_name_and_identifier_filter(values, send_keys:) # This is the only one simple filter at the moment.
- end
-
def set_advanced_filter(name, human_name, human_operator = nil, values = [], send_keys: false)
selected_filter = select_filter(name, human_name)
- apply_operator(name, human_operator)
within(selected_filter) do
+ apply_operator(name, human_operator)
+
return unless values.any?
if boolean_filter?(name)
@@ -285,126 +252,11 @@ module Pages
elsif date_filter?(selected_filter) || date_time_filter?(selected_filter)
select(human_operator, from: "operator")
wait_for_network_idle
- set_date_filter(human_operator, values, send_keys:)
+ set_created_at_filter(human_operator, values, send_keys:)
end
end
end
- def expect_autocomplete_options_for(custom_field, options, grouping: nil, results_selector: "body")
- selected_filter = select_filter(custom_field.column_name, custom_field.name)
-
- within(selected_filter) do
- find('[data-filter-autocomplete="true"]').click
- end
-
- Array(options).each do |option|
- expect_ng_option(selected_filter, option, grouping:, results_selector:)
- end
- end
-
- def expect_user_autocomplete_options_for(custom_field, expected_options)
- selected_filter = select_filter(custom_field.column_name, custom_field.name)
-
- within(selected_filter) do
- find('[data-filter-autocomplete="true"]').click
- end
- options = visible_user_auto_completer_options
-
- expect(options).to eq(expected_options)
- end
-
- def apply_operator(name, human_operator)
- select(human_operator, from: "operator") unless boolean_filter?(name)
- end
-
- def select_filter(name, human_name)
- select human_name, from: "add_filter_select"
- page.find("li[data-filter-name='#{name}']")
- end
-
- def remove_filter(name)
- if name == "name_and_identifier"
- page.find_by_id("name_and_identifier").find(:xpath, "following-sibling::button").click
- else
- page.find("li[data-filter-name='#{name}'] .filter_rem").click
- end
- end
-
- def set_toggle_filter(values)
- should_active = values.first == "yes"
- is_active = page.has_selector? '[data-test-selector="spot-switch-handle"][data-qa-active]'
-
- if should_active != is_active
- page.find('[data-test-selector="spot-switch-handle"]').click
- end
-
- if should_active
- expect(page).to have_css('[data-test-selector="spot-switch-handle"][data-qa-active]')
- else
- expect(page).to have_css('[data-test-selector="spot-switch-handle"]:not([data-qa-active])')
- end
- end
-
- def set_name_and_identifier_filter(values, send_keys: false)
- if send_keys
- find_field("name_and_identifier").send_keys values.first
- else
- fill_in "name_and_identifier", with: values.first
- end
- end
-
- def set_date_filter(human_operator, values, send_keys: false)
- case human_operator
- when "on", "less than days ago", "more than days ago", "days ago"
- if send_keys
- find_field("value").send_keys values.first
- else
- fill_in "value", with: values.first
- end
- when "between"
- if send_keys
- find_field("from_value").send_keys values.first
- find_field("to_value").send_keys values.second
- else
- fill_in "from_value", with: values.first
- fill_in "to_value", with: values.second
- end
- end
- end
-
- def set_autocomplete_filter(values, clear: true)
- element = find('[data-filter-autocomplete="true"]')
-
- ng_select_clear(element, raise_on_missing: false) if clear
-
- Array(values).each do |query|
- select_autocomplete element,
- query:,
- results_selector: "body"
- end
- end
-
- def set_list_filter(values)
- value_select = find('.single-select select[name="value"]')
- value_select.select values.first
- end
-
- def open_filters
- retry_block do
- toggle_filters_section
- expect(page).to have_css(".op-filters-form.-expanded")
- page.find_field("Add filter", visible: true)
- end
- end
-
- def filters_toggle
- page.find('[data-test-selector="filter-component-toggle"]')
- end
-
- def toggle_filters_section
- filters_toggle.click
- end
-
def set_columns(*columns)
open_configure_view
@@ -649,7 +501,7 @@ module Pages
end
def project_in_first_row(column_text_separator: "\n")
- first_row = within("#projects-table") { find(".op-project-row-component", match: :first) }
+ first_row = within("#projects-table") { first(".op-project-row-component") }
Project.find_by!(name: first_row.text.split(column_text_separator).first)
end
@@ -671,18 +523,6 @@ module Pages
%w[active member_of favored public templated].include?(filter.to_s)
end
- def autocomplete_filter?(filter)
- filter.has_css?('[data-filter-autocomplete="true"]', wait: 0)
- end
-
- def date_filter?(filter)
- filter[:"data-filter-type"] == "date"
- end
-
- def date_time_filter?(filter)
- filter[:"data-filter-type"] == "datetime_past"
- end
-
def submenu
Components::Submenu.new
end
diff --git a/modules/meeting/app/services/meeting_contents/update_service.rb b/spec/support/pages/projects/settings/activities.rb
similarity index 74%
rename from modules/meeting/app/services/meeting_contents/update_service.rb
rename to spec/support/pages/projects/settings/activities.rb
index 0436452db9f..b8c70b9d1cc 100644
--- a/modules/meeting/app/services/meeting_contents/update_service.rb
+++ b/spec/support/pages/projects/settings/activities.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
-#-- copyright
+
+# -- copyright
# OpenProject is an open source project management software.
-# Copyright (C) the OpenProject GmbH
+# Copyright (C) 2010-2024 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.
@@ -25,22 +26,26 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
-#++
+# ++
-module MeetingContents
- class UpdateService < ::BaseServices::Update
- include Attachments::ReplaceAttachments
+require "support/pages/page"
- def persist(call)
- content = call.result
+module Pages
+ module Projects
+ module Settings
+ class Activities < Pages::Page
+ attr_accessor :project
- if content.lock_version_changed?
- call.errors.add(:base, I18n.t(:notice_locking_conflict))
- call.success = false
- return call
+ def initialize(project)
+ super()
+
+ @project = project
+ end
+
+ def path
+ "/projects/#{project.identifier}/settings/work_packages/activities"
+ end
end
-
- super
end
end
end
diff --git a/spec/support/pages/projects/show.rb b/spec/support/pages/projects/show.rb
index 708d1525b9c..1d52305260f 100644
--- a/spec/support/pages/projects/show.rb
+++ b/spec/support/pages/projects/show.rb
@@ -85,9 +85,9 @@ module Pages
wait_for_size_animation_completion("[data-test-selector='async-dialog-content']")
end
- def open_edit_dialog_for_life_cycles
+ def open_edit_dialog_for_life_cycle(life_cycle)
within_life_cycles_sidebar do
- page.find("[data-test-selector='project-life-cycles-edit-button']").click
+ page.find("[data-test-selector='project-life-cycle-edit-button-#{life_cycle.id}']").click
end
Components::Projects::ProjectLifeCycles::EditDialog.new.tap(&:expect_open)
diff --git a/spec/support/shared/with_ee.rb b/spec/support/shared/with_ee.rb
index d754d85ea75..4c4f900e454 100644
--- a/spec/support/shared/with_ee.rb
+++ b/spec/support/shared/with_ee.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -48,13 +50,22 @@ RSpec.configure do |config|
if allowed.present?
allowed = aggregate_parent_array(example, allowed.to_set)
+ token_double = instance_double(EnterpriseToken)
+ token_object_double = instance_double(OpenProject::Token)
allow(EnterpriseToken).to receive(:allows_to?).and_call_original
+ allow(token_object_double).to receive(:has_feature?).and_return(false)
allowed.each do |enterprise_feature|
allow(EnterpriseToken).to receive(:allows_to?).with(enterprise_feature).and_return(true)
+ allow(token_object_double).to receive(:has_feature?).with(enterprise_feature).and_return(true)
end
- # Also disable banners to signal the frontend we're on EE
- allow(EnterpriseToken).to receive(:show_banners?).and_return(allowed.empty?)
+ # Also signal available features
+ allow(EnterpriseToken).to receive(:current).and_return(token_double)
+ allow(token_double)
+ .to receive_messages(token_object: token_object_double,
+ available_features: allowed.to_a,
+ expired?: false,
+ restrictions: {})
end
end
end
diff --git a/spec/support/work_packages/activities_tab/restricted_visibility_comments_helpers.rb b/spec/support/work_packages/activities_tab/restricted_visibility_comments_helpers.rb
new file mode 100644
index 00000000000..c5b51e39af3
--- /dev/null
+++ b/spec/support/work_packages/activities_tab/restricted_visibility_comments_helpers.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+require "spec_helper"
+
+module RestrictedVisibilityCommentsHelpers
+ def create_user_without_restricted_comments_view_permissions
+ viewer_role = create(:project_role, permissions: %i[view_work_packages])
+ create(:user,
+ firstname: "A",
+ lastname: "Viewer",
+ member_with_roles: { project => viewer_role })
+ end
+
+ def create_user_as_project_admin
+ member_role = create(:project_role,
+ permissions: %i[view_work_packages add_work_package_notes
+ edit_own_work_package_notes
+ view_comments_with_restricted_visibility
+ add_comments_with_restricted_visibility
+ edit_own_comments_with_restricted_visibility
+ edit_others_comments_with_restricted_visibility])
+ create(:user, firstname: "Project", lastname: "Admin",
+ member_with_roles: { project => member_role })
+ end
+
+ def create_user_with_restricted_comments_view_permissions
+ viewer_role = create(:project_role, permissions: %i[view_work_packages view_comments_with_restricted_visibility])
+ create(:user,
+ firstname: "Restricted",
+ lastname: "Viewer",
+ member_with_roles: { project => viewer_role })
+ end
+
+ def create_user_with_restricted_comments_view_and_write_permissions
+ viewer_role_with_commenting_permission = create(:project_role,
+ permissions: %i[view_work_packages add_work_package_notes
+ edit_own_work_package_notes
+ view_comments_with_restricted_visibility
+ add_comments_with_restricted_visibility
+ edit_own_comments_with_restricted_visibility])
+ create(:user,
+ firstname: "Restricted",
+ lastname: "ViewerCommenter",
+ member_with_roles: { project => viewer_role_with_commenting_permission })
+ end
+end