Skip to content

Commit 4542ecc

Browse files
Mm 65096 - channel admin rules confirmation modal (mattermost#33758)
* MM65096 - channel admin rules confirmation modal * trigger the sync job directly * update the active state correctly and adjust styling * Add ids to policy search so sync job finds the channel policy * combine the channel and the inherited policies expressions for confirm modal * add missing translations * fix tests and fix incorrect Id definition * fix translations * Revert "fix translations" * fix typo * remove plugin auto injected logging code --------- Co-authored-by: Mattermost Build <build@mattermost.com>
1 parent 17c0d7c commit 4542ecc

11 files changed

Lines changed: 1219 additions & 46 deletions

File tree

server/channels/store/sqlstore/access_control_policy_store.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,12 @@ func (s *SqlAccessControlPolicyStore) SearchPolicies(rctx request.CTX, opts mode
568568
count = count.Where(sq.Eq{"Active": true})
569569
}
570570

571+
if len(opts.IDs) > 0 {
572+
condition := sq.Eq{"Id": opts.IDs}
573+
query = query.Where(condition)
574+
count = count.Where(condition)
575+
}
576+
571577
cursor := opts.Cursor
572578

573579
if !cursor.IsEmpty() {

server/channels/store/storetest/access_control_policy_store.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,4 +353,53 @@ func testAccessControlPolicyStoreGetAll(t *testing.T, rctx request.CTX, ss store
353353
require.NotNil(t, policies)
354354
require.Len(t, policies, 0)
355355
})
356+
357+
t.Run("GetAll by IDs", func(t *testing.T) {
358+
// Test searching by specific IDs
359+
policies, _, err := ss.AccessControlPolicy().SearchPolicies(rctx, model.AccessControlPolicySearch{
360+
IDs: []string{parentPolicy.ID, resourcePolicy.ID},
361+
Limit: 10,
362+
})
363+
require.NoError(t, err)
364+
require.NotNil(t, policies)
365+
require.Len(t, policies, 2)
366+
367+
// Verify we got the correct policies
368+
foundIDs := make([]string, len(policies))
369+
for i, p := range policies {
370+
foundIDs[i] = p.ID
371+
}
372+
require.Contains(t, foundIDs, parentPolicy.ID)
373+
require.Contains(t, foundIDs, resourcePolicy.ID)
374+
375+
// Test searching by single ID
376+
policies, _, err = ss.AccessControlPolicy().SearchPolicies(rctx, model.AccessControlPolicySearch{
377+
IDs: []string{parentPolicy.ID},
378+
Limit: 10,
379+
})
380+
require.NoError(t, err)
381+
require.NotNil(t, policies)
382+
require.Len(t, policies, 1)
383+
require.Equal(t, parentPolicy.ID, policies[0].ID)
384+
385+
// Test searching by non-existent IDs
386+
policies, _, err = ss.AccessControlPolicy().SearchPolicies(rctx, model.AccessControlPolicySearch{
387+
IDs: []string{model.NewId(), model.NewId()},
388+
Limit: 10,
389+
})
390+
require.NoError(t, err)
391+
require.NotNil(t, policies)
392+
require.Len(t, policies, 0)
393+
394+
// Test combining IDs with Type filter
395+
policies, _, err = ss.AccessControlPolicy().SearchPolicies(rctx, model.AccessControlPolicySearch{
396+
IDs: []string{parentPolicy.ID, resourcePolicy.ID},
397+
Type: model.AccessControlPolicyTypeParent,
398+
Limit: 10,
399+
})
400+
require.NoError(t, err)
401+
require.NotNil(t, policies)
402+
require.Len(t, policies, 1)
403+
require.Equal(t, parentPolicy.ID, policies[0].ID)
404+
})
356405
}

server/public/model/access_policy.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type AccessControlPolicySearch struct {
4343
Term string `json:"term"`
4444
Type string `json:"type"`
4545
ParentID string `json:"parent_id"`
46+
IDs []string `json:"ids"`
4647
Cursor AccessControlPolicyCursor `json:"cursor"`
4748
Limit int `json:"limit"`
4849
IncludeChildren bool `json:"include_children"`

webapp/channels/src/components/admin_console/access_control/policy_details/__snapshots__/policy_details.test.tsx.snap

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,14 @@ exports[`components/admin_console/access_control/policy_details/PolicyDetails sh
9797
<TableEditor
9898
actions={
9999
Object {
100+
"createJob": [MockFunction],
100101
"getAccessControlFields": [MockFunction],
102+
"getChannelMembers": [MockFunction],
101103
"getChannelPolicy": [MockFunction],
102104
"getVisualAST": [MockFunction],
103105
"saveChannelPolicy": [MockFunction],
104106
"searchUsers": [MockFunction],
107+
"updateAccessControlPolicyActive": [MockFunction],
105108
}
106109
}
107110
enableUserManagedAttributes={false}
@@ -305,11 +308,14 @@ exports[`components/admin_console/access_control/policy_details/PolicyDetails sh
305308
<TableEditor
306309
actions={
307310
Object {
311+
"createJob": [MockFunction],
308312
"getAccessControlFields": [MockFunction],
313+
"getChannelMembers": [MockFunction],
309314
"getChannelPolicy": [MockFunction],
310315
"getVisualAST": [MockFunction],
311316
"saveChannelPolicy": [MockFunction],
312317
"searchUsers": [MockFunction],
318+
"updateAccessControlPolicyActive": [MockFunction],
313319
}
314320
}
315321
enableUserManagedAttributes={false}

webapp/channels/src/components/admin_console/access_control/policy_details/policy_details.test.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ describe('components/admin_console/access_control/policy_details/PolicyDetails',
9090
searchUsers: jest.fn(),
9191
getChannelPolicy: jest.fn(),
9292
saveChannelPolicy: jest.fn(),
93+
getChannelMembers: jest.fn(),
94+
createJob: jest.fn(),
95+
updateAccessControlPolicyActive: jest.fn(),
9396
});
9497

9598
mockCreatePolicy.mockReset();
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2+
// See LICENSE.txt for license information.
3+
4+
.ChannelAccessRulesConfirmModal {
5+
.modal-body {
6+
padding: 0;
7+
}
8+
9+
&__summary {
10+
padding: 24px;
11+
}
12+
13+
&__message {
14+
margin-bottom: 16px;
15+
color: var(--center-channel-color);
16+
font-size: 14px;
17+
line-height: 20px;
18+
19+
strong {
20+
font-weight: 600;
21+
}
22+
}
23+
24+
&__question {
25+
margin-bottom: 24px;
26+
color: var(--center-channel-color);
27+
font-size: 14px;
28+
line-height: 20px;
29+
}
30+
31+
&__details {
32+
display: flex;
33+
height: 500px;
34+
flex-direction: column;
35+
}
36+
37+
&__tabs {
38+
display: flex;
39+
padding: 0 24px;
40+
border-bottom: 1px solid rgba(var(--center-channel-color-rgb), 0.16);
41+
}
42+
43+
&__tab {
44+
position: relative;
45+
padding: 16px 0;
46+
border: none;
47+
margin-right: 32px;
48+
background: none;
49+
color: rgba(var(--center-channel-color-rgb), 0.64);
50+
cursor: pointer;
51+
font-size: 14px;
52+
font-weight: 600;
53+
transition: color 0.15s ease;
54+
55+
&:hover {
56+
color: var(--center-channel-color);
57+
}
58+
59+
&.active {
60+
color: var(--button-bg);
61+
62+
&::after {
63+
position: absolute;
64+
right: 0;
65+
bottom: -1px;
66+
left: 0;
67+
height: 2px;
68+
background-color: var(--button-bg);
69+
content: '';
70+
}
71+
}
72+
73+
&:focus {
74+
outline: none;
75+
}
76+
}
77+
78+
&__userList {
79+
flex: 1;
80+
padding: 16px 24px 0;
81+
overflow-y: auto;
82+
83+
.more-modal__list {
84+
margin: 0;
85+
}
86+
87+
// Add spacing for the search input that comes from SearchableUserList
88+
.user-list__search,
89+
.more-modal__search {
90+
margin-bottom: 16px;
91+
}
92+
93+
// Style the search input container
94+
.search-bar__container {
95+
margin-bottom: 16px;
96+
}
97+
98+
.more-modal__row {
99+
padding: 8px 0;
100+
border-bottom: 1px solid rgba(var(--center-channel-color-rgb), 0.08);
101+
102+
&:last-child {
103+
border-bottom: none;
104+
}
105+
}
106+
107+
.more-modal__details {
108+
display: flex;
109+
align-items: center;
110+
}
111+
112+
.more-modal__name {
113+
margin-right: 8px;
114+
font-weight: 600;
115+
}
116+
117+
.more-modal__description {
118+
color: rgba(var(--center-channel-color-rgb), 0.64);
119+
}
120+
}
121+
122+
&__noResults {
123+
display: flex;
124+
height: 200px;
125+
align-items: center;
126+
justify-content: center;
127+
color: rgba(var(--center-channel-color-rgb), 0.64);
128+
font-size: 14px;
129+
}
130+
131+
&__buttons {
132+
display: flex;
133+
justify-content: flex-end;
134+
padding: 16px 24px;
135+
border-top: 1px solid rgba(var(--center-channel-color-rgb), 0.08);
136+
gap: 8px;
137+
138+
.btn {
139+
min-width: 80px;
140+
height: 32px;
141+
padding: 0 16px;
142+
border-radius: 4px;
143+
font-size: 14px;
144+
font-weight: 600;
145+
146+
&.btn-tertiary {
147+
border: 1px solid rgba(var(--center-channel-color-rgb), 0.16);
148+
background: var(--center-channel-bg);
149+
color: var(--link-color);
150+
151+
&:hover:not(:disabled) {
152+
border-color: rgba(var(--center-channel-color-rgb), 0.24);
153+
background: rgba(var(--center-channel-color-rgb), 0.04);
154+
}
155+
156+
&:disabled {
157+
cursor: not-allowed;
158+
opacity: 0.5;
159+
}
160+
}
161+
162+
&.btn-primary {
163+
border: 1px solid var(--error-text);
164+
background: var(--error-text);
165+
color: var(--button-color);
166+
167+
&:hover:not(:disabled) {
168+
border-color: var(--error-text-hover, #d24b4e);
169+
background: var(--error-text-hover, #d24b4e);
170+
}
171+
172+
&:disabled {
173+
cursor: not-allowed;
174+
opacity: 0.5;
175+
}
176+
177+
.icon-loading {
178+
margin-right: 4px;
179+
}
180+
}
181+
}
182+
}
183+
184+
// Responsive adjustments
185+
@media screen and (max-width: 768px) {
186+
&__details {
187+
height: 400px;
188+
}
189+
190+
&__tabs {
191+
padding: 0 16px;
192+
}
193+
194+
&__userList {
195+
padding: 0 16px;
196+
}
197+
198+
&__buttons {
199+
flex-wrap: wrap;
200+
padding: 12px 16px;
201+
202+
.btn {
203+
min-width: 0;
204+
flex: 1;
205+
}
206+
}
207+
}
208+
}

0 commit comments

Comments
 (0)