From 927cf00cc6f85f8ed3fc95c6a8b5417ff367e603 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 9 Sep 2023 00:41:45 +0200 Subject: [PATCH 01/33] remove duplicate --- src/components/utilities/CippFuzzySearch.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/utilities/CippFuzzySearch.js b/src/components/utilities/CippFuzzySearch.js index a97625dcd818..43021b80014e 100644 --- a/src/components/utilities/CippFuzzySearch.js +++ b/src/components/utilities/CippFuzzySearch.js @@ -13,7 +13,6 @@ function CippfuzzySearch(options) { useExtendedSearch: true, includeMatches: true, includeScore: true, - useExtendedSearch: true, }) return function (value) { if (!value.length) { From f529c982149ec35e0271b7f63f1709e4462b733a Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Sat, 9 Sep 2023 10:07:17 +0100 Subject: [PATCH 02/33] Refactor to support new extensions in the future This refactors the frontend code to allow support for additional extension mappings in the future. --- src/views/cipp/CIPPSettings.js | 37 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/views/cipp/CIPPSettings.js b/src/views/cipp/CIPPSettings.js index 09d06f708953..9f72a84401e9 100644 --- a/src/views/cipp/CIPPSettings.js +++ b/src/views/cipp/CIPPSettings.js @@ -1576,12 +1576,12 @@ const ExtensionsTab = () => { } const MappingsTab = () => { - const [listBackend, listBackendResult] = useLazyGenericGetRequestQuery() - const [setExtensionconfig, extensionConfigResult] = useLazyGenericPostRequestQuery() + const [listHaloBackend, listBackendHaloResult] = useLazyGenericGetRequestQuery() + const [setHaloExtensionconfig, extensionHaloConfigResult] = useLazyGenericPostRequestQuery() - const onSubmit = (values) => { - setExtensionconfig({ - path: 'api/ExecExtensionMapping?AddMapping=true', + const onHaloSubmit = (values) => { + setHaloExtensionconfig({ + path: 'api/ExecExtensionMapping?AddMapping=Halo', values: { mappings: values }, }) } @@ -1590,44 +1590,47 @@ const MappingsTab = () => { {listBackendResult.isUninitialized && listBackend({ path: 'api/ExecExtensionMapping?List=true' })} <> - + HaloPSA Mapping Table - {listBackendResult.isFetching ? ( + {listBackendHaloResult.isFetching ? ( ) : (
{ return ( Use the table below to map your client to the correct PSA client - {listBackendResult.isSuccess && - listBackendResult.data.Tenants.map((tenant) => ( + {listBackendHaloResult.isSuccess && + listBackendHaloResult.data.Tenants.map((tenant) => ( ))} - {extensionConfigResult.isFetching && ( + {extensionHaloConfigResult.isFetching && ( )} Set Mappings - {(extensionConfigResult.isSuccess || extensionConfigResult.isError) && ( - - {extensionConfigResult.isSuccess - ? extensionConfigResult.data.Results + {(extensionHaloConfigResult.isSuccess || + extensionHaloConfigResult.isError) && ( + + {extensionHaloConfigResult.isSuccess + ? extensionHaloConfigResult.data.Results : 'Error'} )} From 2812f32d6cb317d31949b63268bcf0a5b194bf17 Mon Sep 17 00:00:00 2001 From: rvdwegen Date: Tue, 12 Sep 2023 23:48:26 +0200 Subject: [PATCH 03/33] Make user count cards on dashboard interactable --- src/views/home/Home.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/views/home/Home.js b/src/views/home/Home.js index e3025e587fda..10cf1a03c502 100644 --- a/src/views/home/Home.js +++ b/src/views/home/Home.js @@ -143,6 +143,10 @@ const Home = () => { +
{issuccessUserCounts && !isFetchingUserCount ? dashboard?.Users : }
@@ -150,6 +154,14 @@ const Home = () => {
+
{issuccessUserCounts && !isFetchingUserCount ? dashboard?.LicUsers : }
@@ -157,6 +169,15 @@ const Home = () => {
+
{issuccessUserCounts && !isFetchingUserCount ? dashboard?.Gas : }
@@ -164,6 +185,14 @@ const Home = () => {
+
{issuccessUserCounts && !isFetchingUserCount ? dashboard?.Guests : }
From 53c9fd2ca068de94fbe0da92d48045256f3cea5e Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Thu, 14 Sep 2023 08:06:55 +0100 Subject: [PATCH 04/33] Fix load Halo Backend Result Fixed renaming a function that was missed. --- src/views/cipp/CIPPSettings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/cipp/CIPPSettings.js b/src/views/cipp/CIPPSettings.js index 9f72a84401e9..fe54e4805cc2 100644 --- a/src/views/cipp/CIPPSettings.js +++ b/src/views/cipp/CIPPSettings.js @@ -1587,8 +1587,8 @@ const MappingsTab = () => { } return (
- {listBackendResult.isUninitialized && - listBackend({ path: 'api/ExecExtensionMapping?List=true' })} + {listBackendHaloResult.isUninitialized && + listHaloBackend({ path: 'api/ExecExtensionMapping?List=Halo' })} <> From 2c19f8cab209e755a9feee8571e87bde6d6ba5d2 Mon Sep 17 00:00:00 2001 From: BNWEIN Date: Wed, 20 Sep 2023 12:42:30 +0100 Subject: [PATCH 05/33] Added Username to top of the BEC page BEC Page did not show what user it was running on. Added that information --- src/views/identity/administration/Users.js | 2 +- src/views/identity/administration/ViewBEC.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/views/identity/administration/Users.js b/src/views/identity/administration/Users.js index 4f64168fa419..928853445e6a 100644 --- a/src/views/identity/administration/Users.js +++ b/src/views/identity/administration/Users.js @@ -77,7 +77,7 @@ const Offcanvas = (row, rowIndex, formatExtraData) => { }, { label: 'Research Compromised Account', - link: `/identity/administration/ViewBec?userId=${row.id}&tenantDomain=${tenant.defaultDomainName}`, + link: `/identity/administration/ViewBec?userId=${row.id}&tenantDomain=${tenant.defaultDomainName}&ID=${row.userPrincipalName}`, color: 'info', }, { diff --git a/src/views/identity/administration/ViewBEC.js b/src/views/identity/administration/ViewBEC.js index d97e5639ae5a..bfe147e850cb 100644 --- a/src/views/identity/administration/ViewBEC.js +++ b/src/views/identity/administration/ViewBEC.js @@ -1,5 +1,5 @@ import React, { useEffect } from 'react' -import { CButton, CCallout, CLink } from '@coreui/react' +import { CButton, CCallout, CLink, CCardTitle } from '@coreui/react' import { CCardBody, CSpinner } from '@coreui/react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { @@ -27,6 +27,7 @@ import useConfirmModal from 'src/hooks/useConfirmModal' const ViewBec = () => { let query = useQuery() const userId = query.get('userId') + const userName = query.get('ID') const tenantDomain = query.get('tenantDomain') const [execBecRemediate, execRemediateResults] = useLazyGenericPostRequestQuery() const [execBecView, results] = useLazyExecBecCheckQuery() @@ -232,7 +233,7 @@ const ViewBec = () => { Business Email Compromise Overview - {userName}} button={ Date: Thu, 21 Sep 2023 20:38:57 +0200 Subject: [PATCH 06/33] Fixed action menu API calls --- src/views/identity/administration/Groups.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/views/identity/administration/Groups.js b/src/views/identity/administration/Groups.js index 49400c7206d9..5f29ef909853 100644 --- a/src/views/identity/administration/Groups.js +++ b/src/views/identity/administration/Groups.js @@ -161,7 +161,7 @@ const Groups = () => { label: 'Hide from Global Address List', color: 'info', modal: true, - modalUrl: `/api/ExecGroupsHideFromGAL?TenantFilter=${tenant.defaultDomainName}&ID=!mail&GroupType=!calculatedGroupType&HidefromGAL=true`, + modalUrl: `/api/ExecGroupsHideFromGAL?TenantFilter=${tenant.defaultDomainName}&ID=!id&GroupType=!calculatedGroupType&HidefromGAL=true`, modalMessage: 'Are you sure you want to hide this mailbox from the global address list? Remember this will not work if the group is AD Synched.', }, @@ -169,7 +169,7 @@ const Groups = () => { label: 'Unhide from Global Address List', color: 'info', modal: true, - modalUrl: `/api/ExecGroupsHideFromGAL?TenantFilter=${tenant.defaultDomainName}&ID=!mail&GroupType=!calculatedGroupType`, + modalUrl: `/api/ExecGroupsHideFromGAL?TenantFilter=${tenant.defaultDomainName}&ID=!id&GroupType=!calculatedGroupType`, modalMessage: 'Are you sure you want to unhide this mailbox from the global address list? Remember this will not work if the group is AD Synched.', }, @@ -177,7 +177,7 @@ const Groups = () => { label: 'Only allow messages from people inside the organisation', color: 'info', modal: true, - modalUrl: `/api/ExecGroupsDeliveryManagement?TenantFilter=${tenant.defaultDomainName}&ID=!mail&GroupType=!calculatedGroupType&OnlyAllowInternal=true`, + modalUrl: `/api/ExecGroupsDeliveryManagement?TenantFilter=${tenant.defaultDomainName}&ID=!id&GroupType=!calculatedGroupType&OnlyAllowInternal=true`, modalMessage: 'Are you sure you want to only allow messages from people inside the organisation? Remember this will not work if the group is AD Synched.', }, @@ -185,7 +185,7 @@ const Groups = () => { label: 'Allow messages from people inside and outside the organisation', color: 'info', modal: true, - modalUrl: `/api/ExecGroupsDeliveryManagement?TenantFilter=${tenant.defaultDomainName}&ID=!mail&GroupType=!calculatedGroupType`, + modalUrl: `/api/ExecGroupsDeliveryManagement?TenantFilter=${tenant.defaultDomainName}&ID=!id&GroupType=!calculatedGroupType`, modalMessage: 'Are you sure you want to allow messages from people inside and outside the organisation? Remember this will not work if the group is AD Synched.', }, @@ -193,7 +193,7 @@ const Groups = () => { label: 'Delete Group', color: 'warning', modal: true, - modalUrl: `/api/ExecGroupsDelete?TenantFilter=${tenant.defaultDomainName}&ID=!mail&GroupType=!calculatedGroupType&DisplayName=!displayName`, + modalUrl: `/api/ExecGroupsDelete?TenantFilter=${tenant.defaultDomainName}&ID=!id&GroupType=!calculatedGroupType&DisplayName=!displayName`, modalMessage: 'Are you sure you want to delete this group.', }, ], From e3c8737a53ddd746a674650d01f1730d4d442d3b Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 21 Sep 2023 20:51:49 +0200 Subject: [PATCH 07/33] added automatic help to each page --- src/components/layout/AppHeader.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/components/layout/AppHeader.js b/src/components/layout/AppHeader.js index bfda84ceee1b..0bc158732883 100644 --- a/src/components/layout/AppHeader.js +++ b/src/components/layout/AppHeader.js @@ -11,6 +11,7 @@ import { CHeaderToggler, CImage, CSidebarBrand, + CButton, } from '@coreui/react' import { AppHeaderDropdown, AppHeaderSearch } from 'src/components/header' import { TenantSelector } from '../utilities' @@ -22,8 +23,12 @@ import { faCaretSquareLeft, faCaretSquareRight } from '@fortawesome/free-solid-s import { toggleSidebarShow } from 'src/store/features/app' import { useMediaPredicate } from 'react-media-hook' import { useLoadAlertsDashQuery } from 'src/store/api/app' +import { Link } from 'react-router-dom' +import { useLocation } from 'react-router-dom' + const AppHeader = () => { const dispatch = useDispatch() + const location = useLocation() const sidebarShow = useSelector((state) => state.app.sidebarShow) const currentTheme = useSelector((state) => state.app.currentTheme) const preferredTheme = useMediaPredicate('(prefers-color-scheme: dark)') ? 'impact' : 'cyberdrain' @@ -57,6 +62,16 @@ const AppHeader = () => { + + + + + + + From 1aa388b8c649bc6ecf2f269fbe9bba05c73685e5 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 21 Sep 2023 22:52:56 +0200 Subject: [PATCH 08/33] fixed mailbox permissions overview --- .../administration/EditMailboxPermissions.js | 952 +++++++++--------- 1 file changed, 459 insertions(+), 493 deletions(-) diff --git a/src/views/email-exchange/administration/EditMailboxPermissions.js b/src/views/email-exchange/administration/EditMailboxPermissions.js index 2841cf1c4d41..b0649954994b 100644 --- a/src/views/email-exchange/administration/EditMailboxPermissions.js +++ b/src/views/email-exchange/administration/EditMailboxPermissions.js @@ -16,7 +16,6 @@ import { CSpinner, } from '@coreui/react' import useQuery from 'src/hooks/useQuery' -import { CippPage, CippPageList, CippMasonry, CippMasonryItem } from 'src/components/layout' import { useDispatch } from 'react-redux' import { Form, Field } from 'react-final-form' import { RFFSelectSearch, RFFCFormSelect, RFFCFormCheck, RFFCFormInput } from 'src/components/forms' @@ -38,38 +37,131 @@ const formatter = (cell, warning = false, reverse = false, colourless = false) = CellBoolean({ cell, warning, reverse, colourless }) const MailboxSettings = () => { + const dispatch = useDispatch() + let query = useQuery() + const userId = query.get('userId') + const tenantDomain = query.get('tenantDomain') const [active, setActive] = useState(1) + const { + data: usercal = {}, + isFetching: usercalIsFetching, + error: usercalError, + } = useListCalendarPermissionsQuery({ tenantDomain, userId }) + const columnsCal = [ + { + name: 'User', + selector: (row) => row['User'], + sortable: true, + wrap: true, + cell: (row) => row['User'], + exportSelector: 'User', + maxWidth: '150px', + }, + { + name: 'AccessRights', + selector: (row) => row['AccessRights'], + sortable: true, + wrap: true, + cell: (row) => row['AccessRights'], + exportSelector: 'AccessRights', + maxWidth: '150px', + }, + { + name: 'Identity', + selector: (row) => row['Identity'], + sortable: true, + wrap: true, + cell: (row) => row['Identity'], + exportSelector: 'Identity', + maxWidth: '150px', + }, + ] + const columns = [ + { + name: 'User', + selector: (row) => row.User, + sortable: true, + wrap: true, + exportSelector: 'User', + }, + { + name: 'Permissions', + selector: (row) => row['Permissions'], + sortable: true, + wrap: true, + exportSelector: 'Permissions', + }, + ] + + const { + data: user = {}, + isFetching: userIsFetching, + error: userError, + } = useListMailboxPermissionsQuery({ tenantDomain, userId }) + return ( - - - - - setActive(1)} href="#"> - Mailbox Permissions - - setActive(2)} href="#"> - Calendar Permissions - - setActive(3)} href="#"> - Mailbox Forwarding - - - - - - - - - - - - - - - - - - + + + + + + setActive(1)} href="#"> + Mailbox Permissions + + setActive(2)} href="#"> + Calendar Permissions + + setActive(3)} href="#"> + Mailbox Forwarding + + + + + + + + + + + + + + + + + + + + + + Account Information - {userId} + + + {active === 1 && ( + <> + {userIsFetching && } + {!userIsFetching && ( + + )} + + )} + {active === 2 && ( + <> + {usercalIsFetching && } + {!usercalIsFetching && ( + + )} + + )} + {active === 3 && ( + <> + + + )} + + + + ) } @@ -86,13 +178,6 @@ const MailboxPermissions = () => { //const [EditMailboxPermission, { error: EditMailboxPermissionError, isFetching: EditMailboxPermissionIsFetching }] = useEditMailboxPermissionMutation() const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() - const { - data: user = {}, - isFetching: userIsFetching, - error: userError, - refetch: refetchPermissions, - } = useListMailboxPermissionsQuery({ tenantDomain, userId }) - const { data: users = [], isFetching: usersIsFetching, @@ -100,9 +185,6 @@ const MailboxPermissions = () => { } = useListUsersQuery({ tenantDomain }) useEffect(() => { - if (postResults.isSuccess) { - refetchPermissions() - } if (!userId || !tenantDomain) { ModalService.open({ body: 'Error invalid request, could not load requested user.', @@ -112,7 +194,7 @@ const MailboxPermissions = () => { } else { setQueryError(false) } - }, [userId, tenantDomain, dispatch, postResults, refetchPermissions]) + }, [userId, tenantDomain, dispatch, postResults]) const onSubmit = (values) => { const shippedValues = { userid: userId, @@ -128,36 +210,16 @@ const MailboxPermissions = () => { //window.alert(JSON.stringify(shippedValues)) genericPostRequest({ path: '/api/ExecEditMailboxPermissions', values: shippedValues }) } - const initialState = { - ...user, - } - - const columns = [ - { - name: 'User', - selector: (row) => row.User, - sortable: true, - wrap: true, - exportSelector: 'User', - }, - { - name: 'Permissions', - selector: (row) => row['Permissions'], - sortable: true, - wrap: true, - exportSelector: 'Permissions', - }, - ] const formDisabled = queryError === true return ( - + <> {!queryError && ( <> {queryError && ( - + {/* @todo add more descriptive help message here */} Failed to load user @@ -166,203 +228,147 @@ const MailboxPermissions = () => { )} - - - - Account Details - {userId} - - - {usersIsFetching && } - {userError && Error loading user} - {!usersIsFetching && ( - { - return ( - - - - ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, - }))} - placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} - name="RemoveFullAccess" - /> - {usersError && Failed to load list of users} - - - ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, - }))} - placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} - name="AddFullAccess" - /> - {usersError && Failed to load list of users} - - - ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, - }))} - placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} - name="AddFullAccessNoAutoMap" - /> - {usersError && Failed to load list of users} - - - ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, - }))} - placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} - name="AddSendAs" - /> - {usersError && Failed to load list of users} - - - ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, - }))} - placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} - name="RemoveSendAs" - /> - {usersError && Failed to load list of users} - - - ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, - }))} - placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} - name="AddSendOnBehalf" - /> - {usersError && Failed to load list of users} - - - ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, - }))} - placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} - name="RemoveSendOnBehalf" - /> - {usersError && Failed to load list of users} - - - - - - Edit User Permissions - {postResults.isFetching && ( - - )} - - - - {postResults.isSuccess && ( - - {postResults.data.Results.map((result, idx) => ( -
  • {result}
  • - ))} -
    + {usersIsFetching && } + {!usersIsFetching && ( + { + return ( + + + + ({ + value: user.mail, + name: `${user.displayName} - ${user.mail} `, + }))} + placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} + name="RemoveFullAccess" + /> + {usersError && Failed to load list of users} + + + ({ + value: user.mail, + name: `${user.displayName} - ${user.mail} `, + }))} + placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} + name="AddFullAccess" + /> + {usersError && Failed to load list of users} + + + ({ + value: user.mail, + name: `${user.displayName} - ${user.mail} `, + }))} + placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} + name="AddFullAccessNoAutoMap" + /> + {usersError && Failed to load list of users} + + + ({ + value: user.mail, + name: `${user.displayName} - ${user.mail} `, + }))} + placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} + name="AddSendAs" + /> + {usersError && Failed to load list of users} + + + ({ + value: user.mail, + name: `${user.displayName} - ${user.mail} `, + }))} + placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} + name="RemoveSendAs" + /> + {usersError && Failed to load list of users} + + + ({ + value: user.mail, + name: `${user.displayName} - ${user.mail} `, + }))} + placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} + name="AddSendOnBehalf" + /> + {usersError && Failed to load list of users} + + + ({ + value: user.mail, + name: `${user.displayName} - ${user.mail} `, + }))} + placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} + name="RemoveSendOnBehalf" + /> + {usersError && Failed to load list of users} + + + + + + Edit User Permissions + {postResults.isFetching && ( + )} - - ) - }} - /> - )} -
    -
    -
    - - - - Account Information - - - {userIsFetching && } - {!userIsFetching && ( - <> - - - )} - - - +
    + + + {postResults.isSuccess && ( + + {postResults.data.Results.map((result, idx) => ( +
  • {result}
  • + ))} +
    + )} + + ) + }} + /> + )} )} -
    + ) } -const columns = [ - { - name: 'User', - selector: (row) => row['User'], - sortable: true, - wrap: true, - cell: (row) => row['User'], - exportSelector: 'User', - maxWidth: '150px', - }, - { - name: 'AccessRights', - selector: (row) => row['AccessRights'], - sortable: true, - wrap: true, - cell: (row) => row['AccessRights'], - exportSelector: 'AccessRights', - maxWidth: '150px', - }, - { - name: 'Identity', - selector: (row) => row['Identity'], - sortable: true, - wrap: true, - cell: (row) => row['Identity'], - exportSelector: 'Identity', - maxWidth: '150px', - }, -] - const CalendarPermissions = () => { const dispatch = useDispatch() let query = useQuery() @@ -419,7 +425,7 @@ const CalendarPermissions = () => { UsersMapped.unshift({ value: 'Default', name: 'Default' }) return ( - + <> {!queryError && ( <> {postResults.isSuccess && ( @@ -436,109 +442,85 @@ const CalendarPermissions = () => { )} - - - - Account Details - - - {userIsFetching && } - {userError && Error loading user} - {!userIsFetching && ( - { - return ( - - - - - {usersError && Failed to load list of users} - - - - {usersError && Failed to load list of users} - - - + {userIsFetching && } + {userError && Error loading user} + {!userIsFetching && ( + { + return ( + + + + + {usersError && Failed to load list of users} + + + + {usersError && Failed to load list of users} + + + + {usersError && Failed to load list of users} + + + + + + Edit Permissions + {postResults.isFetching && ( + - {usersError && Failed to load list of users} - - - - - - Edit Permissions - {postResults.isFetching && ( - - )} - - - - {postResults.isSuccess && ( - {postResults.data?.Results} - )} - - ) - }} - /> - )} - - - - - - - Current Permissions - - - {userIsFetching && } - {!userIsFetching && ( - <> - {user.length > 0 && ( - - )} - - )} - - + )} + + + + {postResults.isSuccess && ( + {postResults.data?.Results} + )} + + ) + }} + /> + )} )} - + ) } @@ -557,7 +539,6 @@ const MailboxForwarding = () => { data: user = {}, isFetching: userIsFetching, error: userError, - refetch: refetchPermissions, } = useListMailboxPermissionsQuery({ tenantDomain, userId }) const { @@ -568,7 +549,6 @@ const MailboxForwarding = () => { useEffect(() => { if (postResults.isSuccess) { - refetchPermissions() } if (!userId || !tenantDomain) { ModalService.open({ @@ -579,7 +559,7 @@ const MailboxForwarding = () => { } else { setQueryError(false) } - }, [userId, tenantDomain, dispatch, postResults, refetchPermissions]) + }, [userId, tenantDomain, dispatch, postResults]) const onSubmit = (values) => { const shippedValues = { userid: userId, @@ -616,7 +596,7 @@ const MailboxForwarding = () => { const formDisabled = queryError === true return ( - + <> {!queryError && ( <> {queryError && ( @@ -630,139 +610,122 @@ const MailboxForwarding = () => { )} - - - - Account Details - {userId} - - - {usersIsFetching && } - {userError && Error loading user} - {!usersIsFetching && ( - { - return ( - - - -
    - -
    - {values.forwardOption === 'internalAddress' && ( - ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, - }))} - placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} - name="ForwardInternal" - /> - )} - {usersError && Failed to load list of users} -
    -
    - - -
    - -
    - {values.forwardOption === 'ExternalAddress' && ( - - )} -
    -
    - - -
    - -
    -
    -
    - - - - - Edit Forwarding - {postResults.isFetching && ( - - )} - - - - {postResults.isSuccess && ( - - {postResults.data.Results.map((result, idx) => ( -
  • {result}
  • - ))} -
    + + {usersIsFetching && } + {userError && Error loading user} + {!usersIsFetching && ( + { + return ( + + + +
    + +
    + {values.forwardOption === 'internalAddress' && ( + ({ + value: user.mail, + name: `${user.displayName} - ${user.mail} `, + }))} + placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} + name="ForwardInternal" + /> )} -
    - ) - }} - /> - )} -
    -
    -
    - - - - Account Details - {userId} - - - - - + {usersError && Failed to load list of users} + +
    + + +
    + +
    + {values.forwardOption === 'ExternalAddress' && ( + + )} +
    +
    + + +
    + +
    +
    +
    + + + + + Edit Forwarding + {postResults.isFetching && ( + + )} + + + + {postResults.isSuccess && ( + + {postResults.data.Results.map((result, idx) => ( +
  • {result}
  • + ))} +
    + )} + + ) + }} + /> + )} )} -
    + ) } @@ -784,14 +747,17 @@ const ForwardingSettings = () => { return ( - - {content.map((item, index) => ( -
    -
    {item.heading}
    -

    {item.body}

    -
    - ))} -
    + {isFetching && } + {!isFetching && ( + + {content.map((item, index) => ( +
    +
    {item.heading}
    +

    {item.body}

    +
    + ))} +
    + )}
    ) } From 8849b22cc83a8253a5b120dc1f6613ff8682a2b3 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 21 Sep 2023 17:23:19 -0400 Subject: [PATCH 09/33] Webhooks - Add Service Principal operations --- src/views/tenant/administration/AlertWizard.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/views/tenant/administration/AlertWizard.js b/src/views/tenant/administration/AlertWizard.js index b0d8a9dbdfbf..28bf204948d5 100644 --- a/src/views/tenant/administration/AlertWizard.js +++ b/src/views/tenant/administration/AlertWizard.js @@ -222,6 +222,14 @@ const AlertWizard = () => { value: 'UserLoggedIn', name: 'A user has logged in from any location', }, + { + value: 'Add service principal.', + name: 'Enterprise App Added', + }, + { + value: 'Remove service principal.', + name: 'Enterprise App Removed', + }, ]} /> From 8032a4b42f2144cd6033d0e0e2c53af0addc52a8 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 22 Sep 2023 13:35:25 +0200 Subject: [PATCH 10/33] added "see more" to home --- src/views/home/Home.js | 62 ++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/src/views/home/Home.js b/src/views/home/Home.js index e3025e587fda..5429c137e258 100644 --- a/src/views/home/Home.js +++ b/src/views/home/Home.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useState } from 'react' import { faBook, faCog, @@ -16,7 +16,7 @@ import { faUsers, faServer, } from '@fortawesome/free-solid-svg-icons' -import { CCol, CRow } from '@coreui/react' +import { CButton, CCol, CCollapse, CRow } from '@coreui/react' import { useGenericGetRequestQuery } from 'src/store/api/app' import { CippContentCard } from 'src/components/layout' import Skeleton from 'react-loading-skeleton' @@ -30,6 +30,8 @@ import Portals from 'src/data/portals' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' const Home = () => { + const [visible, setVisible] = useState(false) + const currentTenant = useSelector((state) => state.app.currentTenant) const { data: organization, @@ -62,7 +64,7 @@ const Home = () => { }) const { - data: standards, + data: standards = [], isLoading: isLoadingStandards, isSuccess: issuccessStandards, isFetching: isFetchingStandards, @@ -124,6 +126,21 @@ const Home = () => { icon: faUserFriends, }, ] + + const filteredStandards = standards + .filter( + (p) => p.displayName === 'AllTenants' || p.displayName === currentTenant.defaultDomainName, + ) + .flatMap((tenant) => { + return Object.keys(tenant.standards).map((standard) => { + const standardDisplayname = allStandardsList.filter((p) => p.name.includes(standard)) + return ( +
  • + {standardDisplayname[0]?.label} ({tenant.displayName}) +
  • + ) + }) + }) return ( <> @@ -261,26 +278,25 @@ const Home = () => {

    Applied Standards

    {(isLoadingStandards || isFetchingStandards) && } - {issuccessStandards && - !isFetchingStandards && - standards - .filter( - (p) => - p.displayName == 'AllTenants' || - p.displayName == currentTenant.defaultDomainName, - ) - .flatMap((tenant) => { - return Object.keys(tenant.standards).map((standard) => { - const standardDisplayname = allStandardsList.filter((p) => - p.name.includes(standard), - ) - return ( -
  • - {standardDisplayname[0]?.label} ({tenant.displayName}) -
  • - ) - }) - })} + + {issuccessStandards && !isFetchingStandards && ( + <> + {filteredStandards.slice(0, 5)} + + {filteredStandards.length > 5 && ( + <> + {filteredStandards.slice(5)} + setVisible(!visible)} + > + {visible ? 'See less' : 'See more...'} + + + )} + + )}

    Partner Relationships

    From b4e18a8cba0ae559980b07f3e1b761d8d398e6a0 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 23 Sep 2023 23:12:44 +0200 Subject: [PATCH 11/33] Fixed refresh issue --- src/views/cipp/CIPPSettings.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/cipp/CIPPSettings.js b/src/views/cipp/CIPPSettings.js index fe54e4805cc2..00deb2d85098 100644 --- a/src/views/cipp/CIPPSettings.js +++ b/src/views/cipp/CIPPSettings.js @@ -1069,6 +1069,7 @@ const NotificationsSettings = () => { true} initialValues={{ ...notificationListResult.data, logsToInclude: notificationListResult.data?.logsToInclude?.map((m) => ({ From 53100dbaf5d3b28cee798120ab700e548cbfe621 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 23 Sep 2023 23:12:57 +0200 Subject: [PATCH 12/33] add unix time handle --- src/components/tables/CellDate.js | 51 ++++++++++++++----------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src/components/tables/CellDate.js b/src/components/tables/CellDate.js index 9806f137c290..04f7bf9c5f3d 100644 --- a/src/components/tables/CellDate.js +++ b/src/components/tables/CellDate.js @@ -4,13 +4,6 @@ import PropTypes from 'prop-types' import { CTooltip } from '@coreui/react' import ReactTimeAgo from 'react-time-ago' -/** - * - * @param format ['short', 'long', 'relative'] - * @param value - * @returns {JSX.Element} - * @constructor - */ export const CellDate = ({ format = 'short', showTime = true, showDate = true, cell }) => { if (!cell || (!showTime && !showDate)) { return
    @@ -21,25 +14,29 @@ export const CellDate = ({ format = 'short', showTime = true, showDate = true, c locale = navigator.language } - // cheatsheet - // https://devhints.io/wip/intl-datetime - const dateTimeArgs = [ - [locale, 'default'], // add fallback option if locale doesn't load properly - ] + // Convert cell value to a number and check if it's a Unix timestamp + const possibleUnixTimestamp = Number(cell) + const isUnixTimestamp = !isNaN(possibleUnixTimestamp) && possibleUnixTimestamp > 1000000000 + let dateObject + + if (isUnixTimestamp) { + dateObject = moment.unix(possibleUnixTimestamp).toDate() + } else { + dateObject = moment(cell).toDate() + } + + const dateTimeArgs = [[locale, 'default']] const dateTimeFormatOptions = {} - if (format == 'relative') { + + if (format === 'relative') { try { - return ( - - - - ) + return } catch (error) { - console.error('Error formatting date, fallback to string value', { date: cell, error }) + console.error('Error formatting date, fallback to string value', { date: dateObject, error }) return ( - -
    {String(cell)}
    + +
    {String(dateObject)}
    ) } @@ -56,16 +53,14 @@ export const CellDate = ({ format = 'short', showTime = true, showDate = true, c let formatted try { - // lots of dates returned are unreliably parsable (e.g. non ISO8601 format) - // fallback using moment to parse into date object - formatted = new Intl.DateTimeFormat(...dateTimeArgs).format(moment(cell).toDate()) + formatted = new Intl.DateTimeFormat(...dateTimeArgs).format(dateObject) } catch (error) { - console.error('Error formatting date, fallback to string value', { date: cell, error }) - formatted = cell + console.error('Error formatting date, fallback to string value', { date: dateObject, error }) + formatted = dateObject.toString() } return ( - +
    {String(formatted)}
    ) @@ -74,7 +69,7 @@ export const CellDate = ({ format = 'short', showTime = true, showDate = true, c CellDate.propTypes = { format: PropTypes.oneOf(['short', 'medium', 'long', 'full', 'relative']), - cell: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), + cell: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.instanceOf(Date)]), showTime: PropTypes.bool, showDate: PropTypes.bool, } From 4fcc8380e30f8e7b90af4927763ca4d169757284 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 23 Sep 2023 23:13:12 +0200 Subject: [PATCH 13/33] add text cases for badges --- src/components/tables/CellBadge.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/components/tables/CellBadge.js b/src/components/tables/CellBadge.js index 5234f65e2fca..26759c874f60 100644 --- a/src/components/tables/CellBadge.js +++ b/src/components/tables/CellBadge.js @@ -3,6 +3,24 @@ import React from 'react' import { CBadge } from '@coreui/react' export const CellBadge = ({ label = '', color = '', children, ...rest }) => { + //Create a case select, and return the color based on the label + switch (label.toLowerCase()) { + case 'planned': + color = 'info' + break + case 'failed': + color = 'danger' + break + case 'completed': + color = 'success' + break + case 'Banned': + color = 'danger' + break + default: + color = 'primary' + } + return ( {label} From 067057dbb9d4b1251097eea4c3e9e8acb1a96434 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 23 Sep 2023 23:14:20 +0200 Subject: [PATCH 14/33] fix stuff --- src/components/tables/CellBadge.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/tables/CellBadge.js b/src/components/tables/CellBadge.js index 26759c874f60..ae6b948ce83f 100644 --- a/src/components/tables/CellBadge.js +++ b/src/components/tables/CellBadge.js @@ -14,10 +14,10 @@ export const CellBadge = ({ label = '', color = '', children, ...rest }) => { case 'completed': color = 'success' break - case 'Banned': + case 'banned': color = 'danger' break - default: + case 'running': color = 'primary' } From 6ef5addd9713554e8424804cba41ef42f94e5193 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sat, 23 Sep 2023 23:16:43 +0200 Subject: [PATCH 15/33] added scheduler --- src/_nav.js | 5 + src/adminRoutes.js | 3 + src/components/utilities/CippCodeOffcanvas.js | 37 +- src/views/cipp/Scheduler.js | 337 ++++++++++++++++++ 4 files changed, 368 insertions(+), 14 deletions(-) create mode 100644 src/views/cipp/Scheduler.js diff --git a/src/_nav.js b/src/_nav.js index 2f8d05e25f85..c243b18c4e12 100644 --- a/src/_nav.js +++ b/src/_nav.js @@ -663,6 +663,11 @@ const _nav = [ name: 'Settings', to: '/cipp/settings', }, + { + component: CNavItem, + name: 'Scheduler', + to: '/cipp/scheduler', + }, { component: CNavItem, name: 'SAM Setup Wizard', diff --git a/src/adminRoutes.js b/src/adminRoutes.js index b57360064384..8b10173655ab 100644 --- a/src/adminRoutes.js +++ b/src/adminRoutes.js @@ -11,12 +11,15 @@ const GDAPRelationships = React.lazy(() => import('./views/tenant/administration/ListGDAPRelationships'), ) const appapproval = React.lazy(() => import('src/views/cipp/AppApproval')) +const Scheduler = React.lazy(() => import('src/views/cipp/Scheduler')) const adminRoutes = [ { path: '/cipp', name: 'CIPP' }, { path: '/cipp/cipp', name: 'CIPP' }, { path: '/cipp/settings', name: 'Settings', component: CIPPSettings }, { path: '/cipp/setup', name: 'Setup', component: Setup }, + { path: '/cipp/scheduler', name: 'Scheduler', component: Scheduler }, + { path: '/tenant/administration/gdap', name: 'GDAP Wizard', component: GDAP }, { path: '/tenant/administration/gdap-invite', name: 'GDAP Invite Wizard', component: GDAPInvite }, { diff --git a/src/components/utilities/CippCodeOffcanvas.js b/src/components/utilities/CippCodeOffcanvas.js index c8cdbf175d35..38a23b81663f 100644 --- a/src/components/utilities/CippCodeOffcanvas.js +++ b/src/components/utilities/CippCodeOffcanvas.js @@ -6,7 +6,14 @@ import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 's import { Editor } from '@monaco-editor/react' import { useSelector } from 'react-redux' -function CippCodeOffCanvas({ row, state, hideFunction, type }) { +function CippCodeOffCanvas({ + row, + state, + hideFunction, + type, + title = 'Template JSON', + hideButton = false, +}) { const [SaveTemplate, templateDetails] = useLazyGenericPostRequestQuery() const currentTheme = useSelector((state) => state.app.currentTheme) const [templateData, setFormData] = useState(row) @@ -23,7 +30,7 @@ function CippCodeOffCanvas({ row, state, hideFunction, type }) { return ( <> - - SaveTemplate({ - path: `/api/ExecEditTemplate?type=${type}`, - method: 'POST', - values: templateData, - }) - } - > - Save changes {templateDetails.isFetching && } - + {!hideButton && ( + + SaveTemplate({ + path: `/api/ExecEditTemplate?type=${type}`, + method: 'POST', + values: templateData, + }) + } + > + Save changes {templateDetails.isFetching && } + + )} {templateDetails.isSuccess && !templateDetails.isFetching && ( diff --git a/src/views/cipp/Scheduler.js b/src/views/cipp/Scheduler.js new file mode 100644 index 000000000000..e0af97bcb8c8 --- /dev/null +++ b/src/views/cipp/Scheduler.js @@ -0,0 +1,337 @@ +import React, { useEffect, useState } from 'react' +import { CButton, CCallout, CCol, CForm, CFormLabel, CRow, CSpinner, CTooltip } from '@coreui/react' +import useQuery from 'src/hooks/useQuery' +import { useDispatch, useSelector } from 'react-redux' +import { Field, Form } from 'react-final-form' +import { + Condition, + RFFCFormCheck, + RFFCFormInput, + RFFCFormSwitch, + RFFCFormTextarea, + RFFSelectSearch, +} from 'src/components/forms' +import countryList from 'src/data/countryList' + +import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCircleNotch, faEdit, faEye } from '@fortawesome/free-solid-svg-icons' +import { CippContentCard, CippPage, CippPageList } from 'src/components/layout' +import { password } from 'src/validators' +import { + CellDate, + CellDelegatedPrivilege, + cellBadgeFormatter, + cellBooleanFormatter, + cellDateFormatter, +} from 'src/components/tables' +import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGenericFormat' +import DatePicker from 'react-datepicker' +import 'react-datepicker/dist/react-datepicker.css' +import TenantListSelector from 'src/components/utilities/TenantListSelector' +import { ModalService, TenantSelector } from 'src/components/utilities' +import CippCodeOffCanvas from 'src/components/utilities/CippCodeOffcanvas' + +const Offcanvas = (row, rowIndex, formatExtraData) => { + const [ExecuteGetRequest, getResults] = useLazyGenericGetRequestQuery() + const [ocVisible, setOCVisible] = useState(false) + + const handleDeleteSchedule = (apiurl, message) => { + ModalService.confirm({ + title: 'Confirm', + body:
    {message}
    , + onConfirm: () => ExecuteGetRequest({ path: apiurl }), + confirmLabel: 'Continue', + cancelLabel: 'Cancel', + }) + } + let jsonResults + try { + jsonResults = JSON.parse(row.Results) + } catch (error) { + jsonResults = row.Results + } + + return ( + <> + + setOCVisible(true)}> + + + + + + handleDeleteSchedule( + `/api/RemoveScheduledItem?&ID=${row.RowKey}`, + 'Do you want to delete this job?', + ) + } + size="sm" + variant="ghost" + color="danger" + > + + + + setOCVisible(false)} + /> + + ) +} + +const Scheduler = () => { + const currentDate = new Date() + const [startDate, setStartDate] = useState(currentDate) + const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) + const [refreshState, setRefreshState] = useState(false) + const taskName = `Scheduled Task ${currentDate.toLocaleString()}` + + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + const onSubmit = (values) => { + const unixTime = Math.floor(startDate.getTime() / 1000) + const shippedValues = { + TenantFilter: tenantDomain, + Name: values.taskName, + Command: values.command, + Parameters: values.parameters, + ScheduledTime: unixTime, + Recurrence: values.Recurrence, + PostExecution: { + Webhook: values.webhook, + Email: values.email, + PSA: values.psa, + }, + } + genericPostRequest({ path: '/api/AddScheduledItem', values: shippedValues }).then((res) => { + setRefreshState(res.requestId) + console.log(res.requestId) + }) + } + + const columns = [ + { + name: 'Name', + selector: (row) => row['Name'], + sortable: true, + cell: (row) => CellTip(row['Name']), + exportSelector: 'Name', + }, + { + name: 'Tenant', + selector: (row) => row['Tenant'], + sortable: true, + cell: (row) => CellTip(row['Tenant']), + exportSelector: 'Tenant', + }, + { + name: 'Task State', + selector: (row) => row['TaskState'], + sortable: true, + cell: cellBadgeFormatter(), + exportSelector: 'TaskState', + }, + { + name: 'Command', + selector: (row) => row['Command'], + sortable: true, + cell: (row) => CellTip(row['Command']), + exportSelector: 'Command', + }, + { + name: 'Parameters', + selector: (row) => row['Parameters'], + sortable: true, + cell: (row) => CellTip(row['Parameters']), + exportSelector: 'Parameters', + }, + { + name: 'Scheduled Time', + selector: (row) => row['ScheduledTime'], + sortable: true, + cell: cellDateFormatter({ format: 'relative' }), + exportSelector: 'ScheduledTime', + }, + { + name: 'Last executed time', + selector: (row) => row['ExecutedTime'], + sortable: true, + cell: cellDateFormatter({ format: 'relative' }), + exportSelector: 'ExecutedTime', + }, + { + name: 'Recurrence', + selector: (row) => row['Recurrence'], + sortable: true, + cell: (row) => CellTip(row['Recurrence']), + exportSelector: 'Recurrence', + }, + { + name: 'Sending to', + selector: (row) => row['PostExecution'], + sortable: true, + cell: (row) => CellTip(row['PostExecution']), + exportSelector: 'PostExecution', + }, + { + name: 'Actions', + cell: Offcanvas, + maxWidth: '80px', + }, + ] + return ( + + <> + + + + true} + render={({ handleSubmit, submitting, values }) => { + return ( + + + + + {(props) => } + + + + + + + + + + + setStartDate(date)} + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Add Schedule + {postResults.isFetching && ( + + )} + + + + {postResults.isSuccess && ( + +
  • {postResults.data.Results}
  • +
    + )} +
    + ) + }} + /> +
    +
    + + + +
    + +
    + ) +} + +export default Scheduler From 5ea3937738e255c9872c5758893be18630efb57d Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 24 Sep 2023 02:50:26 +0200 Subject: [PATCH 16/33] added allow initial value for checkboxes so we always send what we need. --- src/components/forms/RFFComponents.js | 3 +- src/views/cipp/Scheduler.js | 73 ++++++++++++++++++++++----- 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/src/components/forms/RFFComponents.js b/src/components/forms/RFFComponents.js index a360ec2fc49e..9aef7ca81c35 100644 --- a/src/components/forms/RFFComponents.js +++ b/src/components/forms/RFFComponents.js @@ -88,9 +88,10 @@ export const RFFCFormSwitch = ({ className = 'mb-3', validate, disabled = false, + initialValue, }) => { return ( - + {({ meta, input }) => ( { const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) const [refreshState, setRefreshState] = useState(false) const taskName = `Scheduled Task ${currentDate.toLocaleString()}` - + const { data: availableCommands = [], isLoading: isLoadingcmd } = useGenericGetRequestQuery({ + path: 'api/ListFunctionParameters?Module=CIPPCore', + }) const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() const onSubmit = (values) => { const unixTime = Math.floor(startDate.getTime() / 1000) @@ -111,7 +118,6 @@ const Scheduler = () => { } genericPostRequest({ path: '/api/AddScheduledItem', values: shippedValues }).then((res) => { setRefreshState(res.requestId) - console.log(res.requestId) }) } @@ -247,22 +253,61 @@ const Scheduler = () => { ({ + value: cmd.Function, + name: cmd.Function, + }))} name="command" - placeholder="Select a command or report to execute." + placeholder={ + isLoadingcmd ? ( + + ) : ( + 'Select a command or report to execute.' + ) + } label="Command to execute" /> - - - + + {(props) => { + const selectedCommand = availableCommands.find( + (cmd) => cmd.Function === props.values.command?.value, + ) + let paramblock = null + if (selectedCommand) { + //if the command parameter type is boolean we use else . + const parameters = selectedCommand.Parameters + if (parameters.length > 0) { + paramblock = parameters.map((param, idx) => ( + + + {param.Type === 'System.Boolean' ? ( + <> + + + + ) : ( + + )} + + + )) + } + } + return paramblock + }} + From d9be03c6642a082013f92496325cdc99b889ede7 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 24 Sep 2023 17:53:04 +0200 Subject: [PATCH 17/33] add advanced filtering. --- src/components/tables/CippTable.js | 59 ++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/src/components/tables/CippTable.js b/src/components/tables/CippTable.js index fa11d257ea78..949bfb167989 100644 --- a/src/components/tables/CippTable.js +++ b/src/components/tables/CippTable.js @@ -199,11 +199,64 @@ export default function CippTable({ } } const [resetPaginationToggle, setResetPaginationToggle] = React.useState(false) - const filteredItems = Array.isArray(data) - ? data.filter( + // Helper function to escape special characters in a string for regex + function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + } + + const filterData = (data, filterText) => { + if (filterText.startsWith('Complex:')) { + const conditions = filterText.slice(9).split(';') + + return conditions.reduce((filteredData, condition) => { + const match = condition.trim().match(/(\w+)\s*(eq|ne|like|notlike|gt|lt)\s*(.+)/) + + if (!match) { + return filteredData // Keep the current filtered data as is + } + + let [property, operator, value] = match.slice(1) + value = escapeRegExp(value) // Escape special characters + + return filteredData.filter((item) => { + // Find the actual key in the item that matches the property (case insensitive) + const actualKey = Object.keys(item).find( + (key) => key.toLowerCase() === property.toLowerCase(), + ) + + if (!actualKey) { + //set the error message so the user understands the key is not found. + console.error(`FilterError: Property "${property}" not found.`) + return false // Keep the item if the property is not found + } else { + } + + switch (operator) { + case 'eq': + return String(item[actualKey]).toLowerCase() === value.toLowerCase() + case 'ne': + return String(item[actualKey]).toLowerCase() !== value.toLowerCase() + case 'like': + return String(item[actualKey]).toLowerCase().includes(value.toLowerCase()) + case 'notlike': + return !String(item[actualKey]).toLowerCase().includes(value.toLowerCase()) + case 'gt': + return parseFloat(item[actualKey]) > parseFloat(value) + case 'lt': + return parseFloat(item[actualKey]) < parseFloat(value) + default: + return true + } + }) + }, data) + } else { + return data.filter( (item) => JSON.stringify(item).toLowerCase().indexOf(filterText.toLowerCase()) !== -1, ) - : [] + } + } + + const filteredItems = Array.isArray(data) ? filterData(data, filterText) : [] const applyFilter = (e) => { setFilterText(e.target.value) From 56bdd1580c014009f3f513976122163f922dcda4 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 24 Sep 2023 22:20:57 +0200 Subject: [PATCH 18/33] added exclude onmicrosoft --- src/views/tenant/standards/DomainsAnalyser.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/views/tenant/standards/DomainsAnalyser.js b/src/views/tenant/standards/DomainsAnalyser.js index 6b8bd505d756..a92501851bec 100644 --- a/src/views/tenant/standards/DomainsAnalyser.js +++ b/src/views/tenant/standards/DomainsAnalyser.js @@ -274,6 +274,12 @@ const DomainsAnalyser = () => { tenantSelector={true} showAllTenantSelector={true} datatable={{ + filterlist: [ + { + filterName: 'Exclude onmicrosoft domains', + filter: 'Complex: domain notlike onmicrosoft', + }, + ], path: `/api/DomainAnalyser_List`, params: { tenantFilter: currentTenant.defaultDomainName }, columns, From 303c3b77a493a1244e3d159682647857b63d6cef Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sun, 24 Sep 2023 18:25:16 -0400 Subject: [PATCH 19/33] Scheduler: Update text and filters --- src/views/cipp/Scheduler.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/views/cipp/Scheduler.js b/src/views/cipp/Scheduler.js index 6b209b06d58d..8483c2c477b2 100644 --- a/src/views/cipp/Scheduler.js +++ b/src/views/cipp/Scheduler.js @@ -358,17 +358,19 @@ const Scheduler = () => { label: 'Delete task', modal: true, modalUrl: `/api/RemoveScheduledItem?&ID=!RowKey`, - modalMessage: 'Are you sure you want to exclude these tenants?', + modalMessage: 'Do you want to delete this job?', }, ], }, filterlist: [ - { filterName: 'Excluded Tenants', filter: '"Excluded":true' }, - { filterName: 'Included Tenants', filter: '"Excluded":false' }, + { filterName: 'Planned Jobs', filter: 'Complex: TaskState eq Planned' }, + { filterName: 'Completed Jobs', filter: 'Complex: TaskState eq Completed' }, + { filterName: 'Recurring Jobs', filter: 'Complex: Recurrence gt 0' }, + { filterName: 'One-time Jobs', filter: 'Complex: Recurrence eq 0' }, ], keyField: 'id', columns, - reportName: `Tenants-List`, + reportName: `Scheduled-Jobs`, path: `/api/ListScheduledItems?RefreshGuid=${refreshState}`, }} /> From 6924f35956162140f8d19c80187958052ccbba13 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sun, 24 Sep 2023 22:25:37 -0400 Subject: [PATCH 20/33] CippTable - Graph filter rewrite - Remove graphFilter property from filter lists - Add debounce handler for delayed input changes - Update preset graph filters to new format - Move errors to CCallout and continue to render table --- src/components/tables/CippTable.js | 173 ++++++++---------- src/views/identity/administration/Users.js | 6 +- .../administration/ListEnterpriseApps.js | 8 +- .../administration/ListGDAPRelationships.js | 2 +- 4 files changed, 84 insertions(+), 105 deletions(-) diff --git a/src/components/tables/CippTable.js b/src/components/tables/CippTable.js index 949bfb167989..3bc66a7a0547 100644 --- a/src/components/tables/CippTable.js +++ b/src/components/tables/CippTable.js @@ -1,4 +1,4 @@ -import React, { useRef } from 'react' +import React, { useRef, useMemo, useState } from 'react' import { useSelector } from 'react-redux' import { ExportCsvButton, ExportPDFButton } from 'src/components/buttons' import { @@ -25,16 +25,9 @@ import { cellGenericFormatter } from './CellGenericFormat' import { ModalService } from '../utilities' import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' import { ConfirmModal } from '../utilities/SharedModal' -import { useState } from 'react' +import { debounce } from 'lodash' -const FilterComponent = ({ - filterText, - onFilter, - onClear, - filterlist, - onFilterPreset, - onFilterGraph, -}) => ( +const FilterComponent = ({ filterText, onFilter, onClear, filterlist, onFilterPreset }) => ( <> @@ -50,26 +43,17 @@ const FilterComponent = ({ { onFilterPreset('') - onFilterGraph('') }} > Clear Filter {filterlist && filterlist.map((item, idx) => { - if (item.hasOwnProperty('graphFilter') && item.graphFilter == true) { - return ( - onFilterGraph(item.filter)}> - {item.filterName} - - ) - } else { - return ( - onFilterPreset(item.filter)}> - {item.filterName} - - ) - } + return ( + onFilterPreset(item.filter)}> + {item.filterName} + + ) })} @@ -93,7 +77,6 @@ FilterComponent.propTypes = { onClear: PropTypes.func, filterlist: PropTypes.arrayOf(PropTypes.object), onFilterPreset: PropTypes.func, - onFilterGraph: PropTypes.func, } const customSort = (rows, selector, direction) => { @@ -204,8 +187,23 @@ export default function CippTable({ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') } + const setGraphFilter = (e) => { + if (graphFilterFunction) { + graphFilterFunction(e) + console.log(e) + } + } + + const debounceSetGraphFilter = useMemo(() => { + return debounce(setGraphFilter, 1000) + }, []) + const filterData = (data, filterText) => { - if (filterText.startsWith('Complex:')) { + if (filterText.startsWith('Graph:')) { + var query = filterText.slice(6).trim() + debounceSetGraphFilter(query) + return data + } else if (filterText.startsWith('Complex:')) { const conditions = filterText.slice(9).split(';') return conditions.reduce((filteredData, condition) => { @@ -262,12 +260,6 @@ export default function CippTable({ setFilterText(e.target.value) } - const setGraphFilter = (e) => { - if (graphFilterFunction) { - graphFilterFunction(e) - } - } - useEffect(() => { if (columns !== updatedColumns) { setUpdatedColumns(columns) @@ -592,11 +584,6 @@ export default function CippTable({ onFilter={(e) => setFilterText(e.target.value)} onFilterPreset={(e) => { setFilterText(e) - setGraphFilter('') - }} - onFilterGraph={(e) => { - setFilterText('') - setGraphFilter(e) }} onClear={handleClear} filterText={filterText} @@ -620,65 +607,61 @@ export default function CippTable({ const tablePageSize = useSelector((state) => state.app.tablePageSize) return (
    - {!isFetching && error && Error loading data} - {!error && ( -
    - {(columns.length === updatedColumns.length || !dynamicColumns) && ( - <> - {(massResults.length >= 1 || loopRunning) && ( - - {massResults.map((message, idx) => { - const results = message.data?.Results - const displayResults = Array.isArray(results) ? results.join(', ') : results + {!isFetching && error && Error loading data} +
    + {(columns.length === updatedColumns.length || !dynamicColumns) && ( + <> + {(massResults.length >= 1 || loopRunning) && ( + + {massResults.map((message, idx) => { + const results = message.data?.Results + const displayResults = Array.isArray(results) ? results.join(', ') : results - return
  • {displayResults}
  • - })} - {loopRunning && ( -
  • - -
  • - )} -
    - )} - } - paginationRowsPerPageOptions={[25, 50, 100, 200, 500]} - {...rest} - /> - {selectedRows.length >= 1 && ( - Selected {selectedRows.length} items - )} - - )} -
    - )} + return
  • {displayResults}
  • + })} + {loopRunning && ( +
  • + +
  • + )} +
    + )} + } + paginationRowsPerPageOptions={[25, 50, 100, 200, 500]} + {...rest} + /> + {selectedRows.length >= 1 && Selected {selectedRows.length} items} + + )} +
    ) } diff --git a/src/views/identity/administration/Users.js b/src/views/identity/administration/Users.js index 928853445e6a..bd3df44d1aa4 100644 --- a/src/views/identity/administration/Users.js +++ b/src/views/identity/administration/Users.js @@ -356,13 +356,11 @@ const Users = (row) => { { filterName: 'Users without a license', filter: '"assignedLicenses":[]' }, { filterName: 'Users with a license (Graph)', - filter: 'assignedLicenses/$count ne 0', - graphFilter: true, + filter: 'Graph: assignedLicenses/$count ne 0', }, { filterName: 'Users with a license & Enabled (Graph)', - filter: 'assignedLicenses/$count ne 0 and accountEnabled eq true', - graphFilter: true, + filter: 'Graph: assignedLicenses/$count ne 0 and accountEnabled eq true', }, ], columns, diff --git a/src/views/tenant/administration/ListEnterpriseApps.js b/src/views/tenant/administration/ListEnterpriseApps.js index 8e1a3bfa9b01..d6ae08d59059 100644 --- a/src/views/tenant/administration/ListEnterpriseApps.js +++ b/src/views/tenant/administration/ListEnterpriseApps.js @@ -75,7 +75,6 @@ const EnterpriseApplications = () => { selector: (row) => row.homepage, sortable: true, exportSelector: 'homepage', - cell: cellDateFormatter({ format: 'short' }), }, ] return ( @@ -88,13 +87,12 @@ const EnterpriseApplications = () => { filterlist: [ { filterName: 'All Enterprise Apps', - filter: "tags/any(t:t eq 'WindowsAzureActiveDirectoryIntegratedApp')", - graphFilter: true, + filter: "Graph: tags/any(t:t eq 'WindowsAzureActiveDirectoryIntegratedApp')", }, { filterName: 'Enterprise Apps (SAML)', - filter: "tags/any(t:t eq 'WindowsAzureActiveDirectoryGalleryApplicationPrimaryV1')", - graphFilter: true, + filter: + "Graph: tags/any(t:t eq 'WindowsAzureActiveDirectoryGalleryApplicationPrimaryV1')", }, ], tableProps: { diff --git a/src/views/tenant/administration/ListGDAPRelationships.js b/src/views/tenant/administration/ListGDAPRelationships.js index 8efe4f6795ee..a5703bd84b22 100644 --- a/src/views/tenant/administration/ListGDAPRelationships.js +++ b/src/views/tenant/administration/ListGDAPRelationships.js @@ -68,7 +68,7 @@ const Actions = (row, rowIndex, formatExtraData) => { }) const tenant = useSelector((state) => state.app.currentTenant) - row?.accessDetails.unifiedRoles.map((role) => { + row?.accessDetails?.unifiedRoles?.map((role) => { for (var x = 0; x < GDAPRoles.length; x++) { if (GDAPRoles[x].ObjectId == role.roleDefinitionId) { extendedInfo.push({ From 8fd42f373d638c909f703fdd1ad3ec77aa7900b9 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 25 Sep 2023 12:31:19 +0200 Subject: [PATCH 21/33] text fix --- src/views/email-exchange/spamfilter/DeploySpamfilter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/email-exchange/spamfilter/DeploySpamfilter.js b/src/views/email-exchange/spamfilter/DeploySpamfilter.js index 8c3a470efb20..f798ebf48003 100644 --- a/src/views/email-exchange/spamfilter/DeploySpamfilter.js +++ b/src/views/email-exchange/spamfilter/DeploySpamfilter.js @@ -140,7 +140,7 @@ const SpamFilterAdd = () => { Date: Mon, 25 Sep 2023 13:34:19 +0200 Subject: [PATCH 22/33] added TableFilter option via url --- src/components/tables/CippTable.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/components/tables/CippTable.js b/src/components/tables/CippTable.js index 3bc66a7a0547..fc09031e33be 100644 --- a/src/components/tables/CippTable.js +++ b/src/components/tables/CippTable.js @@ -1,4 +1,4 @@ -import React, { useRef, useMemo, useState } from 'react' +import React, { useRef, useMemo, useState, useCallback } from 'react' import { useSelector } from 'react-redux' import { ExportCsvButton, ExportPDFButton } from 'src/components/buttons' import { @@ -26,6 +26,7 @@ import { ModalService } from '../utilities' import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' import { ConfirmModal } from '../utilities/SharedModal' import { debounce } from 'lodash' +import { useSearchParams } from 'react-router-dom' const FilterComponent = ({ filterText, onFilter, onClear, filterlist, onFilterPreset }) => ( <> @@ -137,12 +138,20 @@ export default function CippTable({ const [loopRunning, setLoopRunning] = React.useState(false) const [massResults, setMassResults] = React.useState([]) const [filterText, setFilterText] = React.useState('') + const [filterviaURL, setFilterviaURL] = React.useState(false) const [updatedColumns, setUpdatedColumns] = React.useState(columns) const [selectedRows, setSelectedRows] = React.useState(false) const [genericGetRequest, getResults] = useLazyGenericGetRequestQuery() const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() const [getDrowndownInfo, dropDownInfo] = useLazyGenericGetRequestQuery() const [modalContent, setModalContent] = useState(null) + //get the search params called "tableFilter" and set the filter to that. + const [searchParams, setSearchParams] = useSearchParams() + if (searchParams.get('tableFilter') && !filterviaURL) { + setFilterText(searchParams.get('tableFilter')) + setFilterviaURL(true) + } + useEffect(() => { if (dropDownInfo.isFetching) { handleModal( @@ -198,7 +207,20 @@ export default function CippTable({ return debounce(setGraphFilter, 1000) }, []) + const debounceSetSearchParams = useCallback(() => { + const currentUrl = new URL(window.location.href) + if (filterText !== '') { + currentUrl.searchParams.set('tableFilter', filterText) + window.history.replaceState({}, '', currentUrl.toString()) + } else { + currentUrl.searchParams.delete('tableFilter') + window.history.replaceState({}, '', currentUrl.toString()) + } + }, [filterText]) + const filterData = (data, filterText) => { + const debouncedSetSearchParams = debounce(debounceSetSearchParams, 1000) + debouncedSetSearchParams() if (filterText.startsWith('Graph:')) { var query = filterText.slice(6).trim() debounceSetGraphFilter(query) From 48b9ab63f1fe76a26b17ebf5df4939e98c1915bb Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 25 Sep 2023 13:41:22 +0200 Subject: [PATCH 23/33] added tableFilter Urls --- src/views/home/Home.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/home/Home.js b/src/views/home/Home.js index d1334e1372c9..04fdd9d5cefa 100644 --- a/src/views/home/Home.js +++ b/src/views/home/Home.js @@ -175,7 +175,7 @@ const Home = () => { href={ '/identity/administration/users?customerId=' + currentTenant.customerId + - '&filterName=Users with a license' + '&tableFilter=Graph%3A+assignedLicenses%2F%24count+ne+0' } class="stretched-link" > @@ -206,7 +206,7 @@ const Home = () => { href={ '/identity/administration/users?customerId=' + currentTenant.customerId + - '&filterName=Guest users' + '&tableFilter=Graph%3A+usertype+eq+%27guest%27' } class="stretched-link" > From d7c7c545544389ec19b8f96dfaf8fd840b260a90 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 25 Sep 2023 14:01:00 +0200 Subject: [PATCH 24/33] use links instead of a's for internal routing --- src/views/home/Home.js | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/views/home/Home.js b/src/views/home/Home.js index 04fdd9d5cefa..1473119811aa 100644 --- a/src/views/home/Home.js +++ b/src/views/home/Home.js @@ -28,6 +28,7 @@ import ReactTimeAgo from 'react-time-ago' import { CellDelegatedPrivilege } from 'src/components/tables/CellDelegatedPrivilege' import Portals from 'src/data/portals' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { Link } from 'react-router-dom' const Home = () => { const [visible, setVisible] = useState(false) @@ -160,10 +161,10 @@ const Home = () => { - +
    {issuccessUserCounts && !isFetchingUserCount ? dashboard?.Users : }
    @@ -171,14 +172,14 @@ const Home = () => {
    - + className="stretched-link" + />
    {issuccessUserCounts && !isFetchingUserCount ? dashboard?.LicUsers : }
    @@ -186,15 +187,15 @@ const Home = () => {
    - + />
    {issuccessUserCounts && !isFetchingUserCount ? dashboard?.Gas : }
    @@ -202,14 +203,14 @@ const Home = () => {
    - + className="stretched-link" + />
    {issuccessUserCounts && !isFetchingUserCount ? dashboard?.Guests : }
    From e93d8159643d8daaed3fa2a6ff97d9eef5b65ae1 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 25 Sep 2023 15:50:33 +0200 Subject: [PATCH 25/33] version up --- public/version_latest.txt | 2 +- version_latest.txt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 version_latest.txt diff --git a/public/version_latest.txt b/public/version_latest.txt index d87edbfc1069..81911389142b 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -4.2.1 \ No newline at end of file +4.3.0 \ No newline at end of file diff --git a/version_latest.txt b/version_latest.txt deleted file mode 100644 index d87edbfc1069..000000000000 --- a/version_latest.txt +++ /dev/null @@ -1 +0,0 @@ -4.2.1 \ No newline at end of file From 0997ef73177cb05bdd544da0a4b93d58c1c85642 Mon Sep 17 00:00:00 2001 From: Roel van der Wegen Date: Mon, 25 Sep 2023 16:11:22 +0100 Subject: [PATCH 26/33] Wording --- src/data/Extensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/Extensions.json b/src/data/Extensions.json index cb15aa1043e8..3c1a1e7f1cd8 100644 --- a/src/data/Extensions.json +++ b/src/data/Extensions.json @@ -4,7 +4,7 @@ "type": "CIPP-API", "cat": "API", "forceSyncButton": false, - "helpText": "This integration allows you to use to enable API access outside of CIPP usage. Requires Global Administrator permissions inside your tenant for activation of the API. These credentials will only be shown once.", + "helpText": "This integration allows you to enable CIPP-API access outside of CIPP. Requires Global Administrator permissions inside your tenant for activation of the API. The API credentials will only be shown once.", "SettingOptions": [ { "type": "checkbox", From ff7450360418717a490987cb794e47cf94cded1b Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 26 Sep 2023 02:00:16 +0200 Subject: [PATCH 27/33] fixed issue with mass deletes webhooks --- src/views/tenant/administration/ListAlertsQueue.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/tenant/administration/ListAlertsQueue.js b/src/views/tenant/administration/ListAlertsQueue.js index d1da4bb53479..720e55a2fcc3 100644 --- a/src/views/tenant/administration/ListAlertsQueue.js +++ b/src/views/tenant/administration/ListAlertsQueue.js @@ -286,7 +286,7 @@ const ListAlertsQueue = () => { label: 'Delete webhook', color: 'info', modal: true, - modalUrl: `/api/RemoveWebhookAlert?CIPPID=!CIPPID&tenantfilter=!tenantName`, + modalUrl: `/api/RemoveWebhookAlert?CIPPID=!RowKey&tenantfilter=!PartitionKey`, modalMessage: 'Are you sure you want to delete this webhook alert?', }, ], From dc29bfcdac246b10f3583349bfe6b4132c82d4f1 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 25 Sep 2023 22:10:43 -0400 Subject: [PATCH 28/33] Prevent table re-render on queued results --- src/components/tables/CippDatatable.js | 31 ++++++++------------------ 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/src/components/tables/CippDatatable.js b/src/components/tables/CippDatatable.js index d41dc8e342ed..3c75376985f5 100644 --- a/src/components/tables/CippDatatable.js +++ b/src/components/tables/CippDatatable.js @@ -15,28 +15,15 @@ export default function CippDatatable({ path, params, ...rest }) { } = useListDatatableQuery({ path, params: { refreshGuid, $filter: graphFilter, ...params } }) return ( <> - {data[0]?.Queued ? ( - <> - {data[0]?.QueueMessage} - - - ) : ( - - )} + {data[0]?.Queued && {data[0]?.QueueMessage}} + ) } From e40eea1b8f054ae3142089d70c5346143c005420 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 25 Sep 2023 23:55:05 -0400 Subject: [PATCH 29/33] CippTable tweaks - Add defaultFilterText - Set initial graph filter from CippDatatable to default filter text - Add isModal - disable search param update --- src/components/tables/CippDatatable.js | 7 +++++++ src/components/tables/CippTable.js | 12 ++++++++---- src/components/utilities/SharedModal.js | 2 +- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/components/tables/CippDatatable.js b/src/components/tables/CippDatatable.js index 3c75376985f5..335af6f33541 100644 --- a/src/components/tables/CippDatatable.js +++ b/src/components/tables/CippDatatable.js @@ -13,6 +13,12 @@ export default function CippDatatable({ path, params, ...rest }) { isFetching, error, } = useListDatatableQuery({ path, params: { refreshGuid, $filter: graphFilter, ...params } }) + + var defaultFilterText = '' + if (params?.Parameters?.$filter) { + defaultFilterText = 'Graph: ' + params?.Parameters?.$filter + console.log(defaultFilterText) + } return ( <> {data[0]?.Queued && {data[0]?.QueueMessage}} @@ -21,6 +27,7 @@ export default function CippDatatable({ path, params, ...rest }) { data={data[0]?.Queued ? [] : data} isFetching={isFetching} error={error} + defaultFilterText={defaultFilterText} refreshFunction={setRefreshGuid} graphFilterFunction={setGraphFilter} /> diff --git a/src/components/tables/CippTable.js b/src/components/tables/CippTable.js index fc09031e33be..288685f43f1a 100644 --- a/src/components/tables/CippTable.js +++ b/src/components/tables/CippTable.js @@ -111,6 +111,8 @@ export default function CippTable({ graphFilterFunction = null, columns = [], dynamicColumns = true, + defaultFilterText = '', + isModal = false, filterlist, tableProps: { keyField = 'id', @@ -137,7 +139,7 @@ export default function CippTable({ const inputRef = useRef('') const [loopRunning, setLoopRunning] = React.useState(false) const [massResults, setMassResults] = React.useState([]) - const [filterText, setFilterText] = React.useState('') + const [filterText, setFilterText] = React.useState(defaultFilterText) const [filterviaURL, setFilterviaURL] = React.useState(false) const [updatedColumns, setUpdatedColumns] = React.useState(columns) const [selectedRows, setSelectedRows] = React.useState(false) @@ -147,7 +149,7 @@ export default function CippTable({ const [modalContent, setModalContent] = useState(null) //get the search params called "tableFilter" and set the filter to that. const [searchParams, setSearchParams] = useSearchParams() - if (searchParams.get('tableFilter') && !filterviaURL) { + if (searchParams.get('tableFilter') && !filterviaURL && !isModal) { setFilterText(searchParams.get('tableFilter')) setFilterviaURL(true) } @@ -219,8 +221,10 @@ export default function CippTable({ }, [filterText]) const filterData = (data, filterText) => { - const debouncedSetSearchParams = debounce(debounceSetSearchParams, 1000) - debouncedSetSearchParams() + if (!isModal) { + const debouncedSetSearchParams = debounce(debounceSetSearchParams, 1000) + debouncedSetSearchParams() + } if (filterText.startsWith('Graph:')) { var query = filterText.slice(6).trim() debounceSetGraphFilter(query) diff --git a/src/components/utilities/SharedModal.js b/src/components/utilities/SharedModal.js index 47fb57f2f547..4975c0cc17f6 100644 --- a/src/components/utilities/SharedModal.js +++ b/src/components/utilities/SharedModal.js @@ -13,7 +13,7 @@ import { CippTable } from 'src/components/tables' function mapBodyComponent({ componentType, data, componentProps }) { switch (componentType) { case 'table': - return + return case 'list': return
    {Array.isArray(data) && data.map((el, idx) =>
    {el}
    )}
    case 'text': From 3d053b13a84d131a0b612dc565230dd7502467af Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 25 Sep 2023 23:56:20 -0400 Subject: [PATCH 30/33] Update CippDatatable.js --- src/components/tables/CippDatatable.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/tables/CippDatatable.js b/src/components/tables/CippDatatable.js index 335af6f33541..e36befb3dea3 100644 --- a/src/components/tables/CippDatatable.js +++ b/src/components/tables/CippDatatable.js @@ -17,7 +17,6 @@ export default function CippDatatable({ path, params, ...rest }) { var defaultFilterText = '' if (params?.Parameters?.$filter) { defaultFilterText = 'Graph: ' + params?.Parameters?.$filter - console.log(defaultFilterText) } return ( <> From ab8bb402f0230b00e28438df1f8dc920244c286d Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 26 Sep 2023 13:07:51 +0200 Subject: [PATCH 31/33] hotfix --- public/version_latest.txt | 2 +- version_latest.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 version_latest.txt diff --git a/public/version_latest.txt b/public/version_latest.txt index 81911389142b..ecedc98d1d5a 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -4.3.0 \ No newline at end of file +4.3.1 \ No newline at end of file diff --git a/version_latest.txt b/version_latest.txt new file mode 100644 index 000000000000..ecedc98d1d5a --- /dev/null +++ b/version_latest.txt @@ -0,0 +1 @@ +4.3.1 \ No newline at end of file From a28bc053a994bf1986c46ce6d7e52add2d332da1 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 26 Sep 2023 18:16:59 +0200 Subject: [PATCH 32/33] fixes data not available bug --- src/views/cipp/CIPPSettings.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/cipp/CIPPSettings.js b/src/views/cipp/CIPPSettings.js index 00deb2d85098..7ba3cb285ad7 100644 --- a/src/views/cipp/CIPPSettings.js +++ b/src/views/cipp/CIPPSettings.js @@ -1577,8 +1577,8 @@ const ExtensionsTab = () => { } const MappingsTab = () => { - const [listHaloBackend, listBackendHaloResult] = useLazyGenericGetRequestQuery() - const [setHaloExtensionconfig, extensionHaloConfigResult] = useLazyGenericPostRequestQuery() + const [listHaloBackend, listBackendHaloResult = []] = useLazyGenericGetRequestQuery() + const [setHaloExtensionconfig, extensionHaloConfigResult = []] = useLazyGenericPostRequestQuery() const onHaloSubmit = (values) => { setHaloExtensionconfig({ @@ -1608,7 +1608,7 @@ const MappingsTab = () => { Use the table below to map your client to the correct PSA client {listBackendHaloResult.isSuccess && - listBackendHaloResult.data.Tenants.map((tenant) => ( + listBackendHaloResult.data.Tenants?.map((tenant) => ( Date: Tue, 26 Sep 2023 18:24:02 +0200 Subject: [PATCH 33/33] add refresh to groups --- src/views/identity/administration/EditGroup.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/views/identity/administration/EditGroup.js b/src/views/identity/administration/EditGroup.js index 9ea8160b396a..1c9e2d44d187 100644 --- a/src/views/identity/administration/EditGroup.js +++ b/src/views/identity/administration/EditGroup.js @@ -32,9 +32,8 @@ const EditGroup = () => { let query = useQuery() const groupId = query.get('groupId') const tenantDomain = query.get('tenantDomain') - const [queryError, setQueryError] = useState(false) - + const [refreshToken, setRefresh] = useState('') const { data: group = {}, isFetching, @@ -47,14 +46,14 @@ const EditGroup = () => { isFetching: membersisFetching, error: membersError, isSuccess: membersIsSuccess, - } = useListGroupMembersQuery({ tenantDomain, groupId }) + } = useListGroupMembersQuery({ tenantDomain, groupId, refreshToken }) const { data: owners = [], isFetching: ownersisFetching, error: ownersError, isSuccess: ownersIsSuccess, - } = useListGroupOwnersQuery({ tenantDomain, groupId }) + } = useListGroupOwnersQuery({ tenantDomain, groupId, refreshToken }) const { data: users = [], isFetching: usersIsFetching, @@ -66,6 +65,7 @@ const EditGroup = () => { isFetching: contactsIsFetching, error: contactsError, } = useListContactsQuery({ tenantDomain }) + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() const [roleInfo, setroleInfo] = React.useState([]) useEffect(() => { @@ -86,7 +86,7 @@ const EditGroup = () => { }) setroleInfo(ownerWithRole.concat(memberwithRole)) } - }, [owners, members, ownersIsSuccess, membersIsSuccess]) + }, [owners, members, ownersIsSuccess, membersIsSuccess, postResults]) useEffect(() => { if (!groupId || !tenantDomain) { @@ -97,7 +97,6 @@ const EditGroup = () => { setQueryError(true) } }, [groupId, tenantDomain, dispatch]) - const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() const onSubmit = (values) => { const shippedValues = { tenantID: tenantDomain, @@ -114,7 +113,9 @@ const EditGroup = () => { mail: group[0].mail, } //window.alert(JSON.stringify(shippedValues)) - genericPostRequest({ path: '/api/EditGroup', values: shippedValues }) + genericPostRequest({ path: '/api/EditGroup', values: shippedValues }).then((res) => { + setRefresh(postResults.requestId) + }) } const tableColumns = [ {