From e119273fe060d27dc450525b6b4e27969f4f2c00 Mon Sep 17 00:00:00 2001 From: Esco Date: Mon, 15 Jul 2024 20:36:54 +0200 Subject: [PATCH 1/5] Added bulkdelete to Contacts --- .../email-exchange/administration/ContactsList.jsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/views/email-exchange/administration/ContactsList.jsx b/src/views/email-exchange/administration/ContactsList.jsx index f0709de26be3..65bca9394927 100644 --- a/src/views/email-exchange/administration/ContactsList.jsx +++ b/src/views/email-exchange/administration/ContactsList.jsx @@ -109,6 +109,17 @@ const ContactList = () => { reportName: `${tenant?.defaultDomainName}-Contacts-List`, path: '/api/ListContacts', columns, + tableProps: { + selectableRows: true, + actionsList: [ + { + label: 'Remove selected Contacts', + color: 'danger', + modal: true, + modalUrl: `/api/RemoveContact?TenantFilter=${tenant.defaultDomainName}&GUID=!id`, + }, + ], + }, params: { TenantFilter: tenant?.defaultDomainName }, }} /> From 2764689c6ad33e8a009298c7c7564aa388d85b07 Mon Sep 17 00:00:00 2001 From: Esco Date: Mon, 15 Jul 2024 21:10:02 +0200 Subject: [PATCH 2/5] orderby desc --- src/views/identity/administration/RiskyUsers.jsx | 2 +- src/views/identity/reports/RiskDetections.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/identity/administration/RiskyUsers.jsx b/src/views/identity/administration/RiskyUsers.jsx index d1ffc4ad0499..03f58eaf241f 100644 --- a/src/views/identity/administration/RiskyUsers.jsx +++ b/src/views/identity/administration/RiskyUsers.jsx @@ -92,7 +92,7 @@ const RiskyUsers = () => { TenantFilter: tenant?.defaultDomainName, Endpoint: `identityProtection/riskyUsers`, $count: true, - $orderby: 'riskLastUpdatedDateTime', + $orderby: 'riskLastUpdatedDateTime desc', }, }} /> diff --git a/src/views/identity/reports/RiskDetections.jsx b/src/views/identity/reports/RiskDetections.jsx index ed1604acda6e..02b84a9b1086 100644 --- a/src/views/identity/reports/RiskDetections.jsx +++ b/src/views/identity/reports/RiskDetections.jsx @@ -114,7 +114,7 @@ const RiskDetections = () => { TenantFilter: tenant?.defaultDomainName, Endpoint: `identityProtection/riskDetections`, $count: true, - $orderby: 'detectedDateTime', + $orderby: 'detectedDateTime desc', }, }} /> From adbb19ff9d547b7595e7a13216707d7b60da25a1 Mon Sep 17 00:00:00 2001 From: BNWEIN Date: Tue, 16 Jul 2024 12:30:46 +0100 Subject: [PATCH 3/5] Added ability to edit contact Added ability to edit contact Fixes: https://github.com/KelvinTegelaar/CIPP/issues/2680 --- src/importsMap.jsx | 2 +- src/store/api/users.js | 3 +- .../administration/ContactsList.jsx | 17 +- .../administration/EditContact.jsx | 295 +++++++++++++++++- 4 files changed, 301 insertions(+), 16 deletions(-) diff --git a/src/importsMap.jsx b/src/importsMap.jsx index e6a8a422c777..8325207e191e 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -118,7 +118,7 @@ import React from 'react' "/email/administration/add-contact": React.lazy(() => import('./views/email-exchange/administration/AddContact')), "/email/administration/edit-calendar-permissions": React.lazy(() => import('./views/email-exchange/administration/EditCalendarPermissions')), "/email/administration/view-mobile-devices": React.lazy(() => import('./views/email-exchange/administration/ViewMobileDevices')), - "/email/administration/edit-contact": React.lazy(() => import('./views/email-exchange/administration/EditContact')), + "/email/administration/edit-contact": React.lazy(() => import('./views/email-exchange/administration/EditContact.jsx')), "/email/administration/mailboxes": React.lazy(() => import('./views/email-exchange/administration/MailboxesList')), "/email/administration/deleted-mailboxes": React.lazy(() => import('./views/email-exchange/administration/DeletedMailboxes')), "/email/administration/mailbox-rules": React.lazy(() => import('./views/email-exchange/administration/MailboxRuleList')), diff --git a/src/store/api/users.js b/src/store/api/users.js index 8dc443ce1dc1..7ef16f12fdb3 100644 --- a/src/store/api/users.js +++ b/src/store/api/users.js @@ -18,10 +18,11 @@ export const usersApi = baseApi.injectEndpoints({ }), }), listContacts: builder.query({ - query: ({ tenantDomain }) => ({ + query: ({ tenantDomain, ContactID }) => ({ path: '/api/ListContacts', params: { TenantFilter: tenantDomain, + ContactID, }, }), }), diff --git a/src/views/email-exchange/administration/ContactsList.jsx b/src/views/email-exchange/administration/ContactsList.jsx index f0709de26be3..0ea00b6f74aa 100644 --- a/src/views/email-exchange/administration/ContactsList.jsx +++ b/src/views/email-exchange/administration/ContactsList.jsx @@ -3,6 +3,7 @@ import { useSelector } from 'react-redux' import { CButton } from '@coreui/react' import { CippPageList } from 'src/components/layout' import { CellTip, cellBooleanFormatter } from 'src/components/tables' +import { Link, useSearchParams } from 'react-router-dom' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faEdit, faEllipsisV } from '@fortawesome/free-solid-svg-icons' import { TitleButton } from 'src/components/buttons' @@ -11,16 +12,16 @@ import { CippActionsOffcanvas } from 'src/components/utilities' const Actions = (row, rowIndex, formatExtraData) => { const tenant = useSelector((state) => state.app.currentTenant) const [ocVisible, setOCVisible] = useState(false) + const editLink = row?.tenant + ? `/email/administration/edit-contact?ContactID=${row.id}&tenantDomain=${row.Tenant}` + : `/email/administration/edit-contact?ContactID=${row.id}&tenantDomain=${tenant.defaultDomainName}` return ( <> - - - - + + + + + setOCVisible(true)}> diff --git a/src/views/email-exchange/administration/EditContact.jsx b/src/views/email-exchange/administration/EditContact.jsx index 00cd06b2f933..460d40d98fda 100644 --- a/src/views/email-exchange/administration/EditContact.jsx +++ b/src/views/email-exchange/administration/EditContact.jsx @@ -1,11 +1,294 @@ -import React from 'react' +import React, { useEffect, useState } from 'react' +import { CButton, CCallout, CCol, CForm, CRow, CSpinner } from '@coreui/react' +import useQuery from 'src/hooks/useQuery' +import { useDispatch, useSelector } from 'react-redux' +import { Form } from 'react-final-form' +import { RFFCFormInput } from 'src/components/forms' +import { useListContactsQuery } from 'src/store/api/users' +import { CippCodeBlock, ModalService } from 'src/components/utilities' +import { 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 } from 'src/components/layout' -const EditContact = (props) => { +const EditContact = () => { + const dispatch = useDispatch() + let query = useQuery() + const ContactID = query.get('ContactID') + const tenantDomain = query.get('tenantDomain') + + const [queryError, setQueryError] = useState(false) + + const { + data: Contact = [], + isFetching: ContactIsFetching, + error: ContactError, + } = useListContactsQuery({ tenantDomain, ContactID }) + + useEffect(() => { + if (!ContactID || !tenantDomain) { + ModalService.open({ + body: 'Error invalid request, could not load requested contact.', + title: 'Invalid Request', + }) + setQueryError(true) + } else { + setQueryError(false) + } + }, [ContactID, tenantDomain, dispatch]) + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + const onSubmit = (values) => { + if (values.defaultAttributes) { + //map default attributes to the addedAttributes array. If addedAttributes is not present, create it. + values.addedAttributes = values.addedAttributes ? values.addedAttributes : [] + Object.keys(values.defaultAttributes).forEach((key) => { + values.addedAttributes.push({ Key: key, Value: values.defaultAttributes[key].Value }) + }) + } + const shippedValues = { + BusinessPhone: values.businessPhones, + City: values.city, + CompanyName: values.companyName, + Country: values.country, + mail: values.mail, + DisplayName: values.displayName, + firstName: values.givenName, + Jobtitle: values.jobTitle, + LastName: values.surname, + MobilePhone: values.mobilePhone, + PostalCode: values.postalCode, + ContactID: ContactID, + streetAddress: values.streetAddress, + tenantID: tenantDomain, + } + // window.alert(JSON.stringify(shippedValues)) + genericPostRequest({ path: '/api/EditContact', values: shippedValues }) + } + const [addedAttributes, setAddedAttribute] = React.useState(0) + const currentSettings = useSelector((state) => state.app) + + // Extract the first contact from the array + const contactData = Contact.length > 0 ? Contact[0] : {} + + // Extract address and phone details + const address = + contactData.addresses && contactData.addresses.length > 0 ? contactData.addresses[0] : {} + const mobilePhone = contactData.phones + ? contactData.phones.find((phone) => phone.type === 'mobile') + : {} + const businessPhones = contactData.phones + ? contactData.phones + .filter((phone) => phone.type === 'business') + .map((phone) => phone.number) + .join(', ') + : '' + + const initialState = { + ...contactData, + streetAddress: address.street || '', + postalCode: address.postalCode || '', + city: address.city || '', + country: address.countryOrRegion || '', + mobilePhone: mobilePhone ? mobilePhone.number : '', + businessPhones: businessPhones || '', + } + + const formDisabled = + queryError === true || !!ContactError || !Contact || Object.keys(contactData).length === 0 + const RawUser = JSON.stringify(contactData, null, 2) return ( -
-

Edit Contact

- future release. -
+ + {!queryError && ( + <> + {postResults.isSuccess && ( + {postResults.data?.Results} + )} + {queryError && ( + + + + {/* @todo add more descriptive help message here */} + Failed to load contact + + + + )} + + + + {ContactIsFetching && } + {ContactError && Error loading user} + {!ContactIsFetching && ( +
{ + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Edit Contact + {postResults.isFetching && ( + + )} + + + + {postResults.isSuccess && ( + + {Array.isArray(postResults.data.Results) ? ( + postResults.data.Results.map((message, idx) => ( +
  • {message}
  • + )) + ) : ( + {postResults.data.Results} + )} +
    + )} +
    + ) + }} + /> + )} + + + + + {ContactIsFetching && } + {ContactError && Error loading user} + {!ContactIsFetching && ( + <> + This is the (raw) information for this contact. + + + )} + + + + + )} + ) } From e5690158e69061c6356878fdfb23b37eb27f1d7f Mon Sep 17 00:00:00 2001 From: chase-vgo <168204519+chase-vgo@users.noreply.github.com> Date: Tue, 16 Jul 2024 08:55:39 -0500 Subject: [PATCH 4/5] Updated verbiage --- src/views/identity/administration/EditUser.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/identity/administration/EditUser.jsx b/src/views/identity/administration/EditUser.jsx index 2f90bedb20e8..517adf718620 100644 --- a/src/views/identity/administration/EditUser.jsx +++ b/src/views/identity/administration/EditUser.jsx @@ -169,7 +169,7 @@ const EditUser = () => { )} {user?.onPremisesSyncEnabled === true && ( - Warning! This user Active Directory sync enabled. Edits should be made from a Domain + Warning! This user is Active Directory sync enabled. Edits should be made from a Domain Controller. )} From 97fae4dadda55697787c4d38a286e08f150c9eb0 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 16 Jul 2024 18:17:43 +0200 Subject: [PATCH 5/5] spacing --- src/views/identity/administration/EditUser.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/identity/administration/EditUser.jsx b/src/views/identity/administration/EditUser.jsx index 517adf718620..126a72f4e29b 100644 --- a/src/views/identity/administration/EditUser.jsx +++ b/src/views/identity/administration/EditUser.jsx @@ -169,8 +169,8 @@ const EditUser = () => { )} {user?.onPremisesSyncEnabled === true && ( - Warning! This user is Active Directory sync enabled. Edits should be made from a Domain - Controller. + Warning! This user is Active Directory sync enabled. Edits should be made from a + Domain Controller. )} {postResults.isSuccess && (