From a09c458aed381431cc792c1a78c7775c702d3634 Mon Sep 17 00:00:00 2001 From: rdbsolutions Date: Thu, 27 Mar 2025 11:02:32 +0000 Subject: [PATCH 1/3] dmw preliminary work --- components/mapping/index.js | 54 +++++++++++++------ pages/_document.js | 1 + pages/api/endpoint/getdnscoords_api.js | 74 +++++++++++++++++++++++++- styles/sass/welshgov/_application.scss | 5 ++ 4 files changed, 118 insertions(+), 16 deletions(-) diff --git a/components/mapping/index.js b/components/mapping/index.js index c218594d..390c295a 100644 --- a/components/mapping/index.js +++ b/components/mapping/index.js @@ -1,3 +1,4 @@ +import { he } from "date-fns/locale"; import GoogleMapReact from "google-map-react"; const GoogleMapComponent = (props) => { const { center, zoom } = props; @@ -21,22 +22,45 @@ const GoogleMapComponent = (props) => { }); } }; + + // console.log( + // "https://datamap.gov.wales/maps/pedw-dev/embed?center=" + + // props.center.lng + + // "," + + // props.center.lat + + // "&zoom=12#/" + // ); return ( - ModelsMap(map, maps)} - > + // ModelsMap(map, maps)} + // > + + ); }; diff --git a/pages/_document.js b/pages/_document.js index 71b93947..b38fa74c 100644 --- a/pages/_document.js +++ b/pages/_document.js @@ -47,6 +47,7 @@ class MyDocument extends Document { csp += `style-src 'self' 'unsafe-inline' https://pro.fontawesome.com/ https://www.google-analytics.com https://tagmanager.google.com/ https://www.googletagmanager.com/ https://fonts.googleapis.com/;`; csp += `img-src 'self' 'unsafe-inline' https://maps.gstatic.com https://maps.googleapis.com https://www.gov.wales https://gov.wales https://fonts.gstatic.com https://www.googletagmanager.com https://www.google-analytics.com https://ssl.gstatic.com/ blob: data:;`; csp += `font-src 'self' 'unsafe-inline' https://pro.fontawesome.com/ https://fonts.gstatic.com/ data:;`; + csp += `frame-src https://datamap.gov.wales`; return ( diff --git a/pages/api/endpoint/getdnscoords_api.js b/pages/api/endpoint/getdnscoords_api.js index 3bdc0b55..8b09582b 100644 --- a/pages/api/endpoint/getdnscoords_api.js +++ b/pages/api/endpoint/getdnscoords_api.js @@ -21,6 +21,70 @@ const hashAPIPath = (queryPath) => { return (queryPath.indexOf("?") > -1 ? "&hash=" : "?hash=") + hashlink; }; +const renameKeys = (obj) => { + const keyMappings = { + "pinswg_projectlocation": "location", + "pinswg_mapzoomlevel": "zoomLevel", + "pinswg_name": "projectName", + "pinswg_anticipatedgridreferenceeastingtext": "easting", + "pinswg_anticipatedgridreferencenorthingtext": "northing", + "pinswg_anticipatedgridrefeasting": "easting", + "pinswg_anticipatedgridrefnorthing": "northing", + "pinswg_projectname": "projectTitle", + "pinswg_dnsid": "dnsId", + "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue": + "lpaDisplayName", + "_pinswg_associatedlpa_value": "lpaId", + "_pinswg_appellant_value@OData.Community.Display.V1.FormattedValue": + "appellantDisplayName", + "_pinswg_appellant_value": "appellantId", + "zoom": "mapZoom", + "center": "locationCenter", + }; + + let renamedObj = {}; + Object.keys(obj).forEach((key) => { + // Skip keys that need to be removed + if (keysToRemove.includes(key)) { + return; + } + + // Check if the key exists in the mapping + if (keyMappings[key]) { + renamedObj[keyMappings[key]] = obj[key]; + } else { + renamedObj[key] = obj[key]; + } + }); + + // Recursively rename keys in nested objects + if (obj.center) { + renamedObj.locationCenter = { + lat: obj.center.lat, + lng: obj.center.lng, + }; + } + + if (renamedObj.easting) { + renamedObj.easting = String(renamedObj.easting); + } + if (renamedObj.northing) { + renamedObj.northing = String(renamedObj.northing); + } + + return renamedObj; +}; + +const keysToRemove = [ + "_pinswg_associatedlpa_value@Microsoft.Dynamics.CRM.associatednavigationproperty", + "_pinswg_appellant_value@Microsoft.Dynamics.CRM.associatednavigationproperty", + "_pinswg_associatedlpa_value@Microsoft.Dynamics.CRM.lookuplogicalname", + "_pinswg_appellant_value@Microsoft.Dynamics.CRM.lookuplogicalname", + "pinswg_anticipatedgridrefeasting@OData.Community.Display.V1.FormattedValue", + "pinswg_anticipatedgridrefnorthing@OData.Community.Display.V1.FormattedValue", + // Add any other keys to remove here +]; + export default async function ApiProxy(req, res) { var token = await getToken(); @@ -90,7 +154,15 @@ export default async function ApiProxy(req, res) { }); // Send the merged values as the response - return res.status(200).json(coordsObj); + + const updatedData = { + value: coordsObj.value.map((item) => renameKeys(item)), + "@odata.count": coordsObj["@odata.count"], + }; + + return req.query.hasOwnProperty("fordmw") + ? res.status(200).json(updatedData) + : res.status(200).json(coordsObj); } catch (error) { // Catch any errors and send an error response consoleLogger(error); diff --git a/styles/sass/welshgov/_application.scss b/styles/sass/welshgov/_application.scss index 87e36f36..02b49b02 100644 --- a/styles/sass/welshgov/_application.scss +++ b/styles/sass/welshgov/_application.scss @@ -1936,6 +1936,11 @@ ul#sharePageLinks.active { position: absolute; } + +.mappingFrame { + border: 1px solid $lightgrey +} + //dns application view .govuk-warning-text__icon { From 8cca0db6e920f2a39b0c59d01cb02e358432728b Mon Sep 17 00:00:00 2001 From: rdbsolutions Date: Thu, 27 Mar 2025 11:03:24 +0000 Subject: [PATCH 2/3] mime type fix for ithc --- package.json | 1 + pages/api/file/uploadsinglefile.js | 74 ++++++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 3bc3b484..04575588 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "date-fns": "^2.30.0", "dom": "^0.0.3", "express": "^4.18.2", + "file-type": "^20.4.1", "fs": "0.0.2", "google-map-react": "^2.2.1", "govuk-frontend": "^5.0.0", diff --git a/pages/api/file/uploadsinglefile.js b/pages/api/file/uploadsinglefile.js index 382c0888..d84d239a 100644 --- a/pages/api/file/uploadsinglefile.js +++ b/pages/api/file/uploadsinglefile.js @@ -7,6 +7,9 @@ import { import nextConnect from "next-connect"; import middleware from "../middleware/middleware"; import { consoleLogger } from "../../../actions"; +import { fileTypeFromBuffer } from "file-type"; +import fs from "fs"; +import path from "path"; const ApiProxy = nextConnect(); ApiProxy.use(middleware); @@ -36,19 +39,70 @@ ApiProxy.post(async (req, res) => { var allowedFilesFormData = {}; + // for (const [fileName, fileDetails] of Object.entries(req.files)) { + // if (allowedMimeTypes.includes(fileDetails[0].headers["content-type"])) { + // allowedFilesFormData[fileName] = fileDetails.map((file) => ({ + // fieldName: file.fieldName, + // originalFilename: file.originalFilename, + // path: file.path, + // size: file.size, + // headers: file.headers, + // contentType: file.headers["content-type"], + // })); + // } + // } + for (const [fileName, fileDetails] of Object.entries(req.files)) { - if (allowedMimeTypes.includes(fileDetails[0].headers["content-type"])) { - allowedFilesFormData[fileName] = fileDetails.map((file) => ({ - fieldName: file.fieldName, - originalFilename: file.originalFilename, - path: file.path, - size: file.size, - headers: file.headers, - contentType: file.headers["content-type"], - })); + const file = fileDetails[0]; + const filePath = file.path; + + // Check the MIME type from file headers + const fileMimeType = file.headers["content-type"]; + if (allowedMimeTypes.includes(fileMimeType)) { + // Check file signature (magic number) using file-type + const buffer = fs.readFileSync(filePath); + + try { + const type = await fileTypeFromBuffer(buffer); // Correct usage of fileTypeFromBuffer + console.log("checking mime type ", type); + if (type && allowedMimeTypes.includes(type.mime)) { + // If the MIME type from the file signature matches the allowed list + allowedFilesFormData[fileName] = fileDetails.map( + (file) => ({ + fieldName: file.fieldName, + originalFilename: file.originalFilename, + path: file.path, + size: file.size, + headers: file.headers, + contentType: fileMimeType, + }) + ); + } else { + console.log( + `\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n\nFile ${fileName} has an invalid MIME type based on its content.\n\n\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\` + ); + return res + .status(400) + .json({ error: `Invalid file type for ${fileName}` }); + } + } catch (error) { + console.log( + `Error reading file ${fileName} for MIME type validation`, + error + ); + return res + .status(500) + .json({ error: "Failed to validate file type" }); + } + } else { + console.log( + `\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n\nFile ${fileName} has an unsupported MIME type: ${fileMimeType}\n\n\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\` + ); + return res.status(400).json({ + error: `Unsupported MIME type for ${fileName}`, + }); } } - //console.log("allowedFilesFormData: ", allowedFilesFormData); try { From 1a740bd78b194b1bf9c94461936649fdf485a0c9 Mon Sep 17 00:00:00 2001 From: rdbsolutions Date: Fri, 28 Mar 2025 06:38:06 +0000 Subject: [PATCH 3/3] 15439 fix for app error in advanced search --- components/mapping/index.js | 54 +++++++++++++++++++-------- components/search/advancedsearch.js | 16 ++++++-- components/search/dnssearchresults.js | 23 +++++++++--- pages/api/file/uploadsinglefile.js | 13 ------- 4 files changed, 68 insertions(+), 38 deletions(-) diff --git a/components/mapping/index.js b/components/mapping/index.js index 390c295a..9cf76758 100644 --- a/components/mapping/index.js +++ b/components/mapping/index.js @@ -46,21 +46,45 @@ const GoogleMapComponent = (props) => { // yesIWantToUseGoogleMapApiInternals // onGoogleApiLoaded={({ map, maps }) => ModelsMap(map, maps)} // > - - + <> + {" "} +
+
+ + See this map on Datamap Wales + +
+
+
+
+ +
+
+ ); }; diff --git a/components/search/advancedsearch.js b/components/search/advancedsearch.js index 7fc0022c..1c23b9a1 100644 --- a/components/search/advancedsearch.js +++ b/components/search/advancedsearch.js @@ -580,10 +580,10 @@ let AdvancedSearch = (props) => { label={t("search:appealtype-label")} errormsg="Appeal type is required" /> - {props.props?.props.form[ - props.form - ]?.values?.appealTypes.split(",")[0] === - "846040002" && ( + + {props.appealTypesValue?.split( + "," + )[0] === "846040002" && ( { const selector = formValueSelector("advancedSearchForm"); // <-- same as form name +AdvancedSearch = connect((state) => { + const appealTypesValue = selector(state, "appealTypes"); + + return { + appealTypesValue, + }; +})(AdvancedSearch); + export default connect( mapStateToProps, mapDispatchToProps diff --git a/components/search/dnssearchresults.js b/components/search/dnssearchresults.js index 2433cca5..29694ff9 100644 --- a/components/search/dnssearchresults.js +++ b/components/search/dnssearchresults.js @@ -753,12 +753,12 @@ const DNSSearchResults = (props) => { {showMap == "true" && (
- { onGoogleApiLoaded={({ map, maps }) => ModelsMap(map, maps) } - /> + /> */} + +
)} diff --git a/pages/api/file/uploadsinglefile.js b/pages/api/file/uploadsinglefile.js index ec827955..bc7af6e0 100644 --- a/pages/api/file/uploadsinglefile.js +++ b/pages/api/file/uploadsinglefile.js @@ -53,19 +53,6 @@ ApiProxy.post(async (req, res) => { // } // } - // for (const [fileName, fileDetails] of Object.entries(req.files)) { - // if (allowedMimeTypes.includes(fileDetails[0].headers["content-type"])) { - // allowedFilesFormData[fileName] = fileDetails.map((file) => ({ - // fieldName: file.fieldName, - // originalFilename: file.originalFilename, - // path: file.path, - // size: file.size, - // headers: file.headers, - // contentType: file.headers["content-type"], - // })); - // } - // } - for (const [fileName, fileDetails] of Object.entries(req.files)) { const file = fileDetails[0]; const filePath = file.path;