diff --git a/package.json b/package.json
index 039c0b97dd85..bdf502e059b8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cipp",
- "version": "5.9.3",
+ "version": "6.0.2",
"description": "The CyberDrain Improved Partner Portal is a portal to help manage administration for Microsoft Partners.",
"homepage": "https://cipp.app/",
"bugs": {
diff --git a/public/version_latest.txt b/public/version_latest.txt
index 99a8b57b6f85..9b9a244206f6 100644
--- a/public/version_latest.txt
+++ b/public/version_latest.txt
@@ -1 +1 @@
-5.9.3
+6.0.2
diff --git a/src/_nav.jsx b/src/_nav.jsx
index 631d6eaf1169..0ab9f9db7c5b 100644
--- a/src/_nav.jsx
+++ b/src/_nav.jsx
@@ -821,6 +821,11 @@ const _nav = [
name: 'Extensions Settings',
to: '/cipp/extensions',
},
+ {
+ component: CNavItem,
+ name: 'Extension Sync',
+ to: '/cipp/extension-sync',
+ },
{
component: CNavItem,
name: 'User Settings',
diff --git a/src/components/tables/CellBytes.jsx b/src/components/tables/CellBytes.jsx
new file mode 100644
index 000000000000..b5b3e38211b8
--- /dev/null
+++ b/src/components/tables/CellBytes.jsx
@@ -0,0 +1,24 @@
+import PropTypes from 'prop-types'
+
+export function CellBytes({ cell }) {
+ return (cell / 1024 ** 3).toFixed(2)
+}
+
+CellBytes.propTypes = {
+ propName: PropTypes.string,
+ cell: PropTypes.object,
+}
+
+export function CellBytesToPercentage({ row, value, dividedBy }) {
+ return Math.round((row[value] / row[dividedBy]) * 100 * 10) / 10
+}
+
+CellBytesToPercentage.propTypes = {
+ propName: PropTypes.string,
+ cell: PropTypes.object,
+}
+
+export const cellBytesFormatter = () => (row, index, column, id) => {
+ const cell = column.selector(row)
+ return CellBytes({ cell })
+}
diff --git a/src/components/tables/CellCopyButton.jsx b/src/components/tables/CellCopyButton.jsx
index eeed76d73478..ca3a2b7c98c2 100644
--- a/src/components/tables/CellCopyButton.jsx
+++ b/src/components/tables/CellCopyButton.jsx
@@ -2,7 +2,6 @@ import PropTypes from 'prop-types'
import CippCopyToClipboard from '../utilities/CippCopyToClipboard'
export function CellCopyButton({ cell }) {
- console.log('hi! cell:', cell)
return
}
@@ -13,6 +12,5 @@ CellCopyButton.propTypes = {
export const cellCopyButtonFormatter = () => (row, index, column, id) => {
const cell = column.selector(row)
- console.log('cell:', cell)
return CellCopyButton({ cell })
}
diff --git a/src/components/tables/CippTable.jsx b/src/components/tables/CippTable.jsx
index 6eee3a54e611..04ede5a4c204 100644
--- a/src/components/tables/CippTable.jsx
+++ b/src/components/tables/CippTable.jsx
@@ -629,12 +629,14 @@ export default function CippTable({
if (!disablePDFExport || !disableCSVExport) {
const keys = []
const exportFormatter = {}
+ const exportFormatterArgs = {}
columns.map((col) => {
if (col.exportSelector) keys.push(col.exportSelector)
if (col.exportFormatter) exportFormatter[col.exportSelector] = col.exportFormatter
+ if (col.exportFormatterArgs)
+ exportFormatterArgs[col.exportSelector] = col.exportFormatterArgs
return null
})
-
// Define the flatten function
const flatten = (obj, prefix = '') => {
if (obj === null) return {}
@@ -664,18 +666,18 @@ export default function CippTable({
// Define the applyFormatter function
const applyFormatter = (obj) => {
return Object.keys(obj).reduce((acc, key) => {
+ const formatterArgs = exportFormatterArgs[key]
const formatter = exportFormatter[key]
- // Since the keys after flattening will be dot-separated, we need to adjust this to support nested keys if necessary.
const keyParts = key.split('.')
const finalKeyPart = keyParts[keyParts.length - 1]
const formattedValue =
- typeof formatter === 'function' ? formatter({ cell: obj[key] }) : obj[key]
+ typeof formatter === 'function'
+ ? formatter({ row: obj, cell: obj[key], ...formatterArgs })
+ : obj[key]
acc[key] = formattedValue
return acc
}, {})
}
-
- // Process exportData function
const processExportData = (exportData, selectedColumns) => {
//filter out the columns that are not selected via selectedColumns
exportData = exportData.map((item) => {
diff --git a/src/importsMap.jsx b/src/importsMap.jsx
index 9666488abfc9..e6a8a422c777 100644
--- a/src/importsMap.jsx
+++ b/src/importsMap.jsx
@@ -137,6 +137,7 @@ import React from 'react'
"/license": React.lazy(() => import('./views/pages/license/License')),
"/cipp/settings": React.lazy(() => import('./views/cipp/app-settings/CIPPSettings')),
"/cipp/extensions": React.lazy(() => import('./views/cipp/Extensions')),
+ "/cipp/extension-sync": React.lazy(() => import('./views/cipp/ExtensionSync')),
"/cipp/setup": React.lazy(() => import('./views/cipp/Setup')),
"/tenant/administration/securescore": React.lazy(() => import('./views/tenant/administration/SecureScore')),
"/tenant/administration/gdap": React.lazy(() => import('./views/tenant/administration/GDAPWizard')),
diff --git a/src/routes.json b/src/routes.json
index 96584165d77c..b6a54db32b03 100644
--- a/src/routes.json
+++ b/src/routes.json
@@ -938,6 +938,12 @@
"component": "views/cipp/Extensions",
"allowedRoles": ["admin"]
},
+ {
+ "path": "/cipp/extension-sync",
+ "name": "Extension Sync",
+ "component": "views/cipp/ExtensionSync",
+ "allowedRoles": ["admin"]
+ },
{
"path": "/cipp/setup",
"name": "Setup",
diff --git a/src/views/cipp/ExtensionMappings.jsx b/src/views/cipp/ExtensionMappings.jsx
index b493dce041a2..4de976555ccc 100644
--- a/src/views/cipp/ExtensionMappings.jsx
+++ b/src/views/cipp/ExtensionMappings.jsx
@@ -226,9 +226,7 @@ export default function ExtensionMappings({ type, fieldMappings = false, autoMap
name: tenant.displayName,
value: tenant.customerId,
}))}
- onChange={(e) => {
- setMappingArray(e.value)
- }}
+ onChange={(e) => setTenantMappingsArray(e.value)}
isLoading={listMappingBackendResult.isFetching}
/>
@@ -238,16 +236,10 @@ export default function ExtensionMappings({ type, fieldMappings = false, autoMap
{
- return !Object.values(listMappingBackendResult.data?.Mappings)
- .map((value) => {
- return value.value
- })
- .includes(client.value.toString())
- }).map((client) => ({
+ values={listMappingBackendResult.data?.Companies.map((client) => ({
name: client.name,
value: client.value,
- }))}
+ })).sort((a, b) => a.name.localeCompare(b.name))}
onChange={(e) => setMappingValue(e)}
placeholder={`Select a ${type} Organization`}
isLoading={listMappingBackendResult.isFetching}
@@ -267,7 +259,7 @@ export default function ExtensionMappings({ type, fieldMappings = false, autoMap
...mappingArray,
{
Tenant: listMappingBackendResult.data?.Tenants.find(
- (tenant) => tenant.customerId === mappingArray,
+ (tenant) => tenant.customerId === tenantMappingArray,
),
companyName: mappingValue.label,
companyId: mappingValue.value,
diff --git a/src/views/cipp/ExtensionSync.jsx b/src/views/cipp/ExtensionSync.jsx
new file mode 100644
index 000000000000..c9937921aa86
--- /dev/null
+++ b/src/views/cipp/ExtensionSync.jsx
@@ -0,0 +1,97 @@
+import React, { useState } from 'react'
+import { CCol, CRow } from '@coreui/react'
+import { useSelector } from 'react-redux'
+
+import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app'
+
+import { CippPage, CippPageList } from 'src/components/layout'
+import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGenericFormat'
+import 'react-datepicker/dist/react-datepicker.css'
+import { CellBadge, cellBadgeFormatter, cellDateFormatter } from 'src/components/tables'
+import { TitleButton } from 'src/components/buttons'
+
+const ExtensionSync = () => {
+ const [ExecuteGetRequest, getResults] = useLazyGenericGetRequestQuery()
+ const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName)
+ const [refreshState, setRefreshState] = useState(false)
+ const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery()
+
+ const columns = [
+ {
+ name: 'Tenant',
+ selector: (row) => row['Tenant'],
+ sortable: true,
+ cell: cellGenericFormatter(),
+ exportSelector: 'Tenants',
+ },
+ {
+ name: 'Sync Type',
+ selector: (row) => row['SyncType'],
+ sortable: true,
+ cell: cellBadgeFormatter({ color: 'info' }),
+ exportSelector: 'SyncType',
+ },
+ {
+ name: 'Task',
+ selector: (row) => row['Name'],
+ sortable: true,
+ cell: cellGenericFormatter(),
+ exportSelector: 'Name',
+ },
+ {
+ name: 'Scheduled Time',
+ selector: (row) => row['ScheduledTime'],
+ sortable: true,
+ cell: cellDateFormatter({ format: 'short' }),
+ exportSelector: 'ScheduledTime',
+ },
+ {
+ name: 'Last Run',
+ selector: (row) => row['ExecutedTime'],
+ sortable: true,
+ cell: cellDateFormatter({ format: 'short' }),
+ exportSelector: 'ExecutedTime',
+ },
+ {
+ name: 'Repeats every',
+ selector: (row) => row['RepeatsEvery'],
+ sortable: true,
+ cell: (row) => CellTip(row['RepeatsEvery']),
+ exportSelector: 'RepeatsEvery',
+ },
+ {
+ name: 'Results',
+ selector: (row) => row['Results'],
+ sortable: true,
+ cell: cellGenericFormatter(),
+ exportSelector: 'Results',
+ },
+ ]
+
+ return (
+
+ <>
+
+
+
+
+
+ >
+
+ )
+}
+
+export default ExtensionSync
diff --git a/src/views/cipp/Extensions.jsx b/src/views/cipp/Extensions.jsx
index 8c8c9dd79dee..eec80f5a1068 100644
--- a/src/views/cipp/Extensions.jsx
+++ b/src/views/cipp/Extensions.jsx
@@ -67,6 +67,7 @@ export default function CIPPExtensions() {
path: 'api/ExecExtensionSync?Extension=' + integrationType,
})
}
+ disabled={disabled}
className="me-2"
>
)}
+ {listSyncExtensionResult?.data?.Results && (
+
+ {listSyncExtensionResult?.data?.Results}
+
+ )}
{integration.mappingRequired && (
diff --git a/src/views/email-exchange/reports/MailboxStatisticsList.jsx b/src/views/email-exchange/reports/MailboxStatisticsList.jsx
index 0a6a4c80e9c0..abe707575e83 100644
--- a/src/views/email-exchange/reports/MailboxStatisticsList.jsx
+++ b/src/views/email-exchange/reports/MailboxStatisticsList.jsx
@@ -2,6 +2,11 @@ import React, { useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import { CellTip, cellBooleanFormatter } from 'src/components/tables'
import { CippPageList } from 'src/components/layout'
+import {
+ CellBytes,
+ CellBytesToPercentage,
+ cellBytesFormatter,
+} from 'src/components/tables/CellBytes'
const MailboxStatsList = () => {
const [tenantColumnSet, setTenantColumn] = useState(true)
@@ -64,23 +69,32 @@ const MailboxStatsList = () => {
exportSelector: 'lastActivityDate',
},
{
- selector: (row) => (row['storageUsedInBytes'] / 1024 ** 3).toFixed(2),
+ selector: (row) => row['storageUsedInBytes'],
+ cell: cellBytesFormatter(),
name: 'Used Space (GB)',
sortable: true,
exportSelector: 'storageUsedInBytes',
+ exportFormatter: CellBytes,
},
{
- selector: (row) => (row['prohibitSendReceiveQuotaInBytes'] / 1024 ** 3).toFixed(2),
+ selector: (row) => row['prohibitSendReceiveQuotaInBytes'],
+ cell: cellBytesFormatter(),
name: 'Quota (GB)',
sortable: true,
- exportSelector: 'QuotaGB',
+ exportSelector: 'prohibitSendReceiveQuotaInBytes',
+ exportFormatter: CellBytes,
},
{
selector: (row) =>
Math.round((row.storageUsedInBytes / row.prohibitSendReceiveQuotaInBytes) * 100 * 10) / 10,
name: 'Quota Used(%)',
sortable: true,
- exportSelector: 'QuotaUsed',
+ exportSelector: 'CippStatus',
+ exportFormatter: CellBytesToPercentage,
+ exportFormatterArgs: {
+ value: 'storageUsedInBytes',
+ dividedBy: 'prohibitSendReceiveQuotaInBytes',
+ },
},
{
selector: (row) => row['itemCount'],
diff --git a/src/views/home/Home.jsx b/src/views/home/Home.jsx
index 7111f9e41bb2..c3dc3de2afa6 100644
--- a/src/views/home/Home.jsx
+++ b/src/views/home/Home.jsx
@@ -289,7 +289,7 @@ const TenantDashboard = () => {
{organization.verifiedDomains?.slice(0, 3).map((item, idx) => (
{item.name}
))}
- {organization.verifiedDomains?.length > 5 && (
+ {organization.verifiedDomains?.length > 3 && (
<>
{organization.verifiedDomains?.slice(3).map((item, idx) => (
diff --git a/src/views/identity/administration/DeployJITAdmin.jsx b/src/views/identity/administration/DeployJITAdmin.jsx
index 9d16549f9867..81d23666fa61 100644
--- a/src/views/identity/administration/DeployJITAdmin.jsx
+++ b/src/views/identity/administration/DeployJITAdmin.jsx
@@ -8,6 +8,7 @@ import {
RFFCFormRadioList,
RFFCFormSwitch,
RFFSelectSearch,
+ RFFCFormSelect,
} from 'src/components/forms'
import {
useGenericGetRequestQuery,
@@ -26,6 +27,7 @@ import 'react-datepicker/dist/react-datepicker.css'
import { useListUsersQuery } from 'src/store/api/users'
import GDAPRoles from 'src/data/GDAPRoles'
import { CippDatatable, cellDateFormatter } from 'src/components/tables'
+import { useListDomainsQuery } from 'src/store/api/domains'
const DeployJITAdmin = () => {
const [ExecuteGetRequest, getResults] = useLazyGenericGetRequestQuery()
@@ -36,6 +38,11 @@ const DeployJITAdmin = () => {
const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName)
const [refreshState, setRefreshState] = useState(false)
const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery()
+ const {
+ data: domains = [],
+ isFetching: domainsIsFetching,
+ error: domainsError,
+ } = useListDomainsQuery({ tenantDomain })
const onSubmit = (values) => {
const startTime = Math.floor(startDate.getTime() / 1000)
@@ -43,7 +50,7 @@ const DeployJITAdmin = () => {
const shippedValues = {
TenantFilter: tenantDomain,
UserId: values.UserId?.value,
- UserPrincipalName: values.UserPrincipalName,
+ UserPrincipalName: `${values.username}@${values.domain}`,
FirstName: values.FirstName,
LastName: values.LastName,
useraction: values.useraction,
@@ -134,8 +141,24 @@ const DeployJITAdmin = () => {
+
+
+
-
+ {domainsIsFetching && }
+ {!domainsIsFetching && (
+ ({
+ value: domain.id,
+ label: domain.id,
+ }))}
+ />
+ )}
+ {domainsError && Failed to load list of domains}
diff --git a/version_latest.txt b/version_latest.txt
index 99a8b57b6f85..9b9a244206f6 100644
--- a/version_latest.txt
+++ b/version_latest.txt
@@ -1 +1 @@
-5.9.3
+6.0.2