enterprise banner with fallback on sprint sharing settings

This commit is contained in:
ulferts
2026-05-05 12:08:14 +02:00
parent 7b858dfdca
commit eb09ce0c1b
8 changed files with 110 additions and 19 deletions
+1 -1
View File
@@ -208,7 +208,7 @@ gem "aws-sdk-core", "~> 3.244"
# File upload via fog + screenshots on travis
gem "aws-sdk-s3", "~> 1.217"
gem "openproject-token", "~> 8.8.2"
gem "openproject-token", "~> 8.9.0"
gem "plaintext", "~> 0.3.7"
+3 -3
View File
@@ -914,7 +914,7 @@ GEM
activesupport (>= 7.2.0)
openproject-octicons (>= 19.34.0)
view_component (>= 3.1, < 5.0)
openproject-token (8.8.2)
openproject-token (8.9.0)
activemodel
openssl (4.0.1)
openssl-signature_algorithm (1.3.0)
@@ -1693,7 +1693,7 @@ DEPENDENCIES
openproject-resource_management!
openproject-storages!
openproject-team_planner!
openproject-token (~> 8.8.2)
openproject-token (~> 8.9.0)
openproject-two_factor_authentication!
openproject-webhooks!
openproject-wikis!
@@ -2075,7 +2075,7 @@ CHECKSUMS
openproject-resource_management (1.0.0)
openproject-storages (1.0.0)
openproject-team_planner (1.0.0)
openproject-token (8.8.2) sha256=081cbff7269d92a82fa1d63e9e09c87b70d47d7aefadcbb80d1e7368bc2cf096
openproject-token (8.9.0) sha256=aa08c144889010750de4edaf61f8614ccb82ac6c63beef1d3a21c6a222358605
openproject-two_factor_authentication (1.0.0)
openproject-webhooks (1.0.0)
openproject-wikis (1.0.0)
@@ -29,7 +29,11 @@ See COPYRIGHT and LICENSE files for more details.
<%=
render(Primer::Beta::Text.new(tag: :div, color: :muted, mb: 3)) do
I18n.t("backlogs.sharing_description")
if only_fallback_allowed
t(".sharing_fallback_description")
else
t(".sharing_description")
end
end
%>
@@ -40,6 +44,6 @@ See COPYRIGHT and LICENSE files for more details.
method: :patch,
data: { turbo_frame: "_top", controller: "show-when-value-selected" }
) do |f|
render(Projects::Settings::Backlogs::SharingForm.new(f))
render(Projects::Settings::Backlogs::SharingForm.new(f, only_fallback_allowed:))
end
%>
@@ -35,15 +35,17 @@ module Projects
include ApplicationHelper
include OpPrimer::ComponentHelpers
def initialize(project:)
def initialize(project:,
only_fallback_allowed: false)
super
@project = project
@only_fallback_allowed = only_fallback_allowed
end
private
attr_reader :project
attr_reader :project, :only_fallback_allowed
end
end
end
@@ -41,7 +41,7 @@ module Projects
# is saved.
# Ideally the hidden field should automatically be rendered by the `radio_button_group`
# helper, similar to how the `collection_radio_buttons` rails helper does.
sharing_form.hidden(name: :sprint_sharing, value: "")
sharing_form.hidden(name: :sprint_sharing, value: model.sprint_sharing)
sharing_form.radio_button_group(
name: :sprint_sharing,
@@ -69,14 +69,22 @@ module Projects
)
end
def initialize(only_fallback_allowed: false)
super()
@only_fallback_allowed = only_fallback_allowed
end
private
attr_reader :only_fallback_allowed
def checked?(option)
option == model.sprint_sharing
end
def disabled?(option)
option == Project::SHARE_ALL_PROJECTS && share_all_projects_disabled?
(only_fallback_allowed && option != Projects::SprintSharing::NO_SHARING) ||
(option == Project::SHARE_ALL_PROJECTS && share_all_projects_disabled?)
end
def sharing_option_text(option, key, **)
@@ -84,12 +92,9 @@ module Projects
end
def caption_for(option)
if disabled?(option)
if User.current.allowed_in_project?(:view_project, global_sprint_sharer)
sharing_option_text(option, :disabled_caption, name: global_sprint_sharer.name)
else
sharing_option_text(option, :disabled_caption_anonymous)
end
case option
when Project::SHARE_ALL_PROJECTS
shared_all_projects_caption
else
sharing_option_text(option, :caption)
end
@@ -117,6 +122,16 @@ module Projects
end
end
end
def shared_all_projects_caption
if !disabled?(Project::SHARE_ALL_PROJECTS)
sharing_option_text(Project::SHARE_ALL_PROJECTS, :caption)
elsif User.current.allowed_in_project?(:view_project, global_sprint_sharer)
sharing_option_text(Project::SHARE_ALL_PROJECTS, :disabled_caption, name: global_sprint_sharer.name)
else
sharing_option_text(Project::SHARE_ALL_PROJECTS, :disabled_caption_anonymous)
end
end
end
end
end
@@ -35,5 +35,18 @@ See COPYRIGHT and LICENSE files for more details.
)
) %>
<%= render(Projects::Settings::Backlogs::SharingFormComponent.new(project: @project)) %>
<% with_enterprise_banner_guard(
:sprint_sharing,
inactive_guard: !@project.not_sharing_sprints?,
variant: :large,
image: "enterprise/project-creation-wizard.png"
) do %>
<%= render(EnterpriseEdition::BannerComponent.new(:sprint_sharing, variant: :inline)) %>
<%= render(
Projects::Settings::Backlogs::SharingFormComponent.new(
project: @project,
only_fallback_allowed: !EnterpriseToken.allows_to?(:sprint_sharing)
)
) %>
<% end %>
<% end %>
+13 -1
View File
@@ -98,7 +98,6 @@ en:
definition_of_done: "Definition of Done"
definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting."
done_status: "Done status"
sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)."
sharing: "Sharing"
label_burndown_chart: "Burndown chart"
label_sprint_board: "Sprint board"
@@ -206,6 +205,13 @@ en:
story_points: "Story points"
story_points_ideal: "Story points (ideal)"
ee:
features:
sprint_sharing: "Sprint sharing"
upsell:
sprint_sharing:
description: "Share sprints across projects to align teams and coordinate work in scaled agile setups (SAFe)."
label_backlog: "Backlog"
label_backlog_bucket_edit: "Edit backlog bucket"
label_backlog_bucket_new: "New backlog bucket"
@@ -257,4 +263,10 @@ en:
info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints."
sprint_sharing: Share sprints
backlogs:
sharing_form_component:
sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)."
sharing_fallback_description: "Lacking a corporate enterprise plan, the sharing options are limited to the project's own sprints. The currently active setting remains active."
remaining_hours: "remaining work"
@@ -42,7 +42,8 @@ RSpec.describe "Backlogs project settings sprint sharing", :js do
login_as current_user
end
context "with share_sprint permission" do
context "with share_sprint permission and enterprise token",
with_ee: [:sprint_sharing] do
it "displays and stores sprint sharing settings" do
visit project_settings_backlog_sharing_path(project)
@@ -127,6 +128,50 @@ RSpec.describe "Backlogs project settings sprint sharing", :js do
end
end
context "with share_sprint permission but no enterprise token" do
context "without existing sharing setting in the project" do
it "shows an enterprise token teaser" do
visit project_settings_backlog_sharing_path(project)
expect(page).to have_text("Share sprints across projects to align teams")
expect(page).to have_no_field("Don't share")
expect(page).to have_no_field("All projects")
expect(page).to have_no_field("Subprojects")
expect(page).to have_no_field("Receive shared sprints")
end
end
context "with existing sharing setting in the project" do
before do
project.update!(sprint_sharing: "receive_shared")
end
it "shows the existing sharing setting but disables them except for `Don't share`" do
visit project_settings_backlog_sharing_path(project)
# All radio buttons are present with the selected option displayed.
# But all except the "Don't share" option are disabled.
expect(page).to have_unchecked_field("Don't share")
expect(page).to have_unchecked_field("All projects", disabled: true)
expect(page).to have_unchecked_field("Subprojects", disabled: true)
expect(page).to have_checked_field("Receive shared sprints", disabled: true)
choose("Don't share")
click_button "Save"
# Now that the `Don't share` option is selected, the large enterprise banner is displayed.
expect(page).to have_text("Share sprints across projects to align teams")
expect(page).to have_no_field("Don't share")
expect(page).to have_no_field("All projects")
expect(page).to have_no_field("Subprojects")
expect(page).to have_no_field("Receive shared sprints")
end
end
end
context "without share_sprint permission" do
let(:permissions) { %i[create_sprints select_done_statuses] }