Skip to content

Commit

Permalink
Merge pull request #15899 from opf/generalize-sharing-modal
Browse files Browse the repository at this point in the history
Generalize sharing modal
  • Loading branch information
klaustopher authored Jun 27, 2024
2 parents ffd343f + fef400e commit 3d04c3d
Show file tree
Hide file tree
Showing 88 changed files with 1,553 additions and 1,355 deletions.
4 changes: 2 additions & 2 deletions app/components/_index.sass
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@import "work_packages/share/modal_body_component"
@import "work_packages/share/invite_user_form_component"
@import "shares/modal_body_component"
@import "shares/invite_user_form_component"
@import "work_packages/progress/modal_body_component"
@import "open_project/common/attribute_component"
@import "filter/filters_component"
Expand Down
6 changes: 3 additions & 3 deletions app/components/members/user_filter_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,11 @@ def builtin_share_roles
def mapped_shared_role_name(role)
case role.builtin
when Role::BUILTIN_WORK_PACKAGE_VIEWER
I18n.t("work_package.sharing.permissions.view")
I18n.t("work_package.permissions.view")
when Role::BUILTIN_WORK_PACKAGE_COMMENTER
I18n.t("work_package.sharing.permissions.comment")
I18n.t("work_package.permissions.comment")
when Role::BUILTIN_WORK_PACKAGE_EDITOR
I18n.t("work_package.sharing.permissions.edit")
I18n.t("work_package.permissions.edit")
else
role.name
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@
dynamic_label: true,
anchor_align: :end,
color: :subtle,
data: { test_selector: 'op-share-wp-bulk-update-role'})) do |menu|
menu.with_show_button(scheme: :invisible, color: :subtle, data: { 'work-packages--share--bulk-selection-target': 'bulkUpdateRoleLabel' }) do |button|
data: { test_selector: 'op-share-dialog-bulk-update-role'})) do |menu|
menu.with_show_button(scheme: :invisible, color: :subtle, data: { 'shares--bulk-selection-target': 'bulkUpdateRoleLabel' }) do |button|
button.with_trailing_action_icon(icon: "triangle-down")
'Placeholder'
end

options.each do |option|
menu.with_item(label: option[:label],
@available_roles.each do |role_hash|
menu.with_item(label: role_hash[:label],
href: update_path,
method: :patch,
active: false,
form_arguments: {
method: :patch,
name: 'role_ids[]',
value: option[:value],
data: { 'work-packages--share--bulk-selection-target': 'bulkForm bulkUpdateRoleForm',
'role-name': option[:label],
'test-selector': "op-share-wp-bulk-update-role-permission-#{option[:label]}" }
value: role_hash[:value],
data: { 'shares--bulk-selection-target': 'bulkForm bulkUpdateRoleForm',
'role-name': role_hash[:label],
'test-selector': "op-share-dialog-bulk-update-role-permission-#{role_hash[:label]}" }
}) do |item|
item.with_description.with_content(option[:description])
item.with_description.with_content(role_hash[:description])
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,17 @@
# See COPYRIGHT and LICENSE files for more details.
# ++

module WorkPackages
module Share
class BulkPermissionButtonComponent < ApplicationComponent
include WorkPackages::Share::Concerns::DisplayableRoles
module Shares
class BulkPermissionButtonComponent < ApplicationComponent # rubocop:disable OpenProject/AddPreviewForViewComponent
def initialize(entity:, available_roles:)
super

def initialize(work_package:)
super

@work_package = work_package
end
@entity = entity
@available_roles = available_roles
end

def update_path
work_package_shares_bulk_path(@work_package)
end
def update_path
url_for([:bulk, @entity, Member])
end
end
end
21 changes: 21 additions & 0 deletions app/components/shares/bulk_selection_counter_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<%
concat(
render(Primer::Alpha::CheckBox.new(name: 'toggle_all',
value: nil,
label: I18n.t('sharing.label_toggle_all'),
visually_hide_label: true,
data: { 'shares--bulk-selection-target': 'toggleAll',
action: 'shares--bulk-selection#toggle' }))
)

concat(
render(Primer::Beta::Text.new(ml: 2, data: { 'shares--bulk-selection-target': 'sharedCounter' })) do
I18n.t('sharing.count', count:)
end
)

# Text contents managed by Stimulus controller
concat(
render(Primer::Beta::Text.new(ml: 2, data: { 'shares--bulk-selection-target': 'selectedCounter' }))
)
%>
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,16 @@
# See COPYRIGHT and LICENSE files for more details.
# ++

module WorkPackages
module Share
class BulkSelectionCounterComponent < ApplicationComponent
def initialize(count:)
super
module Shares
class BulkSelectionCounterComponent < ApplicationComponent # rubocop:disable OpenProject/AddPreviewForViewComponent
def initialize(count:)
super

@count = count
end
@count = count
end

private
private

attr_reader :count
end
attr_reader :count
end
end
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<%=
component_wrapper(data: { test_selector: 'op-share-wp-active-count'}) do
component_wrapper(data: { test_selector: 'op-share-dialog-active-count'}) do
render(Primer::Box.new(display: :flex, aligns_items: :center)) do
# There's no point in rendering the BulkSelectionCounterComponent even if
# I'm able to manage shares if the only user that the work package is
# currently shared is myself, since I'm not able to manage my own share.
if sharing_manageable? && shared_with_anyone_else_other_than_myself?
render(WorkPackages::Share::BulkSelectionCounterComponent.new(count:))
render(Shares::BulkSelectionCounterComponent.new(count:))
else
render(WorkPackages::Share::ShareCounterComponent.new(count:))
render(Shares::ShareCounterComponent.new(count:))
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,30 +28,32 @@
# See COPYRIGHT and LICENSE files for more details.
# ++

module WorkPackages
module Share
class CounterComponent < ApplicationComponent
include ApplicationHelper
include OpTurbo::Streamable
include OpPrimer::ComponentHelpers
include WorkPackages::Share::Concerns::Authorization

def initialize(work_package:, count:)
super

@work_package = work_package
@count = count
end

private

attr_reader :work_package, :count

def shared_with_anyone_else_other_than_myself?
Member.of_work_package(@work_package)
.where.not(principal: User.current)
.any?
end
module Shares
class CounterComponent < ApplicationComponent # rubocop:disable OpenProject/AddPreviewForViewComponent
include ApplicationHelper
include OpTurbo::Streamable
include OpPrimer::ComponentHelpers

def initialize(entity:,
count:,
sharing_manageable:)
super

@entity = entity
@count = count
@sharing_manageable = sharing_manageable
end

private

attr_reader :entity, :count

def sharing_manageable? = @sharing_manageable

def shared_with_anyone_else_other_than_myself?
Member.of_entity(@entity)
.where.not(principal: User.current)
.any?
end
end
end
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
<%=
component_wrapper do
if sharing_manageable?
if @sharing_manageable
primer_form_with(
model: new_share,
url: url_for([@work_package, Member]),
url: url_for([@entity, Member]),
data: { controller: 'user-limit ' \
'work-packages--share--user-selected',
'shares--user-selected',
'application-target': 'dynamic',
'user-limit-open-seats-value': OpenProject::Enterprise.open_seats_count,
action: 'submit->work-packages--share--user-selected#ensureUsersSelected' }
action: 'submit->shares--user-selected#ensureUsersSelected' }
) do |form|
grid_layout('invite-user-form',
tag: :div) do |invite_form|
invite_form.with_area('invitee') do
render(WorkPackages::Share::Invitee.new(form))
render(Shares::Invitee.new(form))
end

invite_form.with_area('permission') do
render(WorkPackages::Share::PermissionButtonComponent.new(
render(Shares::PermissionButtonComponent.new(
share: new_share,
available_roles: @available_roles,
form_arguments: { builder: form, name: "role_id" },
data: { 'test-selector': 'op-share-wp-invite-role' })
data: { 'test-selector': 'op-share-dialog-invite-role' })
)
end

invite_form.with_area('submit') do
render(Primer::Beta::Button.new(scheme: :primary, type: :submit)) do
I18n.t('work_package.sharing.share')
I18n.t('sharing.share')
end
end

if OpenProject::Enterprise.user_limit.present?
invite_form.with_area('userLimitWarning',
data: { 'user-limit-target': 'limitWarning',
'test-selector': 'op-share-wp-user-limit' },
'test-selector': 'op-share-dialog-user-limit' },
display: :none) do
flex_layout do |user_limit_row|
user_limit_row.with_column(mr: 2) do
Expand All @@ -43,8 +44,9 @@
user_limit_row.with_column do
render(Primer::Beta::Text.new(color: :danger)) do
I18n.t(
"work_package.sharing.warning_user_limit_reached#{'_admin' if User.current.admin?}",
upgrade_url: OpenProject::Enterprise.upgrade_url
"sharing.warning_user_limit_reached#{'_admin' if User.current.admin?}",
upgrade_url: OpenProject::Enterprise.upgrade_url,
entity: @entity.model_name.human
).html_safe
end
end
Expand All @@ -53,8 +55,8 @@
end

invite_form.with_area('userSelectedWarning',
data: { 'work-packages--share--user-selected-target': 'error',
'test-selector': 'op-share-wp-no-user-selected' },
data: { 'shares--user-selected-target': 'error',
'test-selector': 'op-share-dialog-no-user-selected' },
display: :none) do
flex_layout do |no_selected_user_row|
no_selected_user_row.with_column(mr: 2) do
Expand All @@ -63,15 +65,15 @@

no_selected_user_row.with_column do
render(Primer::Beta::Text.new(color: :danger)) do
I18n.t("work_package.sharing.warning_no_selected_user")
I18n.t("sharing.warning_no_selected_user", entity: @entity.model_name.human)
end
end
end
end

if @errors.present?
invite_form.with_area('errors',
data: { 'test-selector': 'op-share-wp-error-message' }) do
data: { 'test-selector': 'op-share-dialog-error-message' }) do
flex_layout do |error_rows|
@errors.full_messages.each do |error_message|
error_rows.with_row do
Expand All @@ -94,7 +96,7 @@
end
end
else
render(Primer::Alpha::Banner.new(icon: :info)) { I18n.t('work_package.sharing.permissions.denied') }
render(Primer::Alpha::Banner.new(icon: :info)) { I18n.t('sharing.denied', entities: @entity.model_name.human(count: 2)) }
end
end
%>
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,32 @@
# See COPYRIGHT and LICENSE files for more details.
#++

module WorkPackages
module Share
class InviteUserFormComponent < ApplicationComponent
include ApplicationHelper
include OpTurbo::Streamable
include OpPrimer::ComponentHelpers
include WorkPackages::Share::Concerns::Authorization
module Shares
class InviteUserFormComponent < ApplicationComponent # rubocop:disable OpenProject/AddPreviewForViewComponent
include ApplicationHelper
include OpTurbo::Streamable
include OpPrimer::ComponentHelpers

def initialize(work_package:, errors: nil)
super
def initialize(entity:,
available_roles:,
sharing_manageable:,
errors: nil)
super

@work_package = work_package
@errors = errors
end
@entity = entity
@available_roles = available_roles
@sharing_manageable = sharing_manageable
@errors = errors
end

def new_share
@new_share ||= Member.new(entity: @entity, roles: [Role.new(id: default_role[:value])])
end

private

def new_share
@new_share ||= Member.new(entity: @work_package, roles: [Role.new(builtin: Role::BUILTIN_WORK_PACKAGE_VIEWER)])
end
def default_role
@available_roles.find { |role_hash| role_hash[:default] } || @available_roles.first
end
end
end
Loading

0 comments on commit 3d04c3d

Please sign in to comment.