From 475e699ff0770109f98b1a4dc9b7cab8c2759402 Mon Sep 17 00:00:00 2001 From: Rob Gietema Date: Mon, 2 Dec 2024 06:37:21 -0300 Subject: [PATCH] Add image data in block data. --- CHANGELOG.md | 1 + scripts/convert.js | 36 +++++++++++ src/helpers/content/content.js | 59 ++++++++++++++++++- src/helpers/index.js | 1 + src/models/document/document.js | 58 +++++++++++++++++- .../core/behaviors/preview_image_link.json | 1 + src/profiles/core/catalog.json | 5 ++ src/routes/content/content.js | 23 ++++---- src/seeds/catalog/catalog.js | 3 + 9 files changed, 172 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32006a6..e37b710 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ - Add support for images in controlpanels @robgietema - Add preview link support @robgietema - Add block types index @robgietema +- Expand image data in block data @robgietema ### Bugfix diff --git a/scripts/convert.js b/scripts/convert.js index b4179c1..8f6c97e 100644 --- a/scripts/convert.js +++ b/scripts/convert.js @@ -92,6 +92,23 @@ const convertDocuments = (input, path) => { mkdirSync(`${path}/documents`); mkdirSync(`${path}/documents/images`); + let relations = []; + if (existsSync(`${input}/relations.json`)) { + relations = readfile(`${input}/relations.json`); + } + const references = {}; + map(relations, (relation) => { + if (typeof references[relation.from_uuid] !== 'object') { + references[relation.from_uuid] = []; + } + if (relation.from_attribute !== 'isReferencing') { + references[relation.from_uuid].push({ + attr: relation.from_attribute, + to: relation.to_uuid, + }); + } + }); + map(glob(`${input}/content/**/*.json`), (filename) => { if (endsWith(filename, '__metadata__.json')) { return; @@ -138,6 +155,25 @@ const convertDocuments = (input, path) => { document.image = `/images/${document.uuid}/${file}`; } + // Add references + if (references[document.uuid]) { + map(references[document.uuid], (reference) => { + const targetfile = `${input}/content/${reference.to}/data.json`; + if (existsSync(targetfile)) { + const target = JSON.parse( + readFileSync(`${input}/content/${reference.to}/data.json`, 'utf8'), + ); + document[reference.attr] = [ + { + '@id': target['@id'].replace('.', '-'), + UID: target.UID, + title: target.title, + }, + ]; + } + }); + } + // Convert to string let json = JSON.stringify(document); json = json.replaceAll(/\"[^"]*resolveuid\/([^"]*)\"/g, (result, uuid) => { diff --git a/src/helpers/content/content.js b/src/helpers/content/content.js index 1f5c3a4..b321174 100644 --- a/src/helpers/content/content.js +++ b/src/helpers/content/content.js @@ -3,7 +3,7 @@ * @module helpers/content/content */ -import { isString, last, map } from 'lodash'; +import { isObject, isString, keys, last, map } from 'lodash'; import mime from 'mime-types'; import { @@ -145,11 +145,66 @@ export async function handleRelationLists(json, type) { * Handle block references * @method handleBlockReferences * @param {Object} json Current json object. + * @param {Object} trx Transaction object. * @returns {Object} Json with references expanded. */ -export async function handleBlockReferences(json) { +export async function handleBlockReferences(json, trx) { // Make a copy of the json data const output = { ...json }; + const { Catalog } = require('../../models/catalog/catalog'); + + const extendHref = async (href, trx) => { + const target = await Catalog.fetchOne({ _path: href['@id'] }, {}, trx); + if (target) { + return { + ...href, + image_field: target.image_field, + image_scales: target.image_scales, + }; + } + return href; + }; + + if (isObject(output.blocks)) { + await Promise.all( + map(keys(output.blocks), async (block) => { + if (isObject(output.blocks[block].href)) { + output.blocks[block].href[0] = await extendHref( + output.blocks[block].href[0], + trx, + ); + } + if (isObject(output.blocks[block].slides)) { + await Promise.all( + map(output.blocks[block].slides, async (slide, index) => { + console.log('image'); + console.log(index); + if (isObject(output.blocks[block].slides[index].image)) { + console.log('image'); + output.blocks[block].slides[index].image[0] = await extendHref( + output.blocks[block].slides[index].image[0], + trx, + ); + } + }), + ); + } + if (isObject(output.blocks[block].blocks)) { + await Promise.all( + map(keys(output.blocks[block].blocks), async (subblock) => { + if (isObject(output.blocks[block].blocks[subblock].href)) { + output.blocks[block].blocks[subblock].href[0] = + await extendHref( + output.blocks[block].blocks[subblock].href[0], + trx, + ); + } + }), + ); + } + }), + ); + } // Return new json data return output; diff --git a/src/helpers/index.js b/src/helpers/index.js index 0c45774..2de074b 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.js @@ -6,6 +6,7 @@ export { getUserId, hasPermission } from './auth/auth'; export { + handleBlockReferences, handleFiles, handleImages, handleRelationLists, diff --git a/src/models/document/document.js b/src/models/document/document.js index 407e7d8..5a1b5df 100644 --- a/src/models/document/document.js +++ b/src/models/document/document.js @@ -15,6 +15,7 @@ import _, { isArray, isEmpty, isFunction, + isObject, isUndefined, keys, last, @@ -765,7 +766,10 @@ export class Document extends Model { * @return {Boolean} True if has preview image */ hasPreviewImage() { - return this.preview_image || this.preview_image_link ? true : false; + return isObject(this.json.preview_image) || + isObject(this.json.preview_image_link) + ? true + : false; } /** @@ -809,6 +813,58 @@ export class Document extends Model { } } + /** + * Get image scales + * @method getImageScales + * @param {Object} trx Transaction object. + * @return {Object} Image scales object. + */ + async getImageScales(trx) { + const image_scales = {}; + const relationChoiceFields = this._type.getFactoryFields('Relation Choice'); + const imageFields = this._type.getFactoryFields('Image'); + + const addDownload = (field) => { + return { + ...field, + download: `@@images/${field.uuid}.${last(field.filename.split('.'))}`, + scales: mapValues(field.scales, (scale) => ({ + width: scale.width, + height: scale.height, + download: `@@images/${scale.uuid}.${last(field.filename.split('.'))}`, + })), + }; + }; + + // Add image fields + map(imageFields, (field) => { + image_scales[field] = [addDownload(this.json[field])]; + }); + + // Add relation choice fields + await Promise.all( + map(relationChoiceFields, async (field) => { + if (this.json[field] && this.json[field].length > 0) { + const target = await Document.fetchById( + this.json[field][0].UID, + {}, + trx, + ); + if (isObject(target.json.image)) { + image_scales[field] = [ + { + ...addDownload(target.json.image), + base_path: target.path, + }, + ]; + } + } + }), + ); + + return image_scales; + } + /** * Re index children * @method reindexChildren diff --git a/src/profiles/core/behaviors/preview_image_link.json b/src/profiles/core/behaviors/preview_image_link.json index 94c7986..deef742 100644 --- a/src/profiles/core/behaviors/preview_image_link.json +++ b/src/profiles/core/behaviors/preview_image_link.json @@ -15,6 +15,7 @@ "title:i18n": "Preview image", "description:i18n": "Select an image that will be used in listing and teaser blocks.", "widget": "object_browser", + "factory": "Relation Choice", "mode": "image" }, "preview_caption_link": { diff --git a/src/profiles/core/catalog.json b/src/profiles/core/catalog.json index 13079f9..6ba5250 100644 --- a/src/profiles/core/catalog.json +++ b/src/profiles/core/catalog.json @@ -913,6 +913,11 @@ "type": "string", "attr": "getImageField" }, + { + "name": "image_scales", + "type": "json", + "attr": "getImageScales" + }, { "name": "hasPreviewImage", "type": "boolean", diff --git a/src/routes/content/content.js b/src/routes/content/content.js index 1cb0532..61406e2 100644 --- a/src/routes/content/content.js +++ b/src/routes/content/content.js @@ -288,8 +288,8 @@ export default [ const buffer = readFile(field.uuid); return { headers: { - 'Content-Type': field['content-type'], - 'Content-Disposition': `attachment; filename="${field.filename}"`, + 'content-type': field['content-type'], + 'content-disposition': `attachment; filename="${field.filename}"`, 'Accept-Ranges': 'bytes', }, binary: buffer, @@ -304,7 +304,7 @@ export default [ const buffer = readFile(req.params.uuid); return { headers: { - 'Content-Type': `image/${req.params.ext}`, + 'content-type': `image/${req.params.ext}`, }, binary: buffer, }; @@ -319,8 +319,8 @@ export default [ const buffer = readFile(field.uuid); return { headers: { - 'Content-Type': field['content-type'], - 'Content-Disposition': `attachment; filename="${field.filename}"`, + 'content-type': field['content-type'], + 'content-disposition': `attachment; filename="${field.filename}"`, }, binary: buffer, }; @@ -335,8 +335,8 @@ export default [ const buffer = readFile(field.scales[req.params.scale].uuid); return { headers: { - 'Content-Type': field['content-type'], - 'Content-Disposition': `attachment; filename="${field.filename}"`, + 'content-type': field['content-type'], + 'content-disposition': `attachment; filename="${field.filename}"`, }, binary: buffer, }; @@ -350,8 +350,8 @@ export default [ const json = await req.document.toJSON(req); return { headers: { - 'Content-Type': 'application/json', - 'Content-Disposition': `attachment; filename="${ + 'content-cype': 'application/json', + 'content-disposition': `attachment; filename="${ req.document.path === '/' ? '_root.json' : `${join(drop(split(req.document.path, '/')), '.')}.json` @@ -381,8 +381,9 @@ export default [ handler: async (req, trx) => { await req.document.fetchRelated('[_children(order)._type, _type]', trx); await req.document.fetchRelationLists(trx); + const json = await req.document.toJSON(req, await getComponents(req)); return { - json: await req.document.toJSON(req, await getComponents(req)), + json: await handleBlockReferences(json, trx), }; }, }, @@ -447,7 +448,6 @@ export default [ json = await handleFiles(json, type); json = await handleImages(json, type); json = await handleRelationLists(json, req.type); - json = await handleBlockReferences(json); // Create new document let document = Document.fromJson({ @@ -604,7 +604,6 @@ export default [ json = await handleFiles(json, req.type); json = await handleImages(json, req.type); json = await handleRelationLists(json, req.type); - json = await handleBlockReferences(json); // Create new version const modified = moment.utc().format(); diff --git a/src/seeds/catalog/catalog.js b/src/seeds/catalog/catalog.js index 0963d0c..5451786 100644 --- a/src/seeds/catalog/catalog.js +++ b/src/seeds/catalog/catalog.js @@ -77,6 +77,9 @@ export const seedCatalog = async (trx, profilePath) => { case 'boolean': table.boolean(metadata.name); break; + case 'json': + table.json(metadata.name); + break; case 'string[]': table.specificType(metadata.name, 'character varying(255)[]'); break;