diff --git a/.github/workflows/PR_Branch_Check.yml b/.github/workflows/PR_Branch_Check.yml new file mode 100644 index 000000000000..2fd5b8e65249 --- /dev/null +++ b/.github/workflows/PR_Branch_Check.yml @@ -0,0 +1,62 @@ +name: PR Branch Check + +on: + # Using pull_request_target instead of pull_request for secure handling of fork PRs + pull_request_target: + # Only run on these PR events + types: [opened, synchronize, reopened] + # Only check PRs targeting these branches + branches: + - main + - master + +permissions: + pull-requests: write + issues: write + +jobs: + check-branch: + runs-on: ubuntu-latest + steps: + - name: Check and Comment on PR + # Only process fork PRs with specific branch conditions + # Must be a fork AND (source is main/master OR target is main/master) + if: | + github.event.pull_request.head.repo.fork == true && + ((github.event.pull_request.head.ref == 'main' || github.event.pull_request.head.ref == 'master') || + (github.event.pull_request.base.ref == 'main' || github.event.pull_request.base.ref == 'master')) + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + let message = ''; + + message += '🔄 If you are attempting to update your CIPP repo please follow the instructions at: https://docs.cipp.app/setup/self-hosting-guide/updating '; + message += '\n\n'; + + // Check if PR is targeting main/master + if (context.payload.pull_request.base.ref === 'main' || context.payload.pull_request.base.ref === 'master') { + message += '⚠️ PRs cannot target the main branch directly. If you are attempting to contribute code please PR to the dev branch.\n\n'; + } + + // Check if PR is from a fork's main/master branch + if (context.payload.pull_request.head.repo.fork && + (context.payload.pull_request.head.ref === 'main' || context.payload.pull_request.head.ref === 'master')) { + message += '⚠️ This PR cannot be merged because it originates from your fork\'s main/master branch. If you are attempting to contribute code please PR from your dev branch or another non-main/master branch.\n\n'; + } + + message += '🔒 This PR will now be automatically closed due to the above violation(s).'; + + // Post the comment + await github.rest.issues.createComment({ + ...context.repo, + issue_number: context.issue.number, + body: message + }); + + // Close the PR + await github.rest.pulls.update({ + ...context.repo, + pull_number: context.issue.number, + state: 'closed' + }); diff --git a/.github/workflows/dev_deploy.yml b/.github/workflows/dev_deploy.yml index 9c4e74f79fc9..5a8c8dfe933d 100644 --- a/.github/workflows/dev_deploy.yml +++ b/.github/workflows/dev_deploy.yml @@ -3,7 +3,7 @@ name: CIPP Development Frontend CI/CD on: push: branches: - - interface-rewrite + - dev jobs: build_and_deploy_job: diff --git a/src/components/CippComponents/CippApiDialog.jsx b/src/components/CippComponents/CippApiDialog.jsx index 84bf69a17599..282d8bab7b3b 100644 --- a/src/components/CippComponents/CippApiDialog.jsx +++ b/src/components/CippComponents/CippApiDialog.jsx @@ -209,10 +209,10 @@ export const CippApiDialog = (props) => { } useEffect(() => { if (api.noConfirm) { - formHook.handleSubmit(onSubmit)(); // Submits the form on mount - createDialog.handleClose(); // Closes the dialog after submitting + formHook.handleSubmit(onSubmit)(); + createDialog.handleClose(); } - }, [api.noConfirm]); // Run effect only when api.noConfirm changes + }, [api.noConfirm]); const handleClose = () => { createDialog.handleClose(); diff --git a/src/components/CippFormPages/CippAddGroupTemplateForm.jsx b/src/components/CippFormPages/CippAddGroupTemplateForm.jsx index 8e638142c9fe..a9362038e9d0 100644 --- a/src/components/CippFormPages/CippAddGroupTemplateForm.jsx +++ b/src/components/CippFormPages/CippAddGroupTemplateForm.jsx @@ -13,6 +13,7 @@ const CippAddGroupTemplateForm = (props) => { type="textField" label="Display Name" name="displayName" + required formControl={formControl} fullWidth /> diff --git a/src/components/CippIntegrations/CippIntegrationFieldMapping.jsx b/src/components/CippIntegrations/CippIntegrationFieldMapping.jsx index 9157364fe66b..8b46afd209f7 100644 --- a/src/components/CippIntegrations/CippIntegrationFieldMapping.jsx +++ b/src/components/CippIntegrations/CippIntegrationFieldMapping.jsx @@ -44,7 +44,7 @@ const CippIntegrationFieldMapping = () => { var missingMappings = []; fieldMapping?.data?.Mappings?.forEach((mapping) => { const exists = fieldMapping?.data?.IntegrationFields?.some( - (integrationField) => integrationField.value === mapping.IntegrationId + (integrationField) => String(integrationField.value) === mapping.IntegrationId ); if (exists) { newMappings[mapping.RowKey] = { diff --git a/src/components/CippIntegrations/CippIntegrationTenantMapping.jsx b/src/components/CippIntegrations/CippIntegrationTenantMapping.jsx index 8c3c9fd15aff..4461db5a8bc9 100644 --- a/src/components/CippIntegrations/CippIntegrationTenantMapping.jsx +++ b/src/components/CippIntegrations/CippIntegrationTenantMapping.jsx @@ -81,10 +81,10 @@ const CippIntegrationSettings = ({ children }) => { const selectedTenant = formControl.getValues("tenantFilter"); const selectedCompany = formControl.getValues("integrationCompany"); if (!selectedTenant || !selectedCompany) return; - if (tableData?.find((item) => item.TenantId === selectedTenant.value)) return; + if (tableData?.find((item) => item.TenantId === selectedTenant.addedFields.customerId)) return; const newRowData = { - TenantId: selectedTenant.value, + TenantId: selectedTenant.addedFields.customerId, Tenant: selectedTenant.label, IntegrationName: selectedCompany.label, IntegrationId: selectedCompany.value, diff --git a/src/components/CippStandards/CippStandardsSideBar.jsx b/src/components/CippStandards/CippStandardsSideBar.jsx index 43a827138a09..b65a83299312 100644 --- a/src/components/CippStandards/CippStandardsSideBar.jsx +++ b/src/components/CippStandards/CippStandardsSideBar.jsx @@ -228,7 +228,11 @@ const CippStandardsSideBar = ({ }} row={formControl.getValues()} formControl={formControl} - relatedQueryKeys={"listStandardTemplates"} + relatedQueryKeys={[ + "listStandardTemplates", + "listStandards", + `listStandardTemplates-${watchForm.GUID}`, + ]} /> ); diff --git a/src/components/CippTable/util-columnsFromAPI.js b/src/components/CippTable/util-columnsFromAPI.js index 0d04d20d0800..886a6f1d258b 100644 --- a/src/components/CippTable/util-columnsFromAPI.js +++ b/src/components/CippTable/util-columnsFromAPI.js @@ -6,14 +6,15 @@ import { getCippTranslation } from "../../utils/get-cipp-translation"; const mergeKeys = (dataArray) => { return dataArray.reduce((acc, item) => { const mergeRecursive = (obj, base = {}) => { - Object?.keys(obj)?.forEach((key) => { - // If base[key] is a string, it should not be merged as an object - if (typeof base[key] === "string") { - return; // Skip further merging for this key - } - + Object.keys(obj).forEach((key) => { if (typeof obj[key] === "object" && obj[key] !== null && !Array.isArray(obj[key])) { + if (typeof base[key] === "boolean") { + // Skip merging if base[key] is a boolean + return; + } base[key] = mergeRecursive(obj[key], base[key] || {}); + } else if (typeof obj[key] === "boolean") { + base[key] = obj[key]; } else if (typeof obj[key] === "string" && obj[key].toUpperCase() === "FAILED") { base[key] = base[key]; // Keep existing value if it's 'FAILED' } else if (obj[key] !== undefined && obj[key] !== null) { diff --git a/src/data/Extensions.json b/src/data/Extensions.json index bf70ef5b6a83..0110e7d63729 100644 --- a/src/data/Extensions.json +++ b/src/data/Extensions.json @@ -334,7 +334,7 @@ "logoDark": "/assets/integrations/pwpush_dark.png", "forceSyncButton": false, "description": "Enable the PasswordPusher integration to generate password links instead of plain text passwords.", - "helpText": "This integration allows you to generate password links instead of plain text passwords. Configure authentication and expiration settings that will apply to all generated passwords.", + "helpText": "This integration allows you to generate password links instead of plain text passwords. Configure authentication and expiration settings that will apply to all generated passwords. If you are a PWPush Pro customer and utilizing custom domains, please do not enter your custom domain in the Base URL field.", "links": [ { "name": "PWPush Documentation", diff --git a/src/layouts/config.js b/src/layouts/config.js index e74843605127..39d5f0c8714e 100644 --- a/src/layouts/config.js +++ b/src/layouts/config.js @@ -236,7 +236,6 @@ export const nativeMenuItems = [ { title: "Autopilot Devices", path: "/endpoint/autopilot/list-devices" }, { title: "Add Autopilot Device", path: "/endpoint/autopilot/add-device" }, { title: "Profiles", path: "/endpoint/autopilot/list-profiles" }, - { title: "Add Profile", path: "/endpoint/autopilot/add-profile" }, { title: "Status Pages", path: "/endpoint/autopilot/list-status-pages" }, { title: "Add Status Page", path: "/endpoint/autopilot/add-status-page" }, ], diff --git a/src/pages/cipp/integrations/configure.js b/src/pages/cipp/integrations/configure.js index eccc98cfd826..52d4766d9c09 100644 --- a/src/pages/cipp/integrations/configure.js +++ b/src/pages/cipp/integrations/configure.js @@ -44,6 +44,9 @@ const Page = () => { }; const handleIntegrationTest = () => { + if (testQuery.waiting) { + actionTestResults.refetch(); + } setTestQuery({ url: "/api/ExecExtensionTest", data: { diff --git a/src/pages/email/connectionfilter/list-templates/index.js b/src/pages/email/connectionfilter/list-templates/index.js index acd7b58f0ac7..4a104db53a18 100644 --- a/src/pages/email/connectionfilter/list-templates/index.js +++ b/src/pages/email/connectionfilter/list-templates/index.js @@ -6,19 +6,13 @@ const Page = () => { const pageTitle = "Connection filter Templates"; const actions = [ - { - label: "View Template", - icon: , // Placeholder for the view icon - color: "success", - offCanvas: true, - }, { label: "Delete Template", type: "POST", url: "/api/RemoveConnectionfilterTemplate", data: { ID: "GUID" }, confirmText: "Do you want to delete the template?", - icon: , // Placeholder for the delete icon + icon: , color: "danger", }, ]; diff --git a/src/pages/email/connectors/list-connector-templates/index.js b/src/pages/email/connectors/list-connector-templates/index.js index 02e97d3ca1f5..671c148f21e2 100644 --- a/src/pages/email/connectors/list-connector-templates/index.js +++ b/src/pages/email/connectors/list-connector-templates/index.js @@ -15,7 +15,7 @@ const Page = () => { ID: "GUID", }, confirmText: "Do you want to delete the template?", - icon: , // Placeholder for delete icon + icon: , color: "danger", }, ]; diff --git a/src/pages/email/spamfilter/list-templates/index.js b/src/pages/email/spamfilter/list-templates/index.js index 4c27ca4da7ed..faa7f9a905a0 100644 --- a/src/pages/email/spamfilter/list-templates/index.js +++ b/src/pages/email/spamfilter/list-templates/index.js @@ -6,12 +6,6 @@ const Page = () => { const pageTitle = "Spamfilter Templates"; const actions = [ - { - label: "View Template", - icon: , // Placeholder for the view icon - color: "success", - offCanvas: true, - }, { label: "Delete Template", type: "POST", diff --git a/src/pages/email/transport/list-rules/index.js b/src/pages/email/transport/list-rules/index.js index 448c609e9265..7312f49794fc 100644 --- a/src/pages/email/transport/list-rules/index.js +++ b/src/pages/email/transport/list-rules/index.js @@ -1,6 +1,8 @@ import { Layout as DashboardLayout } from "/src/layouts/index.js"; import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; import { Button } from "@mui/material"; +import { Book, DoDisturb, Done } from "@mui/icons-material"; +import { TrashIcon } from "@heroicons/react/24/outline"; import Link from "next/link"; const Page = () => { @@ -11,8 +13,9 @@ const Page = () => { label: "Create template based on rule", type: "POST", url: "/api/AddTransportTemplate", - data: {}, // No extra data was specified for this action in the original file + postEntireRow: true, confirmText: "Are you sure you want to create a template based on this rule?", + icon: , }, { label: "Enable Rule", @@ -23,6 +26,7 @@ const Page = () => { GUID: "Guid", }, confirmText: "Are you sure you want to enable this rule?", + icon: , }, { label: "Disable Rule", @@ -33,6 +37,7 @@ const Page = () => { GUID: "Guid", }, confirmText: "Are you sure you want to disable this rule?", + icon: , }, { label: "Delete Rule", @@ -43,15 +48,24 @@ const Page = () => { }, confirmText: "Are you sure you want to delete this rule?", color: "danger", + icon: , }, ]; const offCanvas = { - extendedInfoFields: ["CreatedBy", "LastModifiedBy", "WhenChanged", "Description", "Guid"], + extendedInfoFields: [ + "Guid", + "CreatedBy", + "LastModifiedBy", + "WhenChanged", + "Name", + "Comments", + "Description", + ], actions: actions, }; - const simpleColumns = ["Name", "State", "Mode", "RuleErrorAction", "WhenChanged"]; + const simpleColumns = ["Name", "State", "Mode", "RuleErrorAction", "WhenChanged", "Comments"]; return ( { const pageTitle = "Transport Rule Templates"; const actions = [ - { - label: "View Template", - icon: , // Placeholder icon for developer customization - color: "success", - offCanvas: true, - }, { label: "Delete Template", type: "POST", url: "/api/RemoveTransportRuleTemplate", data: { ID: "GUID" }, confirmText: "Do you want to delete the template?", - icon: , // Placeholder icon for developer customization + icon: , color: "danger", }, ]; diff --git a/src/pages/endpoint/autopilot/add-profile/index.js b/src/pages/endpoint/autopilot/add-profile/index.js deleted file mode 100644 index 83f5fdb1a829..000000000000 --- a/src/pages/endpoint/autopilot/add-profile/index.js +++ /dev/null @@ -1,17 +0,0 @@ - -import { Layout as DashboardLayout } from "/src/layouts/index.js"; - -const Page = () => { - const pageTitle = "Add Profile"; - - return ( - - {pageTitle} - This is a placeholder page for the add profile section. - - ); -}; - -Page.getLayout = (page) => {page}; - -export default Page; diff --git a/src/pages/tenant/gdap-management/onboarding/start.js b/src/pages/tenant/gdap-management/onboarding/start.js index e04846851b4d..cb6a58d3d675 100644 --- a/src/pages/tenant/gdap-management/onboarding/start.js +++ b/src/pages/tenant/gdap-management/onboarding/start.js @@ -206,8 +206,8 @@ const Page = () => { var missingDefaults = []; cippDefaults.forEach((defaultRole) => { - if (!relationshipRoles?.find((role) => defaultRole.value === role.roleDefinitionId)) { - missingDefaults.push(role); + if (!relationshipRoles?.find((role) => defaultRole?.value === role?.roleDefinitionId)) { + missingDefaults.push(defaultRole); } }); setMissingDefaults(missingDefaults.length > 0); @@ -409,7 +409,9 @@ const Page = () => { {(currentInvite || selectedRole) && rolesMissingFromRelationship.length > 0 && ( The following roles are not mapped with the current template:{" "} - {rolesMissingFromRelationship.map((role) => role.Name).join(", ")} + {rolesMissingFromRelationship + .map((role) => role?.Name ?? "Unknown Role") + .join(", ")} )} {(currentInvite || selectedRole) && diff --git a/src/pages/tenant/standards/list-standards/index.js b/src/pages/tenant/standards/list-standards/index.js index 68f46ba5a249..e041ea9cd23b 100644 --- a/src/pages/tenant/standards/list-standards/index.js +++ b/src/pages/tenant/standards/list-standards/index.js @@ -118,6 +118,7 @@ const Page = () => { "updatedAt", "updatedBy", "runManually", + "standards", ]} queryKey="listStandardTemplates" /> diff --git a/src/pages/tenant/standards/template.jsx b/src/pages/tenant/standards/template.jsx index d71adc70227c..2a5934014bc6 100644 --- a/src/pages/tenant/standards/template.jsx +++ b/src/pages/tenant/standards/template.jsx @@ -92,6 +92,14 @@ const Page = () => { }; const handleAddMultipleStandard = (standardName) => { + //if the standardname contains an array qualifier,e.g standardName[0], strip that away. + const arrayPattern = /(.*)\[(\d+)\]$/; + const match = standardName.match(arrayPattern); + if (match) { + standardName = match[1]; + } + console.log("Adding multiple", standardName); + setSelectedStandards((prev) => { const existingInstances = Object.keys(prev).filter((name) => name.startsWith(standardName)); const newIndex = existingInstances.length; diff --git a/src/pages/tenant/tools/tenantlookup/index.js b/src/pages/tenant/tools/tenantlookup/index.js index 5c212c3d0751..ff8d73863b50 100644 --- a/src/pages/tenant/tools/tenantlookup/index.js +++ b/src/pages/tenant/tools/tenantlookup/index.js @@ -18,7 +18,7 @@ import { ApiGetCall } from "../../../../api/ApiCall"; const Page = () => { const formControl = useForm({ mode: "onBlur" }); const domain = useWatch({ control: formControl.control, name: "domain" }); - const getGeoIP = ApiGetCall({ + const getTenant = ApiGetCall({ url: "/api/ListExternalTenantInfo", data: { tenant: domain }, queryKey: `tenant-${domain}`, @@ -52,7 +52,7 @@ const Page = () => { getGeoIP.refetch()} + onClick={() => getTenant.refetch()} variant="contained" startIcon={} > @@ -64,7 +64,7 @@ const Page = () => { {/* Results Card */} - {getGeoIP.isFetching ? ( + {getTenant.isFetching ? ( @@ -74,25 +74,25 @@ const Page = () => { - ) : getGeoIP.data ? ( + ) : getTenant.data ? ( - + Tenant Name: {domain} - Tenant Id: {getGeoIP.data?.GraphRequest?.tenantId} + Tenant Id: {getTenant.data?.GraphRequest?.tenantId} Default Domain Name:{" "} - {getGeoIP.data?.GraphRequest?.defaultDomainName} + {getTenant.data?.GraphRequest?.defaultDomainName} Tenant Brand Name :{" "} - {getGeoIP.data?.GraphRequest?.federationBrandName - ? getGeoIP.data?.GraphRequest?.federationBrandName + {getTenant.data?.GraphRequest?.federationBrandName + ? getTenant.data?.GraphRequest?.federationBrandName : "N/A"} @@ -101,7 +101,7 @@ const Page = () => { domains: - {getGeoIP.data?.Domains?.map((domain) => ( + {getTenant.data?.Domains?.map((domain) => ( {domain}
This is a placeholder page for the add profile section.