From 5059a0301d63b011b405595781e84d37e15ee2e4 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 27 May 2024 16:05:13 -0400 Subject: [PATCH] Graph Explorer Scheduler Add CippScheduleOffcanvas --- .../utilities/CippScheduleOffcanvas.jsx | 273 ++++++++++++++++++ src/views/cipp/Scheduler.jsx | 1 - .../tenant/administration/GraphExplorer.jsx | 53 ++++ 3 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 src/components/utilities/CippScheduleOffcanvas.jsx diff --git a/src/components/utilities/CippScheduleOffcanvas.jsx b/src/components/utilities/CippScheduleOffcanvas.jsx new file mode 100644 index 000000000000..b040ff031192 --- /dev/null +++ b/src/components/utilities/CippScheduleOffcanvas.jsx @@ -0,0 +1,273 @@ +import React, { useState } from 'react' +import PropTypes from 'prop-types' +import { + CButton, + CCallout, + CCard, + CCardBody, + CCardHeader, + CCol, + CForm, + CRow, + CSpinner, + CTooltip, +} from '@coreui/react' +import { CippOffcanvas, TenantSelector } from '.' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { Field, Form, FormSpy } from 'react-final-form' +import arrayMutators from 'final-form-arrays' +import { + RFFCFormInput, + RFFCFormInputArray, + RFFCFormSwitch, + RFFSelectSearch, +} from 'src/components/forms' +import { useSelector } from 'react-redux' +import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' +import DatePicker from 'react-datepicker' +import 'react-datepicker/dist/react-datepicker.css' + +export default function CippScheduleOffcanvas({ + state: visible, + hideFunction, + title, + placement, + ...props +}) { + 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 { data: availableCommands = [], isLoading: isLoadingcmd } = useGenericGetRequestQuery({ + path: 'api/ListFunctionParameters?Module=CIPPCore', + }) + + const recurrenceOptions = [ + { value: '0', name: 'Only once' }, + { value: '1', name: 'Every 1 day' }, + { value: '7', name: 'Every 7 days' }, + { value: '30', name: 'Every 30 days' }, + { value: '365', name: 'Every 365 days' }, + ] + + 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, + AdditionalProperties: values.additional, + PostExecution: { + Webhook: values.webhook, + Email: values.email, + PSA: values.psa, + }, + } + genericPostRequest({ path: '/api/AddScheduledItem', values: shippedValues }).then((res) => { + setRefreshState(res.requestId) + }) + } + + return ( + + + + +
{ + return ( + + + + + {(props) => } + + + + + + + + + + + setStartDate(date)} + /> + + + + + + + + + + ({ + value: cmd.Function, + name: cmd.Function, + }))} + name="command" + placeholder={ + isLoadingcmd ? ( + + ) : ( + 'Select a command or report to execute.' + ) + } + label="Command to execute" + /> + + + + {/* eslint-disable react/prop-types */} + {(props) => { + const selectedCommand = availableCommands.find( + (cmd) => cmd.Function === props.values.command?.value, + ) + return ( + + {selectedCommand?.Synopsis} + + ) + }} + + + + {/* eslint-disable react/prop-types */} + {(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' || + param.Type === + 'System.Management.Automation.SwitchParameter' ? ( + <> + + + + ) : ( + <> + {param.Type === 'System.Collections.Hashtable' ? ( + + ) : ( + + )} + + )} + + + + )) + } + } + return paramblock + }} + + + + + + + + + + + + + + + + + + + Add Schedule + {postResults.isFetching && ( + + )} + + + + {postResults.isSuccess && ( + +
  • {postResults.data.Results}
  • +
    + )} +
    + ) + }} + /> + + + + ) +} + +CippScheduleOffcanvas.propTypes = { + groups: PropTypes.array, + placement: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + state: PropTypes.bool, + hideFunction: PropTypes.func.isRequired, +} diff --git a/src/views/cipp/Scheduler.jsx b/src/views/cipp/Scheduler.jsx index aa07fb3722b6..7ffed5837fe0 100644 --- a/src/views/cipp/Scheduler.jsx +++ b/src/views/cipp/Scheduler.jsx @@ -157,7 +157,6 @@ const Scheduler = () => { if (typeof row?.Parameters[key] === 'object') { var nestedParamList = [] Object.keys(row?.Parameters[key]).forEach((nestedKey) => { - console.log(nestedKey) nestedParamList.push({ Key: nestedKey, Value: row?.Parameters[key][nestedKey], diff --git a/src/views/tenant/administration/GraphExplorer.jsx b/src/views/tenant/administration/GraphExplorer.jsx index 3d6950c598d4..ebc3abd8b39d 100644 --- a/src/views/tenant/administration/GraphExplorer.jsx +++ b/src/views/tenant/administration/GraphExplorer.jsx @@ -32,6 +32,7 @@ import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' import PropTypes from 'prop-types' import { CippCodeOffCanvas, ModalService } from 'src/components/utilities' import { debounce } from 'lodash-es' +import CippScheduleOffcanvas from 'src/components/utilities/CippScheduleOffcanvas' const GraphExplorer = () => { const tenant = useSelector((state) => state.app.currentTenant) @@ -57,6 +58,8 @@ const GraphExplorer = () => { error: presetsError, } = useGenericGetRequestQuery({ path: '/api/ListGraphExplorerPresets', params: { random2 } }) const QueryColumns = { set: false, data: [] } + const [scheduleVisible, setScheduleVisible] = useState(false) + const [scheduleValues, setScheduleValues] = useState({}) const debounceEndpointChange = useMemo(() => { function endpointChange(value) { @@ -148,6 +151,36 @@ const GraphExplorer = () => { }) } + function handleSchedule(values) { + var graphParameters = [] + const paramNames = ['$filter', '$format', '$search', '$select', '$top'] + paramNames.map((param) => { + if (values[param]) { + if (Array.isArray(values[param])) { + graphParameters.push({ Key: param, Value: values[param].map((p) => p.value).join(',') }) + } else { + graphParameters.push({ Key: param, Value: values[param] }) + } + } + }) + + const reportName = values.name ?? 'Graph Explorer' + const shippedValues = { + taskName: reportName + ' - ' + tenant.displayName, + command: { label: 'Get-GraphRequestList', value: 'Get-GraphRequestList' }, + parameters: { + Parameters: graphParameters, + NoPagination: values.NoPagination, + ReverseTenantLookup: values.ReverseTenantLookup, + ReverseTenantLookupProperty: values.ReverseTenantLookupProperty, + Endpoint: values.endpoint, + SkipCache: true, + }, + } + setScheduleValues(shippedValues) + setScheduleVisible(true) + } + const presets = [ { name: 'All users with email addresses', @@ -617,6 +650,19 @@ const GraphExplorer = () => { Query + + {(props) => { + return ( + handleSchedule(props.values)} + className="ms-2" + > + + Schedule Report + + ) + }} + @@ -628,6 +674,13 @@ const GraphExplorer = () => { + setScheduleVisible(false)} + initialValues={scheduleValues} + />
    {!searchNow && Execute a search to get started.}