From bbe743c4c4a38b49f5b8cd3524dbe1eca4417e76 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Tue, 21 Nov 2023 09:15:11 +0000 Subject: [PATCH 01/17] Added NinjaOne Integration Settings --- src/data/Extensions.json | 50 +++++++++ src/views/cipp/CIPPSettings.jsx | 184 ++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+) diff --git a/src/data/Extensions.json b/src/data/Extensions.json index 7d7703f4d13a..c83bfbf51ce5 100644 --- a/src/data/Extensions.json +++ b/src/data/Extensions.json @@ -106,5 +106,55 @@ "label": "Enable Integration" } ] + }, + { + "name": "NinjaOne Integration", + "type": "NinjaOne", + "cat": "Documentation & Monitoring", + "forceSyncButton": true, + "helpText": "NOTE: This integration requires version 5.6 of NinjaOne. Current release dates are: ca.ninjarmm.com - 28th November, oc.ninjarmm.com - 29th November, eu.ninjarmm.com - 4th December, us2.ninjarmm.com - 12th December, app.ninjarmm.com - 13th December. These dates are subject to change. Please consult https://status.ninjaone.com for the latest information. This integration allows you to populate custom fields with Tenant information, monitor device compliance state, document other items and generate relationships inside NinjaOne.", + "SettingOptions": [ + { + "type": "input", + "fieldtype": "input", + "name": "NinjaOne.Instance", + "label": "Please enter your NinjaOne Instance", + "placeholder": "app.ninjarmm.com, eu.ninjarmm.com, oc.ninjarmm.com, ca.ninjarmm.com, us2.ninjarmm.com" + }, + { + "type": "input", + "fieldtype": "password", + "name": "NinjaOne.ClientID", + "label": "NinjaOne API Client ID", + "placeholder": "Enter your NinjaOne API Client ID" + }, + { + "type": "input", + "fieldtype": "password", + "name": "NinjaOne.TEMPSECRETCHANGEME", + "label": "NinjaOne API Client Secret", + "placeholder": "Enter your NinjaOne API Client Secret" + }, + { + "type": "checkbox", + "name": "NinjaOne.UserDocumentsEnabled", + "label": "Synchronize Detailed User Information (Requires NinjaOne Documentation)" + }, + { + "type": "checkbox", + "name": "NinjaOne.LicenseDocumentsEnabled", + "label": "Synchronize Detailed License Information (Requires NinjaOne Documentation)" + }, + { + "type": "checkbox", + "name": "NinjaOne.LicensedOnly", + "label": "Only Synchronize Licensed Users" + }, + { + "type": "checkbox", + "name": "NinjaOne.Enabled", + "label": "Enable Integration" + } + ] } ] diff --git a/src/views/cipp/CIPPSettings.jsx b/src/views/cipp/CIPPSettings.jsx index daf8d6c507b8..12478b286666 100644 --- a/src/views/cipp/CIPPSettings.jsx +++ b/src/views/cipp/CIPPSettings.jsx @@ -1766,7 +1766,15 @@ const ExtensionsTab = () => { const MappingsTab = () => { const [listHaloBackend, listBackendHaloResult = []] = useLazyGenericGetRequestQuery() + const [listNinjaOrgsBackend, listBackendNinjaOrgsResult] = useLazyGenericGetRequestQuery() + const [listNinjaFieldsBackend, listBackendNinjaFieldsResult] = useLazyGenericGetRequestQuery() const [setHaloExtensionconfig, extensionHaloConfigResult = []] = useLazyGenericPostRequestQuery() + const [setNinjaOrgsExtensionconfig, extensionNinjaOrgsConfigResult] = + useLazyGenericPostRequestQuery() + const [setNinjaOrgsExtensionAutomap, extensionNinjaOrgsAutomapResult] = + useLazyGenericPostRequestQuery() + const [setNinjaFieldsExtensionconfig, extensionNinjaFieldsConfigResult] = + useLazyGenericPostRequestQuery() const onHaloSubmit = (values) => { setHaloExtensionconfig({ @@ -1774,10 +1782,38 @@ const MappingsTab = () => { values: { mappings: values }, }) } + const onNinjaOrgsSubmit = (values) => { + setNinjaOrgsExtensionconfig({ + path: 'api/ExecExtensionMapping?AddMapping=NinjaOrgs', + values: { mappings: values }, + }) + } + + const onNinjaOrgsAutomap = async (values) => { + await setNinjaOrgsExtensionAutomap({ + path: 'api/ExecExtensionMapping?AutoMapping=NinjaOrgs', + values: { mappings: values }, + }) + await listNinjaOrgsBackend({ + path: 'api/ExecExtensionMapping?List=NinjaOrgs', + }) + } + + const onNinjaFieldsSubmit = (values) => { + setNinjaFieldsExtensionconfig({ + path: 'api/ExecExtensionMapping?AddMapping=NinjaFields', + + values: { mappings: values }, + }) + } return (
{listBackendHaloResult.isUninitialized && listHaloBackend({ path: 'api/ExecExtensionMapping?List=Halo' })} + {listBackendNinjaOrgsResult.isUninitialized && + listNinjaOrgsBackend({ path: 'api/ExecExtensionMapping?List=NinjaOrgs' })} + {listBackendNinjaFieldsResult.isUninitialized && + listNinjaFieldsBackend({ path: 'api/ExecExtensionMapping?List=NinjaFields' })} <> @@ -1831,6 +1867,154 @@ const MappingsTab = () => { )} + + + NinjaOne Organization Mapping Table + + + {listBackendNinjaOrgsResult.isFetching ? ( + + ) : ( +
{ + return ( + + + Use the table below to map your client to the correct NinjaOne Organization + {listBackendNinjaOrgsResult.isSuccess && + listBackendNinjaOrgsResult.data.Tenants.map((tenant) => ( + + ))} + + + + {extensionNinjaOrgsConfigResult.isFetching && ( + + )} + Set Mappings + + onNinjaOrgsAutomap()} className="me-2"> + {extensionNinjaOrgsAutomapResult.isFetching && ( + + )} + Automap NinjaOne Organizations + + {(extensionNinjaOrgsConfigResult.isSuccess || + extensionNinjaOrgsConfigResult.isError) && ( + + {extensionNinjaOrgsConfigResult.isSuccess + ? extensionNinjaOrgsConfigResult.data.Results + : 'Error'} + + )} + {(extensionNinjaOrgsAutomapResult.isSuccess || + extensionNinjaOrgsAutomapResult.isError) && ( + + {extensionNinjaOrgsAutomapResult.isSuccess + ? extensionNinjaOrgsAutomapResult.data.Results + : 'Error'} + + )} + + + ) + }} + /> + )} + + + + + NinjaOne Field Mapping Table + + + {listBackendNinjaFieldsResult.isFetching ? ( + + ) : ( + { + return ( + + +
Organization Global Custom Field Mapping
+

+ Use the table below to map your Organization Field to the correct NinjaOne + Field +

+ {listBackendNinjaFieldsResult.isSuccess && + listBackendNinjaFieldsResult.data.CIPPOrgFields.map((CIPPOrgFields) => ( + item.type === CIPPOrgFields.Type || item.type === 'unset', + )} + placeholder="Select a Field" + /> + ))} +
+ +
Device Custom Field Mapping
+

+ Use the table below to map your Device field to the correct NinjaOne + WYSIWYG Field +

+ {listBackendNinjaFieldsResult.isSuccess && + listBackendNinjaFieldsResult.data.CIPPNodeFields.map((CIPPNodeFields) => ( + + item.type === CIPPNodeFields.Type || item.type === 'unset', + )} + placeholder="Select a Field" + /> + ))} +
+ + + {extensionNinjaFieldsConfigResult.isFetching && ( + + )} + Set Mappings + + {(extensionNinjaFieldsConfigResult.isSuccess || + extensionNinjaFieldsConfigResult.isError) && ( + + {extensionNinjaFieldsConfigResult.isSuccess + ? extensionNinjaFieldsConfigResult.data.Results + : 'Error'} + + )} + +
+ ) + }} + /> + )} +
+
) From 922a9a52419ea249b4121a0ddd2461e08cf7f039 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 21 Nov 2023 11:53:30 +0100 Subject: [PATCH 02/17] fixes links colours --- src/scss/_themes.scss | 4 ++-- src/scss/_variables.scss | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/scss/_themes.scss b/src/scss/_themes.scss index 04c8d346d806..85ffbda82362 100644 --- a/src/scss/_themes.scss +++ b/src/scss/_themes.scss @@ -264,7 +264,7 @@ --cui-input-focus-bg: var(--cyberdrain-light-striped); --cui-input-focus-border-colour: var(--cyberdrain-primary); --cui-input-focus-color: var(--cyberdrain-dark); - --cui-link-color: var(--cyberdrain-dark); + --cui-link-color-rgb: var(--cyberdrain-dark); --cui-link-hover-color: var(--cyberdrain-primary); --cui-list-group-bg: var(--cui-color-white) !important; --cui-list-group-color: var(--cyberdrain-dark); @@ -452,7 +452,7 @@ --cui-input-focus-bg: var(--cyberdrain-dark-striped); --cui-input-focus-border-color: var(--cyberdrain-primary); --cui-input-focus-color: var(--cyberdrain-primary); - --cui-link-color: var(--cyberdrain-light); + --cui-link-color-rgb: var(--cyberdrain-light); --cui-link-hover-color: var(--cyberdrain-primary); --cui-list-group-bg: var(--cyberdrain-dark); --cui-list-group-color: var(--cyberdrain-light); diff --git a/src/scss/_variables.scss b/src/scss/_variables.scss index 05ef2745afd3..099a132a062b 100644 --- a/src/scss/_variables.scss +++ b/src/scss/_variables.scss @@ -86,10 +86,10 @@ $secondary-base: rgba(0, 48, 73) !default; $secondary-50: rgba(0, 47, 73, 0.479) !default; $secondary-25: rgba(0, 47, 73, 0.205) !default; -$link-color: rgb(247, 247, 247) !default; +//$link-color: var(--cui-link-color) !default; $link-decoration: underline !default; $link-shade-percentage: 20% !default; -$link-hover-color: rgba($link-color, $link-shade-percentage) !default; +//$link-hover-color: rgba($link-color, $link-shade-percentage) !default; $link-hover-decoration: null !default; $search-bg-color: #ffffff; From e187d208106cd5d607e3d1095e67fbd728cc7214 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 21 Nov 2023 11:59:03 +0100 Subject: [PATCH 03/17] fixes hover --- src/scss/_themes.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scss/_themes.scss b/src/scss/_themes.scss index 85ffbda82362..54c25faff62e 100644 --- a/src/scss/_themes.scss +++ b/src/scss/_themes.scss @@ -312,7 +312,7 @@ --cipp-table-primary-colour: var(--cyberdrain-dark); --cipp-table-secondary-colour: var(--cyberdrain-secondary); --cipp-table-sort-focus-bg: var(--cyberdrain-secondary); - --cipp-table-highlight-on-hover-bg: rgb(150, 150, 150); + //--cipp-table-highlight-on-hover-bg: rgb(150, 150, 150); --cipp-table-highlight-on-hover-color: rgb(150, 150, 150); --cipp-table-striped-bg: var(--cyberdrain-light-striped); --cipp-table-striped-colour: var(--cyberdrain-dark-striped); @@ -500,7 +500,7 @@ --cipp-table-primary-colour: var(--cyberdrain-light); --cipp-table-secondary-colour: var(--cui-gray-100); --cipp-table-sort-focus-bg: var(--cyberdrain-secondary); - --cipp-table-highlight-on-hover-bg: rgb(138, 136, 136); + // --cipp-table-highlight-on-hover-bg: rgb(138, 136, 136); --cipp-table-highlight-on-hover-color: rgb(138, 136, 136); --cipp-table-striped-bg: var(--cyberdrain-dark-striped); --cipp-table-striped-colour: var(--cyberdrain-light-striped); From cca63a3b2a02a9f8bb54c8e2eb2263f9769adc30 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 21 Nov 2023 15:09:09 +0100 Subject: [PATCH 04/17] fixes footer and zindex issues --- src/components/layout/AppFooter.jsx | 2 +- src/scss/_custom.scss | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/layout/AppFooter.jsx b/src/components/layout/AppFooter.jsx index 967f7bb3d1ff..0ed8a1a5f2a7 100644 --- a/src/components/layout/AppFooter.jsx +++ b/src/components/layout/AppFooter.jsx @@ -8,7 +8,7 @@ import netfriends from 'src/assets/images/netfriends.png' //todo: Add darkmode detection and change logos accordingly. const AppFooter = () => { return ( - +

This application is sponsored by{' '} diff --git a/src/scss/_custom.scss b/src/scss/_custom.scss index 09dfc85fe1a4..4404602e90a4 100644 --- a/src/scss/_custom.scss +++ b/src/scss/_custom.scss @@ -706,3 +706,10 @@ i.glyphicon { .array-item-remove::after { content: 'Remove'; } + +.stickyfooter { + position: sticky; + bottom: 0; + z-index: 0; /* Adjust this value as needed, should be lower than dropdowns */ + /* Other styling as needed */ +} From cc51b8e64ddc024eb42612f532cbc4a593394276 Mon Sep 17 00:00:00 2001 From: Mikey O'Toole Date: Tue, 21 Nov 2023 16:38:50 +0000 Subject: [PATCH 05/17] Link to edit mailbox permissions page should pass userId not userEmail. --- src/views/identity/administration/UserActions.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/identity/administration/UserActions.jsx b/src/views/identity/administration/UserActions.jsx index 756e349914bf..8d2219883a70 100644 --- a/src/views/identity/administration/UserActions.jsx +++ b/src/views/identity/administration/UserActions.jsx @@ -32,7 +32,7 @@ export default function UserActions({ tenantDomain, userId, userEmail, className } const editLink = `/identity/administration/users/edit?tenantDomain=${tenantDomain}&userId=${userId}` - const editMailboxLink = `/email/administration/edit-mailbox-permissions?tenantDomain=${tenantDomain}&userId=${userEmail}` + const editMailboxLink = `/email/administration/edit-mailbox-permissions?tenantDomain=${tenantDomain}&userId=${userId}` const actions = [ { From 3f59914519de270c3c31ffea4a15cf467f2e64db Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Thu, 23 Nov 2023 22:11:08 +0000 Subject: [PATCH 06/17] Update Extensions.json --- 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 c83bfbf51ce5..c8916adcb683 100644 --- a/src/data/Extensions.json +++ b/src/data/Extensions.json @@ -112,7 +112,7 @@ "type": "NinjaOne", "cat": "Documentation & Monitoring", "forceSyncButton": true, - "helpText": "NOTE: This integration requires version 5.6 of NinjaOne. Current release dates are: ca.ninjarmm.com - 28th November, oc.ninjarmm.com - 29th November, eu.ninjarmm.com - 4th December, us2.ninjarmm.com - 12th December, app.ninjarmm.com - 13th December. These dates are subject to change. Please consult https://status.ninjaone.com for the latest information. This integration allows you to populate custom fields with Tenant information, monitor device compliance state, document other items and generate relationships inside NinjaOne.", + "helpText": "NOTE: This integration requires version 5.6 of NinjaOne, which rolls out regionally between the end of November and mid-December. This integration allows you to populate custom fields with Tenant information, monitor device compliance state, document other items and generate relationships inside NinjaOne.", "SettingOptions": [ { "type": "input", From f15153b544c31b6abeeef150bf8866cd66f3048e Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Fri, 24 Nov 2023 12:57:11 +0000 Subject: [PATCH 07/17] Set Password Storage --- src/data/Extensions.json | 2 +- src/views/cipp/CIPPSettings.jsx | 136 ++++++++++++++++---------------- 2 files changed, 69 insertions(+), 69 deletions(-) diff --git a/src/data/Extensions.json b/src/data/Extensions.json index c8916adcb683..4d31c1df3dbc 100644 --- a/src/data/Extensions.json +++ b/src/data/Extensions.json @@ -131,7 +131,7 @@ { "type": "input", "fieldtype": "password", - "name": "NinjaOne.TEMPSECRETCHANGEME", + "name": "NinjaOne.APIKey", "label": "NinjaOne API Client Secret", "placeholder": "Enter your NinjaOne API Client Secret" }, diff --git a/src/views/cipp/CIPPSettings.jsx b/src/views/cipp/CIPPSettings.jsx index 12478b286666..1c1f82a32759 100644 --- a/src/views/cipp/CIPPSettings.jsx +++ b/src/views/cipp/CIPPSettings.jsx @@ -1867,74 +1867,6 @@ const MappingsTab = () => { )} - - - NinjaOne Organization Mapping Table - - - {listBackendNinjaOrgsResult.isFetching ? ( - - ) : ( - { - return ( - - - Use the table below to map your client to the correct NinjaOne Organization - {listBackendNinjaOrgsResult.isSuccess && - listBackendNinjaOrgsResult.data.Tenants.map((tenant) => ( - - ))} - - - - {extensionNinjaOrgsConfigResult.isFetching && ( - - )} - Set Mappings - - onNinjaOrgsAutomap()} className="me-2"> - {extensionNinjaOrgsAutomapResult.isFetching && ( - - )} - Automap NinjaOne Organizations - - {(extensionNinjaOrgsConfigResult.isSuccess || - extensionNinjaOrgsConfigResult.isError) && ( - - {extensionNinjaOrgsConfigResult.isSuccess - ? extensionNinjaOrgsConfigResult.data.Results - : 'Error'} - - )} - {(extensionNinjaOrgsAutomapResult.isSuccess || - extensionNinjaOrgsAutomapResult.isError) && ( - - {extensionNinjaOrgsAutomapResult.isSuccess - ? extensionNinjaOrgsAutomapResult.data.Results - : 'Error'} - - )} - - - ) - }} - /> - )} - - NinjaOne Field Mapping Table @@ -2015,6 +1947,74 @@ const MappingsTab = () => { )} + + + NinjaOne Organization Mapping Table + + + {listBackendNinjaOrgsResult.isFetching ? ( + + ) : ( + { + return ( + + + Use the table below to map your client to the correct NinjaOne Organization + {listBackendNinjaOrgsResult.isSuccess && + listBackendNinjaOrgsResult.data.Tenants.map((tenant) => ( + + ))} + + + + {extensionNinjaOrgsConfigResult.isFetching && ( + + )} + Set Mappings + + onNinjaOrgsAutomap()} className="me-2"> + {extensionNinjaOrgsAutomapResult.isFetching && ( + + )} + Automap NinjaOne Organizations + + {(extensionNinjaOrgsConfigResult.isSuccess || + extensionNinjaOrgsConfigResult.isError) && ( + + {extensionNinjaOrgsConfigResult.isSuccess + ? extensionNinjaOrgsConfigResult.data.Results + : 'Error'} + + )} + {(extensionNinjaOrgsAutomapResult.isSuccess || + extensionNinjaOrgsAutomapResult.isError) && ( + + {extensionNinjaOrgsAutomapResult.isSuccess + ? extensionNinjaOrgsAutomapResult.data.Results + : 'Error'} + + )} + + + ) + }} + /> + )} + +

) From 39c541ae1855f901187046404c51eef1592a0a39 Mon Sep 17 00:00:00 2001 From: lwhitelock <79275328+lwhitelock@users.noreply.github.com> Date: Fri, 24 Nov 2023 13:15:53 +0000 Subject: [PATCH 08/17] Added NinjaOne in app logo --- src/assets/images/ninjaone.png | Bin 0 -> 13636 bytes src/components/layout/AppFooter.jsx | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 src/assets/images/ninjaone.png diff --git a/src/assets/images/ninjaone.png b/src/assets/images/ninjaone.png new file mode 100644 index 0000000000000000000000000000000000000000..a5508a19fd922cf61b3403bec67af21a727e5e19 GIT binary patch literal 13636 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 W?aa6C<9}zQkUq+)%2Y}j2mU`X0q?E= literal 0 HcmV?d00001 diff --git a/src/components/layout/AppFooter.jsx b/src/components/layout/AppFooter.jsx index 0ed8a1a5f2a7..1830b0201999 100644 --- a/src/components/layout/AppFooter.jsx +++ b/src/components/layout/AppFooter.jsx @@ -5,6 +5,7 @@ import huntressLogo from 'src/assets/images/huntress_teal.png' import dattoLogo from 'src/assets/images/datto.png' import rewstLogo from 'src/assets/images/rewst.png' import netfriends from 'src/assets/images/netfriends.png' +import ninjaLogo from 'src/assets/images/ninjaone.png' //todo: Add darkmode detection and change logos accordingly. const AppFooter = () => { return ( @@ -24,6 +25,9 @@ const AppFooter = () => { + + +