From 92c89d38cc017e309ba17f3d385d60da42a23665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Fichot?= Date: Fri, 27 Oct 2017 15:36:00 +0200 Subject: [PATCH] [gatsby-source-wordpress] Refactored featured_media map for deep nodes (#2648) * Refactored featured_media map for deep nodes * Featured medias coul be nested at any level * The most certain way to have photos works with this version on the plugin is to either name the fiel featured_media or include the image as Post Object. * Fixed bug where in some case the old featured_media field was not deleted. * Update normalize.js.snap * Integrated pieh 's changes * Most of the credits goes to @pieh for a better solution that solved the same problems and widely improved code simplicity. https://github.com/gatsbyjs/gatsby/pull/2646 * Made the Media lookup begin at JSON tree root instead of keeping this for ACF Field only. Changed constants names accordingly. * Making thise more generic allorws Custom Post Types to benefit from this improvements and yet unseen objects shapes. * Incorporated fix on bug mentionned here https://github.com/gatsbyjs/gatsby/pull/2646#commitcomment-25218740 * May solve this https://github.com/gatsbyjs/gatsby/issues/2587, this https://github.com/gatsbyjs/gatsby/issues/2492, this https://github.com/gatsbyjs/gatsby/issues/2328 * Update normalize.js.snap --- .../__tests__/__snapshots__/normalize.js.snap | 16 ++- .../gatsby-source-wordpress/src/normalize.js | 136 ++++++++++-------- 2 files changed, 88 insertions(+), 64 deletions(-) diff --git a/packages/gatsby-source-wordpress/src/__tests__/__snapshots__/normalize.js.snap b/packages/gatsby-source-wordpress/src/__tests__/__snapshots__/normalize.js.snap index 87c575723cb56..31335dc95743f 100644 --- a/packages/gatsby-source-wordpress/src/__tests__/__snapshots__/normalize.js.snap +++ b/packages/gatsby-source-wordpress/src/__tests__/__snapshots__/normalize.js.snap @@ -5790,6 +5790,7 @@ Array [ ], }, "acf": Object { + "dummy": true, "linked_image___NODE": "950513a1-a720-5588-b262-9533aa598624", }, "author___NODE": "ec1fb3e3-d897-5549-b5f6-72b358fbbe83", @@ -5858,7 +5859,7 @@ Array [ "date": "2017-09-02T10:36:43.000Z", "description": "

\\"\\"

", - "guid": "http://dev-gatbsyjswp.pantheonsite.io/wp-content/uploads/2017/09/pexels-photo-534327.jpeg", + "guid___NODE": "61ef7ef2-032f-5c90-bba3-99d04c9a1f07", "id": "61ef7ef2-032f-5c90-bba3-99d04c9a1f07", "link": "http://dev-gatbsyjswp.pantheonsite.io/sample-post-1/pexels-photo-534327/", "media_details": Object { @@ -5970,7 +5971,7 @@ Array [ "date": "2017-09-02T10:35:16.000Z", "description": "

\\"\\"

", - "guid": "http://dev-gatbsyjswp.pantheonsite.io/wp-content/uploads/2017/09/pexels-photo-534351.jpeg", + "guid___NODE": "950513a1-a720-5588-b262-9533aa598624", "id": "950513a1-a720-5588-b262-9533aa598624", "link": "http://dev-gatbsyjswp.pantheonsite.io/gatsby-sample-home-page/pexels-photo-534351/", "media_details": Object { @@ -11337,6 +11338,7 @@ Array [ ], }, "acf": Object { + "dummy": true, "linked_image___NODE": "950513a1-a720-5588-b262-9533aa598624", }, "author___NODE": "ec1fb3e3-d897-5549-b5f6-72b358fbbe83", @@ -11355,7 +11357,7 @@ Array [ "guid": "http://dev-gatbsyjswp.pantheonsite.io/?page_id=5", "id": "340ceded-7db7-583c-9486-120758f5d967", "internal": Object { - "contentDigest": "bf7491f5ede200a6f3ce8db4c28e3eca", + "contentDigest": "730b646cd1ae6cd070d74956e5041fd2", "type": "wordpress__PAGE", }, "link": "http://dev-gatbsyjswp.pantheonsite.io/", @@ -11413,10 +11415,10 @@ Array [ "date": "2017-09-02T10:36:43.000Z", "description": "

\\"\\"

", - "guid": "http://dev-gatbsyjswp.pantheonsite.io/wp-content/uploads/2017/09/pexels-photo-534327.jpeg", + "guid___NODE": "61ef7ef2-032f-5c90-bba3-99d04c9a1f07", "id": "61ef7ef2-032f-5c90-bba3-99d04c9a1f07", "internal": Object { - "contentDigest": "345cd9205939fd066a98ad92bce14d60", + "contentDigest": "e34c3e03101d1305788a27f283e90c6b", "type": "wordpress__wp_media", }, "link": "http://dev-gatbsyjswp.pantheonsite.io/sample-post-1/pexels-photo-534327/", @@ -11532,10 +11534,10 @@ Array [ "date": "2017-09-02T10:35:16.000Z", "description": "

\\"\\"

", - "guid": "http://dev-gatbsyjswp.pantheonsite.io/wp-content/uploads/2017/09/pexels-photo-534351.jpeg", + "guid___NODE": "950513a1-a720-5588-b262-9533aa598624", "id": "950513a1-a720-5588-b262-9533aa598624", "internal": Object { - "contentDigest": "60e31d4f0302cc6cae5c7021444ff564", + "contentDigest": "7bb20caef5bf65715b92b109cbfae7b9", "type": "wordpress__wp_media", }, "link": "http://dev-gatbsyjswp.pantheonsite.io/gatsby-sample-home-page/pexels-photo-534351/", diff --git a/packages/gatsby-source-wordpress/src/normalize.js b/packages/gatsby-source-wordpress/src/normalize.js index 177806db7162b..667ee98dfafd2 100644 --- a/packages/gatsby-source-wordpress/src/normalize.js +++ b/packages/gatsby-source-wordpress/src/normalize.js @@ -239,20 +239,10 @@ exports.mapEntitiesToMedia = entities => { return entities.map(e => { // Map featured_media to its media node - let featuredMedia - if (e.featured_media) { - featuredMedia = media.find(m => m.wordpress_id === e.featured_media) - } - - if (featuredMedia) { - e.featured_media___NODE = featuredMedia.id - } - // Always delete even if we can't find a featuredMedia as WordPress' API sets - // featured_media to 0 when there isn't one which is useless to us. - delete e.featured_media - - const isPhoto = field => + // Check if it's value of ACF Image field, that has 'Return value' set to + // 'Image Object' ( https://www.advancedcustomfields.com/resources/image/ ) + const isPhotoObject = field => _.isObject(field) && field.wordpress_id && field.url && @@ -262,61 +252,93 @@ exports.mapEntitiesToMedia = entities => { : false const photoRegex = /\.(gif|jpg|jpeg|tiff|png)$/i - const isPhotoUrl = filename => photoRegex.test(filename) - const replacePhoto = field => - media.find(m => m.wordpress_id === field.wordpress_id).id - - const replaceFieldsInObject = object => { - _.each(object, (value, key) => { - if (_.isArray(value)) { - value.forEach(v => replaceFieldsInObject(v)) - } - if (isPhoto(value)) { - object[`${key}___NODE`] = replacePhoto(value) - delete object[key] + const isPhotoUrl = filename => + _.isString(filename) && photoRegex.test(filename) + const isPhotoUrlAlreadyProcessed = key => key == `source_url` + const isFeaturedMedia = (value, key) => + (_.isNumber(value) || _.isBoolean(value)) && key === `featured_media` + // ACF Gallery and similarly shaped arrays + const isArrayOfPhotoObject = field => + _.isArray(field) && field.length > 0 && isPhotoObject(field[0]) + const getMediaItemID = mediaItem => (mediaItem ? mediaItem.id : null) + + // Try to get media node from value: + // - special case - check if key is featured_media and value is photo ID + // - check if value is photo url + // - check if value is ACF Image Object + // - check if value is ACF Gallery + const getMediaFromValue = (value, key) => { + if (isFeaturedMedia(value, key)) { + return { + mediaNodeID: _.isNumber(value) + ? getMediaItemID(media.find(m => m.wordpress_id === value)) + : null, + deleteField: true, } - - // featured_media can be nested inside ACF fields - if (_.isObject(value) && value.featured_media) { - featuredMedia = media.find( - m => m.wordpress_id === value.featured_media - ) - if (featuredMedia) { - value.featured_media___NODE = featuredMedia.id - } - delete value.featured_media + } else if (isPhotoUrl(value) && !isPhotoUrlAlreadyProcessed(key)) { + const mediaNodeID = getMediaItemID( + media.find(m => m.source_url === value) + ) + return { + mediaNodeID, + deleteField: !!mediaNodeID, } - if (_.isNumber(value) && key == `featured_media`) { - featuredMedia = media.find(m => m.wordpress_id === value) - if (featuredMedia) { - object[`${key}___NODE`] = featuredMedia.id - } - delete object[key] + } else if (isPhotoObject(value)) { + const mediaNodeID = getMediaItemID( + media.find(m => m.source_url === value.url) + ) + return { + mediaNodeID, + deleteField: !!mediaNodeID, } - if (_.isBoolean(value) && key == `featured_media`) { - delete object[key] + } else if (isArrayOfPhotoObject(value)) { + return { + mediaNodeID: value + .map(item => getMediaFromValue(item, key).mediaNodeID) + .filter(id => id !== null), + deleteField: true, } - }) + } + return { + mediaNodeID: null, + deleteField: false, + } } - if (e.acf) { - _.each(e.acf, (value, key) => { - if (_.isString(value) && isPhotoUrl(value)) { - const me = media.find(m => m.source_url === value) - if (me) { - e.acf[`${key}___NODE`] = me.id - delete e.acf[key] - } + const replaceFieldsInObject = object => { + let deletedAllFields = true + _.each(object, (value, key) => { + const { mediaNodeID, deleteField } = getMediaFromValue(value, key) + if (mediaNodeID) { + object[`${key}___NODE`] = mediaNodeID + } + if (deleteField) { + delete object[key] + // We found photo node (even if it has no image), + // We can end processing this path + return + } else { + deletedAllFields = false } - if (_.isArray(value) && value[0] && value[0].acf_fc_layout) { - e.acf[key] = e.acf[key].map(f => { - replaceFieldsInObject(f) - return f - }) + if (_.isArray(value)) { + value.forEach(v => replaceFieldsInObject(v)) + } else if (_.isObject(value)) { + replaceFieldsInObject(value) } }) + + // Deleting fields and replacing them with links to different nodes + // can cause build errors if object will have only linked properites: + // https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/src/schema/infer-graphql-input-fields.js#L205 + // Hacky workaround: + // Adding dummy field with concrete value (not link) fixes build + if (deletedAllFields && object && _.isObject(object)) { + object[`dummy`] = true + } } + replaceFieldsInObject(e) + return e }) }