Skip to content

Commit e2d540d

Browse files
committed
Merge branch 'release/14.3' into dev
2 parents aa784d5 + 748b45c commit e2d540d

File tree

96 files changed

+620
-162
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+620
-162
lines changed

Diff for: app/components/shares/invite_user_form_component.html.erb

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<%=
22
component_wrapper do
3-
if strategy.manageable?
4-
primer_form_with(
3+
primer_form_with(
54
model: new_share,
65
url: url_for([entity, Member]),
76
data: { controller: 'user-limit shares--user-selected',
@@ -17,7 +16,7 @@
1716
invite_form.with_area('permission') do
1817
render(Shares::PermissionButtonComponent.new(
1918
share: new_share,
20-
available_roles: strategy.available_roles,
19+
strategy: strategy,
2120
form_arguments: { builder: form, name: "role_id" },
2221
data: { 'test-selector': 'op-share-dialog-invite-role' })
2322
)
@@ -92,9 +91,6 @@
9291
end
9392
end
9493
end
95-
end
96-
else
97-
render(Primer::Alpha::Banner.new(icon: :info)) { I18n.t('sharing.denied', entities: entity.model_name.human(count: 2)) }
9894
end
9995
end
10096
%>

Diff for: app/components/shares/modal_body_component.html.erb

+4-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
end
99
end
1010

11-
modal_content.with_row do
12-
render(Shares::InviteUserFormComponent.new(strategy:, errors: errors))
11+
if strategy.manageable?
12+
modal_content.with_row do
13+
render(Shares::InviteUserFormComponent.new(strategy:, errors: errors))
14+
end
1315
end
1416

1517
modal_content.with_row(data: { 'test-selector': 'op-share-dialog-active-list',

Diff for: app/components/shares/modal_body_component.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def initialize(strategy:, shares:, errors: nil)
4848
end
4949

5050
def self.wrapper_key
51-
"share_list"
51+
"share_modal_body"
5252
end
5353

5454
private

Diff for: app/components/shares/modal_body_component.sass

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
&--user-row
66
display: grid
7-
grid-template-columns: minmax(31px, auto) 1fr // 31px is the width needed to display a group avatar
8-
grid-template-areas: "avatar user_details"
7+
grid-template-columns: minmax(31px, auto) 1fr auto // 31px is the width needed to display a group avatar
8+
grid-template-areas: "avatar user_details button"
99
grid-column-gap: 10px
1010

1111
&_manageable

Diff for: app/components/shares/modal_upsale_component.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class ModalUpsaleComponent < ApplicationComponent # rubocop:disable OpenProject/
3333
include OpPrimer::ComponentHelpers
3434

3535
def self.wrapper_key
36-
"share_list"
36+
"share_modal_body"
3737
end
3838
end
3939
end

Diff for: app/components/shares/permission_button_component.html.erb

+24-22
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,30 @@
11
<%=
22
component_wrapper do
3-
render(Primer::Alpha::ActionMenu.new(**{ select_variant: :single,
4-
size: :small,
5-
dynamic_label: true,
6-
anchor_align: :end,
7-
color: :subtle }.deep_merge(@system_arguments))) do |menu|
8-
menu.with_show_button(data: { 'shares--bulk-selection-target': 'userRowRole',
9-
'share-id': share.id,
10-
'active-role-name': permission_name(active_role.id)}) do |button|
11-
button.with_trailing_action_icon(icon: :"triangle-down")
12-
permission_name(active_role.id)
13-
end
3+
content_tag(:div, class: tooltip_wrapper_classes, data: { tooltip: tooltip_message }) do
4+
render(Primer::Alpha::ActionMenu.new(**{ select_variant: :single,
5+
size: :small,
6+
dynamic_label: true,
7+
anchor_align: :end,
8+
color: :subtle }.deep_merge(@system_arguments))) do |menu|
9+
menu.with_show_button(disabled: !editable?, data: { 'shares--bulk-selection-target': 'userRowRole',
10+
'share-id': share.id,
11+
'active-role-name': permission_name(active_role.id)}) do |button|
12+
button.with_trailing_action_icon(icon: :"triangle-down")
13+
permission_name(active_role.id)
14+
end
1415

15-
@available_roles.each do |role_hash|
16-
menu.with_item(label: role_hash[:label],
17-
href: update_path,
18-
method: :patch,
19-
active: role_active?(role_hash),
20-
data: { value: role_hash[:value] },
21-
form_arguments: {
22-
method: :patch,
23-
inputs: form_inputs(role_hash[:value])
24-
}) do |item|
25-
item.with_description.with_content(role_hash[:description])
16+
strategy.available_roles.each do |role_hash|
17+
menu.with_item(label: role_hash[:label],
18+
href: update_path,
19+
method: :patch,
20+
active: role_active?(role_hash),
21+
data: { value: role_hash[:value] },
22+
form_arguments: {
23+
method: :patch,
24+
inputs: form_inputs(role_hash[:value])
25+
}) do |item|
26+
item.with_description.with_content(role_hash[:description])
27+
end
2628
end
2729
end
2830
end

Diff for: app/components/shares/permission_button_component.rb

+20-4
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ class PermissionButtonComponent < ApplicationComponent # rubocop:disable OpenPro
3232
include OpPrimer::ComponentHelpers
3333
include OpTurbo::Streamable
3434

35-
def initialize(share:, available_roles:, **system_arguments)
35+
def initialize(share:, strategy:, **system_arguments)
3636
super
3737

38-
@available_roles = available_roles
38+
@strategy = strategy
3939
@share = share
4040
@system_arguments = system_arguments
4141
end
@@ -58,7 +58,7 @@ def wrapper_uniq_by
5858

5959
private
6060

61-
attr_reader :share, :available_roles
61+
attr_reader :share, :strategy
6262

6363
def active_role
6464
if share.persisted?
@@ -71,7 +71,7 @@ def active_role
7171
end
7272

7373
def permission_name(value)
74-
available_roles.find { |role_hash| role_hash[:value] == value }[:label]
74+
strategy.available_roles.find { |role_hash| role_hash[:value] == value }[:label]
7575
end
7676

7777
def form_inputs(role_id)
@@ -80,5 +80,21 @@ def form_inputs(role_id)
8080
inputs << { name: "filters", value: params[:filters] } if params[:filters]
8181
end
8282
end
83+
84+
def tooltip_message
85+
return if strategy.manageable?
86+
87+
I18n.t("sharing.denied", entities: strategy.entity.class.model_name.human(count: 2))
88+
end
89+
90+
def tooltip_wrapper_classes
91+
return [] if strategy.manageable?
92+
93+
["tooltip--left"]
94+
end
95+
96+
def editable?
97+
strategy.manageable? && share.principal != User.current
98+
end
8399
end
84100
end

Diff for: app/components/shares/project_queries/empty_state_component.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def public_blankslate_config
6262

6363
def unfiltered_blankslate_config
6464
{
65-
icon: :people,
65+
icon: "share-android",
6666
heading_text: I18n.t("sharing.project_queries.blank_state.private.header"),
6767
description_text: I18n.t("sharing.project_queries.blank_state.private.description")
6868
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
<%=
22
container.with_row do
3-
render(Primer::Forms::ToggleSwitchForm.new(
4-
name: "publish_project_query",
5-
label: t('sharing.project_queries.public_flag.label', instance_name: Setting.app_title),
6-
caption: t('sharing.project_queries.public_flag.caption'),
7-
src: toggle_public_flag_link,
8-
csrf_token: form_authenticity_token,
9-
status_label_position: :start,
10-
checked: published?,
11-
enabled: can_publish?
12-
))
3+
content_tag(:div, class: tooltip_wrapper_classes, data: { tooltip: tooltip_message }) do
4+
render(Primer::Forms::ToggleSwitchForm.new(
5+
name: "publish_project_query",
6+
label: t("sharing.project_queries.public_flag.label", instance_name: Setting.app_title),
7+
caption: t("sharing.project_queries.public_flag.caption"),
8+
src: toggle_public_flag_link,
9+
csrf_token: form_authenticity_token,
10+
status_label_position: :start,
11+
checked: published?,
12+
enabled: can_publish?
13+
))
14+
end
1315
end
1416
%>

Diff for: app/components/shares/project_queries/public_flag_component.rb

+12
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,18 @@ def published?
5656
def can_publish?
5757
User.current.allowed_globally?(:manage_public_project_queries)
5858
end
59+
60+
def tooltip_message
61+
return if can_publish?
62+
63+
I18n.t("sharing.project_queries.publishing_denied")
64+
end
65+
66+
def tooltip_wrapper_classes
67+
["d-flex", "flex-column"].tap do |classlist|
68+
classlist << "tooltip--bottom" unless can_publish?
69+
end
70+
end
5971
end
6072
end
6173
end

Diff for: app/components/shares/share_row_component.html.erb

+6-6
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@
2020
render(Shares::UserDetailsComponent.new(share:, manager_mode: share_editable?))
2121
end
2222

23-
if share_editable?
24-
user_row_grid.with_area(:button, tag: :div, color: :subtle) do
25-
render(Shares::PermissionButtonComponent.new(share:,
26-
available_roles: @available_roles,
27-
data: { 'test-selector': 'op-share-dialog-update-role' }))
28-
end
23+
user_row_grid.with_area(:button, tag: :div, color: :subtle) do
24+
render(Shares::PermissionButtonComponent.new(share:,
25+
strategy: strategy,
26+
data: { 'test-selector': 'op-share-dialog-update-role' }))
27+
end
2928

29+
if share_editable?
3030
user_row_grid.with_area(:remove, tag: :div) do
3131
form_with url: url_for([entity, share]), method: :delete do
3232
render(Primer::Beta::IconButton.new(icon: "trash",

Diff for: app/controllers/projects/queries_controller.rb

+16-3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
class Projects::QueriesController < ApplicationController
3030
include Projects::QueryLoading
31+
include OpTurbo::ComponentStream
3132

3233
# No need for a more specific authorization check. That is carried out in the contracts.
3334
no_authorization_required! :show, :new, :create, :rename, :update, :toggle_public, :destroy
@@ -71,15 +72,27 @@ def update
7172
render_result(call, success_i18n_key: "lists.update.success", error_i18n_key: "lists.update.failure")
7273
end
7374

74-
def toggle_public
75-
to_be_public = !@query.public?
75+
def toggle_public # rubocop:disable Metrics/AbcSize
76+
to_be_public = ActiveRecord::Type::Boolean.new.cast(params["value"])
7677
i18n_key = to_be_public ? "lists.publish" : "lists.unpublish"
7778

7879
call = Queries::Projects::ProjectQueries::PublishService
7980
.new(user: current_user, model: @query)
8081
.call(public: to_be_public)
8182

82-
render_result(call, success_i18n_key: "#{i18n_key}.success", error_i18n_key: "#{i18n_key}.failure")
83+
respond_to do |format|
84+
format.turbo_stream do
85+
# Load shares and replace the modal
86+
strategy = SharingStrategies::ProjectQueryStrategy.new(@query, user: current_user)
87+
shares = strategy.shares_query({}).results
88+
replace_via_turbo_stream(component: Shares::ModalBodyComponent.new(strategy:, shares:, errors: []))
89+
render turbo_stream: turbo_streams, status:
90+
end
91+
92+
format.html do
93+
render_result(call, success_i18n_key: "#{i18n_key}.success", error_i18n_key: "#{i18n_key}.failure")
94+
end
95+
end
8396
end
8497

8598
def destroy

Diff for: app/controllers/shares_controller.rb

+3-18
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ def respond_with_update_permission_button
223223
replace_via_turbo_stream(
224224
component: Shares::PermissionButtonComponent.new(
225225
share: @share,
226-
available_roles: sharing_strategy.available_roles,
226+
strategy: sharing_strategy,
227227
data: { "test-selector": "op-share-dialog-update-role" }
228228
)
229229
)
@@ -265,7 +265,7 @@ def respond_with_bulk_updated_permission_buttons
265265
replace_via_turbo_stream(
266266
component: Shares::PermissionButtonComponent.new(
267267
share:,
268-
available_roles: sharing_strategy.available_roles,
268+
strategy: sharing_strategy,
269269
data: { "test-selector": "op-share-dialog-update-role" }
270270
)
271271
)
@@ -326,22 +326,7 @@ def current_visible_member_count
326326
end
327327

328328
def load_query
329-
return @query if defined?(@query)
330-
331-
@query = ParamsToQueryService
332-
.new(Member, current_user, query_class: Queries::Members::NonInheritedMemberQuery)
333-
.call(params)
334-
335-
# Set default filter on the entity
336-
@query.where("entity_id", "=", @entity.id)
337-
@query.where("entity_type", "=", @entity.class.name)
338-
if @project
339-
@query.where("project_id", "=", @project.id)
340-
end
341-
342-
@query.order(name: :asc) unless params[:sortBy]
343-
344-
@query
329+
@query = sharing_strategy.shares_query(params)
345330
end
346331

347332
def load_shares

Diff for: app/models/queries/base_query.rb

+4-5
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,13 @@ def i18n_scope
5151
:activerecord
5252
end
5353

54-
# Use the Query class' error messages.
55-
# So everything under
54+
# Also use the Query class' as a lookup ancestor so that error messages, etc can be shared.
55+
# So if nothing is defined for the specific query class, we fall back to the generic query class.
5656
#
57+
# This is useful for error messages, because we can fall back to error messages, etc in
5758
# activerecord.errors.models.query
58-
#
59-
# is found.
6059
def lookup_ancestors
61-
[Query]
60+
super + [Query]
6261
end
6362
end
6463

Diff for: app/models/sharing_strategies/base_strategy.rb

+19
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,24 @@ def custom_empty_state_component?
7979
def empty_state_component
8080
nil
8181
end
82+
83+
def shares_query(params) # rubocop:disable Metrics/AbcSize
84+
return @query if defined?(@query)
85+
86+
@query = ParamsToQueryService
87+
.new(Member, user, query_class: Queries::Members::NonInheritedMemberQuery)
88+
.call(params)
89+
90+
# Set default filter on the entity
91+
@query.where("entity_id", "=", entity.id)
92+
@query.where("entity_type", "=", entity.class.name)
93+
if entity.respond_to?(:project)
94+
@query.where("project_id", "=", entity.project.id)
95+
end
96+
97+
@query.order(name: :asc) unless params[:sortBy]
98+
99+
@query
100+
end
82101
end
83102
end

Diff for: config/locales/crowdin/af.yml

+4
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,9 @@ af:
11401140
other: "Notifications"
11411141
placeholder_user: "Placeholder user"
11421142
project: "Projek"
1143+
project_query:
1144+
one: "Project list"
1145+
other: "Project lists"
11431146
query: "Pasgemaakte navraag"
11441147
role:
11451148
one: "Rol"
@@ -3444,6 +3447,7 @@ af:
34443447
additional_privileges_group: "Might have additional privileges (as group member)"
34453448
additional_privileges_project_or_group: "Might have additional privileges (as project or group member)"
34463449
project_queries:
3450+
publishing_denied: "You do not have permission to make project lists public."
34473451
access_warning: "Users will only see the projects they have access to. Sharing project lists does not impact individual project permissions."
34483452
public_flag:
34493453
label: "Share with everyone at %{instance_name}"

0 commit comments

Comments
 (0)