diff --git a/api/package-lock.json b/api/package-lock.json index b6d5d19c3..d7299d6f8 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -12,6 +12,7 @@ "@contentstack/cli-cm-import": "^1.16.6", "@contentstack/cli-utilities": "^1.7.1", "@contentstack/json-rte-serializer": "^2.0.7", + "@contentstack/marketplace-sdk": "^1.2.4", "axios": "^1.7.8", "chokidar": "^3.6.0", "cors": "^2.8.5", diff --git a/api/package.json b/api/package.json index 915bc010b..adc83e7af 100644 --- a/api/package.json +++ b/api/package.json @@ -28,6 +28,7 @@ "@contentstack/cli-cm-import": "^1.16.6", "@contentstack/cli-utilities": "^1.7.1", "@contentstack/json-rte-serializer": "^2.0.7", + "@contentstack/marketplace-sdk": "^1.2.4", "axios": "^1.7.8", "chokidar": "^3.6.0", "cors": "^2.8.5", diff --git a/api/src/constants/app/index.json b/api/src/constants/app/index.json new file mode 100644 index 000000000..83c6583c7 --- /dev/null +++ b/api/src/constants/app/index.json @@ -0,0 +1,3435 @@ +{ + "entries": [ + { + "uid": "blt306893215247514d", + "_version": 6, + "locale": "en-us", + "ACL": {}, + "_in_progress": false, + "app_type": [ + { + "uid": "blte9d290b2e9e0931e", + "_content_type_uid": "app_types" + } + ], + "app_uid": "6307403f08132800192fe615", + "built_by": [ + { + "uid": "bltec929d731b1f9d85", + "_content_type_uid": "organizations" + } + ], + "category": [ + { + "uid": "blt6abfa301df06bf93", + "_content_type_uid": "categories" + } + ], + "coming_soon": false, + "created_at": "2022-09-14T13:26:39.897Z", + "created_by": "bltf389fd468f32cd6e", + "description": "

With the Contentstack Marketplace Aprimo integration, into your headless CMS, you can easily manage and share digital assets across multiple platforms.

Using the Custom Field and JSON RTE plugin, you can integrate Aprimo with Contentstack. You can create an entry in Contentstack and, with the integration, view all the digital assets in Contentstack.

", + "dynamic_links": [], + "feature_types": [ + { + "feature_type": [ + { + "uid": "blt6b35fa46f253a6bd", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "cs3997a434a3407aca" + }, + "description": "Custom Field allows you to select multiple images to add them to your entry from your Aprimo account." + }, + { + "feature_type": [ + { + "uid": "bltf9b70a5fd3c08c36", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "csb4b648266a7e609c" + }, + "description": "You can add images from the Aprimo app to your JSON Rich Text Editor field using the Aprimo JSON RTE Plugin." + } + ], + "icon": { + "_version": 1, + "is_dir": false, + "uid": "bltf38c093728e6118d", + "ACL": {}, + "content_type": "image/svg+xml", + "created_at": "2022-09-14T13:28:32.147Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "7340", + "filename": "Aprimo.svg", + "parent_uid": "blt2eb26851df9c0e2e", + "tags": [], + "title": "Aprimo.svg", + "updated_at": "2022-09-14T13:28:32.147Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:01.962Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltf38c093728e6118d/6321d700acc2b02a2d2beb76/Aprimo.svg" + }, + "installation_url": "/#!/apps/6307403f08132800192fe615/install", + "links": { + "source_code": "", + "documentation": "", + "end_user_license_agreement": "", + "others": [] + }, + "screenshots": [ + { + "_version": 1, + "is_dir": false, + "uid": "bltab7667c5bfa68ab5", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-14T13:27:02.543Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "47090", + "filename": "aprimo1.png", + "parent_uid": "bltd9b123ad99b0ca80", + "tags": [], + "title": "aprimo1.png", + "updated_at": "2022-09-14T13:27:02.543Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:01.962Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltab7667c5bfa68ab5/6321d6a6aab1fc6da8413bbe/aprimo1.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt3a5bc2faed088ba0", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-14T13:27:03.228Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "44450", + "filename": "aprimo2.png", + "parent_uid": "bltd9b123ad99b0ca80", + "tags": [], + "title": "aprimo2.png", + "updated_at": "2022-09-14T13:27:03.228Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:01.962Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt3a5bc2faed088ba0/6321d6a77b677842d8282859/aprimo2.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "bltb6404261010a70ac", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-14T13:27:09.528Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "293528", + "filename": "aprimo3.png", + "parent_uid": "bltd9b123ad99b0ca80", + "tags": [], + "title": "aprimo3.png", + "updated_at": "2022-09-14T13:27:09.528Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:01.962Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltb6404261010a70ac/6321d6ad3a97082c2920f61b/aprimo3.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt54e7fb96e980dabd", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-14T13:27:09.312Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "224997", + "filename": "aprimo4.png", + "parent_uid": "bltd9b123ad99b0ca80", + "tags": [], + "title": "aprimo4.png", + "updated_at": "2022-09-14T13:27:09.312Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:01.962Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt54e7fb96e980dabd/6321d6adaa855a2a336b9bb0/aprimo4.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt6d4821e32a03a9ce", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-14T13:27:08.114Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "74233", + "filename": "aprimo5.png", + "parent_uid": "bltd9b123ad99b0ca80", + "tags": [], + "title": "aprimo5.png", + "updated_at": "2022-09-14T13:27:08.114Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:01.962Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt6d4821e32a03a9ce/6321d6ac2769d078008690e0/aprimo5.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt720989f14aff7ec3", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-14T13:27:08.848Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "137310", + "filename": "aprimo6.png", + "parent_uid": "bltd9b123ad99b0ca80", + "tags": [], + "title": "aprimo6.png", + "updated_at": "2022-09-14T13:27:08.848Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:01.962Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt720989f14aff7ec3/6321d6ac5d2a8f4646a65840/aprimo6.png" + } + ], + "seo": { + "canonical_url": "", + "description": "", + "image": null, + "robots": "", + "title": "" + }, + "static_links": [ + { + "type": "End-User License Agreement", + "url": "https://www.contentstack.com/legal/eula-for-contentstack-proprietary-marketplace-apps/", + "_metadata": { + "uid": "cse67b21f4286086a8" + } + }, + { + "type": "Documentation", + "url": "https://www.contentstack.com/docs/developers/marketplace-apps/aprimo/", + "_metadata": { + "uid": "cs74da8d29f725d7bf" + } + } + ], + "summary": "Fetch digital assets (images) from your Aprimo account into Contentstack entries.", + "tags": [], + "title": "Aprimo", + "updated_at": "2025-01-24T11:35:05.653Z", + "updated_by": "blt05ac693470ad5ff7", + "url": "/marketplace/aprimo", + "use_cases": "

Manage Aprimo digital media with ease: The Aprimo app lets you manage images within your entries. With Aprimo, you can easily link all the images without leaving your headless CMS.

Better marketing campaigns with ease-to-access assets: You can create content in Contentstack and use the Aprimo Custom Field or JSON RTE field to insert images in your content. With this, cross-functional teams can work on large-scale campaigns with ease.

Easy distribution of digital assets: The Aprimo app allows geographically dispersed teams to easily store, share and fetch digital media assets.

", + "publish_details": { + "time": "2025-01-28T10:53:49.590Z", + "user": "blt05ac693470ad5ff7", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + } + }, + { + "uid": "blte0c272ac85362b1e", + "_version": 7, + "locale": "en-us", + "ACL": {}, + "_in_progress": false, + "app_type": [ + { + "uid": "blte9d290b2e9e0931e", + "_content_type_uid": "app_types" + } + ], + "app_uid": "63e1fd2363081e001992f20f", + "built_by": [ + { + "uid": "bltec929d731b1f9d85", + "_content_type_uid": "organizations" + } + ], + "category": [ + { + "uid": "blt6abfa301df06bf93", + "_content_type_uid": "categories" + } + ], + "coming_soon": false, + "created_at": "2023-02-07T09:50:55.600Z", + "created_by": "bltf389fd468f32cd6e", + "description": "

With the Contentstack Marketplace Brandfolder app integration into your headless CMS, you can easily manage and share digital assets (images) across multiple platforms.
Using the Custom Field and JSON Rich Text Editor plugin, you can integrate Brandfolder with Contentstack. You can create an entry in Contentstack and view all the digital assets within your Contentstack website.

", + "dynamic_links": [], + "feature_types": [ + { + "feature_type": [ + { + "uid": "blt6b35fa46f253a6bd", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "csabc9083d38203db6" + }, + "description": "Custom Field allows you to select multiple images to add to your entry from your Brandfolder account." + }, + { + "feature_type": [ + { + "uid": "bltf9b70a5fd3c08c36", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "cs2b539819aee4ff04" + }, + "description": "You can add images from the Brandfolder app to your JSON Rich Text Editor field using the Brandfolder JSON RTE Plugin." + } + ], + "icon": { + "_version": 1, + "is_dir": false, + "uid": "blta70fdaf98d6bb82a", + "ACL": {}, + "content_type": "image/svg+xml", + "created_at": "2023-02-07T10:08:04.960Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "2242", + "filename": "Brandfolder.svg", + "parent_uid": "blt2eb26851df9c0e2e", + "tags": [], + "title": "Brandfolder.svg", + "updated_at": "2023-02-07T10:08:04.960Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blta70fdaf98d6bb82a/63e22304933db53d66f72d37/Brandfolder.svg" + }, + "installation_url": "/#!/apps/63e1fd2363081e001992f20f/install", + "links": { + "source_code": "", + "documentation": "", + "end_user_license_agreement": "", + "others": [] + }, + "screenshots": [ + { + "_version": 1, + "is_dir": false, + "uid": "bltc0ed1ab61388a84e", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-06-22T07:49:19.401Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "100032", + "filename": "App_Config.png", + "parent_uid": "blt8f6e144007ba8c5c", + "tags": [], + "title": "App_Config.png", + "updated_at": "2023-06-22T07:49:19.401Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltc0ed1ab61388a84e/6493fcffcbbe0752f5d15fd6/App_Config.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt932072d875540e5a", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-02-07T10:06:58.097Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "78360", + "filename": "brandfolder_customfield_select.png", + "parent_uid": "blt8f6e144007ba8c5c", + "tags": [], + "title": "brandfolder_customfield_select.png", + "updated_at": "2023-02-07T10:06:58.097Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt932072d875540e5a/63e222c2ea95785b83d3f5d0/brandfolder_customfield_select.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt73f9737af627514f", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-02-07T10:06:58.115Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "83799", + "filename": "brandfolder_contenttype.png", + "parent_uid": "blt8f6e144007ba8c5c", + "tags": [], + "title": "brandfolder_contenttype.png", + "updated_at": "2023-02-07T10:06:58.115Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt73f9737af627514f/63e222c2f03f747756684c77/brandfolder_contenttype.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt516dbfe1818c2389", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-02-07T10:06:59.171Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "92332", + "filename": "brandfolder_jsonrte_contenttype.png", + "parent_uid": "blt8f6e144007ba8c5c", + "tags": [], + "title": "brandfolder_jsonrte_contenttype.png", + "updated_at": "2023-02-07T10:06:59.171Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt516dbfe1818c2389/63e222c34c4b0210bc379ec6/brandfolder_jsonrte_contenttype.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt90ab63cc6a44d249", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-02-07T10:06:57.943Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "18057", + "filename": "customfield_without_data.png", + "parent_uid": "blt8f6e144007ba8c5c", + "tags": [], + "title": "customfield_without_data.png", + "updated_at": "2023-02-07T10:06:57.943Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt90ab63cc6a44d249/63e222c197fe254050234eb3/customfield_without_data.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt3485004145939873", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-02-07T10:06:58.822Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "59051", + "filename": "selector_collection.png", + "parent_uid": "blt8f6e144007ba8c5c", + "tags": [], + "title": "selector_collection.png", + "updated_at": "2023-02-07T10:06:58.822Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt3485004145939873/63e222c2fc79c04b19c2f344/selector_collection.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blta5a897dafc73a996", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-02-07T10:06:59.724Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "389773", + "filename": "selector_data.png", + "parent_uid": "blt8f6e144007ba8c5c", + "tags": [], + "title": "selector_data.png", + "updated_at": "2023-02-07T10:06:59.724Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blta5a897dafc73a996/63e222c3be94ee3bf4afd8aa/selector_data.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt58b1a3a249de1f1f", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-02-07T10:06:59.582Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "282122", + "filename": "selector_place_image.png", + "parent_uid": "blt8f6e144007ba8c5c", + "tags": [], + "title": "selector_place_image.png", + "updated_at": "2023-02-07T10:06:59.582Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt58b1a3a249de1f1f/63e222c3e414c23655e13ee9/selector_place_image.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt05120b040d890a1c", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-02-07T10:06:58.751Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "61474", + "filename": "customfield_data.png", + "parent_uid": "blt8f6e144007ba8c5c", + "tags": [], + "title": "customfield_data.png", + "updated_at": "2023-02-07T10:06:58.751Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt05120b040d890a1c/63e222c276abcd351bacd47b/customfield_data.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt44176a644efbe9b4", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-02-07T10:06:58.464Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "170476", + "filename": "jsonrte_data.png", + "parent_uid": "blt8f6e144007ba8c5c", + "tags": [], + "title": "jsonrte_data.png", + "updated_at": "2023-02-07T10:06:58.464Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt44176a644efbe9b4/63e222c23849a0141b70237a/jsonrte_data.png" + } + ], + "seo": { + "canonical_url": "", + "description": "", + "image": null, + "robots": "", + "title": "" + }, + "static_links": [ + { + "type": "Documentation", + "url": "https://www.contentstack.com/docs/developers/marketplace-apps/brandfolder/", + "_metadata": { + "uid": "csd2223db13369101b" + } + }, + { + "type": "End-User License Agreement", + "url": "https://www.contentstack.com/legal/eula-for-contentstack-proprietary-marketplace-apps", + "_metadata": { + "uid": "cs8c54cc546665821f" + } + } + ], + "summary": "Fetch digital assets (images) from your Brandfolder account into Contentstack entries.", + "tags": [], + "title": "Brandfolder", + "updated_at": "2025-01-24T11:19:39.982Z", + "updated_by": "blt05ac693470ad5ff7", + "url": "/marketplace/brandfolder", + "use_cases": "

Manage Brandfolder digital media readily: The Brandfolder app lets you fetch and manage images within your entries. With Brandfolder, you can easily link all the images without leaving your headless CMS.

Better marketing campaigns with ease-to-access assets: You can create content in Contentstack and use the Custom Field or JSON RTE field to insert images in your content. With this, cross-functional teams can work on large-scale campaigns with ease.

Easy distribution of digital assets: The Brandfolder app allows geographically dispersed teams to store, share, and fetch digital media assets.

", + "publish_details": { + "time": "2025-01-28T10:55:14.141Z", + "user": "blt05ac693470ad5ff7", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + } + }, + { + "uid": "blt2e5ed9f109bb156f", + "_version": 12, + "locale": "en-us", + "ACL": {}, + "_in_progress": false, + "app_type": [ + { + "uid": "blte9d290b2e9e0931e", + "_content_type_uid": "app_types" + } + ], + "app_uid": "61ddc1b51ecff10018907c4d", + "built_by": [ + { + "uid": "bltec929d731b1f9d85", + "_content_type_uid": "organizations" + } + ], + "category": [ + { + "uid": "blt6abfa301df06bf93", + "_content_type_uid": "categories" + } + ], + "coming_soon": false, + "created_at": "2022-03-29T11:13:06.028Z", + "created_by": "blt89b5b90cb9e7c645", + "description": "

Contentstack's Marketplace apps offer a variety of ways to integrate third-party applications, such as Bynder, directly into your headless CMS. With this, you can streamline the process of managing and sharing digital assets across multiple platforms.

Using a Contentstack Custom Field, you can integrate Bynder with Contentstack directly. You can create an entry in Contentstack, the app will ensure that you are able to view the digital assets within your CMS.

", + "dynamic_links": [], + "feature_types": [ + { + "feature_type": [ + { + "uid": "blt6b35fa46f253a6bd", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "cs23cd1a05a970bc44" + }, + "description": "While creating entries, you can select one or more images, videos, or documents as the input value for the field from your Bynder account." + }, + { + "feature_type": [ + { + "uid": "bltf9b70a5fd3c08c36", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "csdae58a2b06a451d4" + }, + "description": "You can add assets from the Bynder app to your JSON Rich Text Editor field using the Bynder JSON RTE Plugin." + } + ], + "icon": { + "_version": 1, + "is_dir": false, + "uid": "blteb81b84adb512d31", + "ACL": {}, + "content_type": "image/svg+xml", + "created_at": "2022-03-29T11:09:59.102Z", + "created_by": "blt89b5b90cb9e7c645", + "file_size": "2589", + "filename": "Bynder.svg", + "parent_uid": "blt2eb26851df9c0e2e", + "tags": [], + "title": "Bynder.svg", + "updated_at": "2022-03-29T11:09:59.102Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blteb81b84adb512d31/6242e9070466d7630dd3a706/Bynder.svg" + }, + "installation_url": "#!/apps/61ddc1b51ecff10018907c4d/install", + "links": { + "source_code": "", + "documentation": "", + "others": [], + "end_user_license_agreement": "https://www.contentstack.com/legal/eula-for-contentstack-proprietary-marketplace-apps/" + }, + "screenshots": [ + { + "_version": 2, + "is_dir": false, + "uid": "blt9f6cc27c83239660", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:09:48.091Z", + "created_by": "blt89b5b90cb9e7c645", + "description": "", + "file_size": "43135", + "filename": "bynder-config-screen.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "Bynder_ConfigScreen.png", + "updated_at": "2022-07-21T14:47:18.151Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt9f6cc27c83239660/62d966f6a2057a5e86269a95/bynder-config-screen.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt4c0f3e1846fac19e", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:09:47.477Z", + "created_by": "blt89b5b90cb9e7c645", + "description": "", + "file_size": "42470", + "filename": "Bynder_ContentType_AppSelection.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "Bynder_ContentType_AppSelection.png", + "updated_at": "2022-03-29T11:09:47.477Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt4c0f3e1846fac19e/6242e8fb311ad5112f12b57c/Bynder_ContentType_AppSelection.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt511316bff90cd2a3", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:09:45.503Z", + "created_by": "blt89b5b90cb9e7c645", + "description": "", + "file_size": "54617", + "filename": "Bynder_ContentTypeExtensionSelection.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "Bynder_ContentTypeExtensionSelection.png", + "updated_at": "2022-03-29T11:09:45.503Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt511316bff90cd2a3/6242e8f99b68390fcd0530a6/Bynder_ContentTypeExtensionSelection.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt24acb3261bd466fa", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:09:47.491Z", + "created_by": "blt89b5b90cb9e7c645", + "description": "", + "file_size": "22852", + "filename": "Bynder_CustomField_NoAsset.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "Bynder_CustomField_NoAsset.png", + "updated_at": "2022-03-29T11:09:47.491Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt24acb3261bd466fa/6242e8fbff4cca132d166a2c/Bynder_CustomField_NoAsset.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "bltd2691e7f305d8a94", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:09:55.518Z", + "created_by": "blt89b5b90cb9e7c645", + "file_size": "33864", + "filename": "Bynder_SelectorPage_Connect.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "Bynder_SelectorPage_Connect.png", + "updated_at": "2022-03-29T11:09:55.518Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltd2691e7f305d8a94/6242e9039b68390fcd0530ae/Bynder_SelectorPage_Connect.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "bltc58200f52a299c8a", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:09:53.880Z", + "created_by": "blt89b5b90cb9e7c645", + "file_size": "202476", + "filename": "Bynder_SelectorPage_Login.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "Bynder_SelectorPage_Login.png", + "updated_at": "2022-03-29T11:09:53.880Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltc58200f52a299c8a/6242e901c470e610f0a9fea5/Bynder_SelectorPage_Login.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "bltf6a97201c2cfb505", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:09:51.876Z", + "created_by": "blt89b5b90cb9e7c645", + "file_size": "358816", + "filename": "Bynder_SelectorPage.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "Bynder_SelectorPage.png", + "updated_at": "2022-03-29T11:09:51.876Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltf6a97201c2cfb505/6242e8ffa9ee655f1991e1a1/Bynder_SelectorPage.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "bltd390b7049019a676", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:09:53.003Z", + "created_by": "blt89b5b90cb9e7c645", + "file_size": "333643", + "filename": "Bynder_SelectorPage_AddOption.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "Bynder_SelectorPage_AddOption.png", + "updated_at": "2022-03-29T11:09:53.003Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltd390b7049019a676/6242e9015564b679c0c521c6/Bynder_SelectorPage_AddOption.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt334ba0bfefd731cd", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:09:45.456Z", + "created_by": "blt89b5b90cb9e7c645", + "description": "", + "file_size": "128375", + "filename": "Bynder_CustomField_Assets.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "Bynder_CustomField_Assets.png", + "updated_at": "2022-03-29T11:09:45.456Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt334ba0bfefd731cd/6242e8f9906f01655b799992/Bynder_CustomField_Assets.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt9ec31cb593cc984f", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:09:46.572Z", + "created_by": "blt89b5b90cb9e7c645", + "description": "", + "file_size": "211471", + "filename": "Bynder_CustomField_ActiveHover.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "Bynder_CustomField_ActiveHover.png", + "updated_at": "2022-03-29T11:09:46.572Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt9ec31cb593cc984f/6242e8fadac6690b70a2dc07/Bynder_CustomField_ActiveHover.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt22269eac414805e1", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-07-21T14:22:48.908Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "49642", + "filename": "bynder-rte-field.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "bynder-rte-field.png", + "updated_at": "2022-07-21T14:22:48.908Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt22269eac414805e1/62d9613848cc366ab5407e1c/bynder-rte-field.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt85f67a817a0741b7", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-07-21T14:22:49.521Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "21861", + "filename": "bynder-select-plugin.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "bynder-select-plugin.png", + "updated_at": "2022-07-21T14:22:49.521Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt85f67a817a0741b7/62d96139e49d4a6983cc1ec2/bynder-select-plugin.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "bltce795eb8689ec488", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-07-21T14:22:49.605Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "51773", + "filename": "bynder-selected-plugin.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "bynder-selected-plugin.png", + "updated_at": "2022-07-21T14:22:49.605Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltce795eb8689ec488/62d96139ecc54c6cacd810c5/bynder-selected-plugin.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt4a7978cc57659046", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-07-21T14:22:49.156Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "52128", + "filename": "bynder-rte-icon.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "bynder-rte-icon.png", + "updated_at": "2022-07-21T14:22:49.156Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt4a7978cc57659046/62d961392b85cb6d849cce74/bynder-rte-icon.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blte04688a858dde8ed", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-07-21T14:22:49.349Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "97587", + "filename": "bynder-rte-selected-assets.png", + "parent_uid": "blt75dcc859c2359a54", + "tags": [], + "title": "bynder-rte-selected-assets.png", + "updated_at": "2022-07-21T14:22:49.349Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blte04688a858dde8ed/62d96139e9d7646bfeda2b47/bynder-rte-selected-assets.png" + } + ], + "seo": { + "canonical_url": "", + "description": "", + "image": null, + "robots": "", + "title": "" + }, + "static_links": [ + { + "type": "Documentation", + "url": "https://www.contentstack.com/docs/developers/marketplace-apps/bynder", + "_metadata": { + "uid": "cs5381f9ee9cce2964" + } + }, + { + "type": "End-User License Agreement", + "url": "https://www.contentstack.com/legal/eula-for-contentstack-proprietary-marketplace-apps/", + "_metadata": { + "uid": "cs072dfa7b76c413be" + } + } + ], + "summary": "Fetch digital assets (images and videos) from your Bynder account into a field of an entry. ", + "tags": [], + "title": "Bynder", + "updated_at": "2025-01-24T11:33:44.597Z", + "updated_by": "blt05ac693470ad5ff7", + "url": "/marketplace/bynder", + "use_cases": "

Manage Bynder digital media with ease: Contentstack's integration with Bynder allows you to link all your Bynder digital assets with ease. To access your Bynder assets, you don't have to leave your CMS. Instead, the integration allows the Bynder portal to open in the entry of your content type and you can choose and insert the assets within your entry page.

Get better control over all your marketing resources: The integration allows you to manage the entire content lifecycle of your marketing strategy. You can create content in Contentstack and use the Bynder Custom Field Extension to insert digital assets in your content. The integration provides a single solution to manage all your marketing resources inside your CMS.

Simplify sharing and distribution of digital assets: With Bynder's integration with Contentstack, your geographically dispersed teams can easily create, share, and distribute content. This makes it possible to execute large-scale marketing campaigns with cross-functional teams.

", + "publish_details": { + "time": "2025-01-28T10:53:49.649Z", + "user": "blt05ac693470ad5ff7", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + } + }, + { + "uid": "blt5307633e8c63d59d", + "_version": 17, + "locale": "en-us", + "ACL": {}, + "_in_progress": false, + "app_type": [ + { + "uid": "blte9d290b2e9e0931e", + "_content_type_uid": "app_types" + } + ], + "app_uid": "61c04dd81ecff10018907bed", + "built_by": [ + { + "uid": "bltec929d731b1f9d85", + "_content_type_uid": "organizations" + } + ], + "category": [ + { + "uid": "blt6abfa301df06bf93", + "_content_type_uid": "categories" + } + ], + "coming_soon": false, + "created_at": "2022-03-29T11:13:07.327Z", + "created_by": "blt89b5b90cb9e7c645", + "description": "

Contentstack' Marketplace Apps offers a variety of ways to integrate third-party applications, such as Cloudinary, directly into your headless CMS. With the Cloudinary app, you can streamline the process of managing and sharing images across multiple platforms.

Custom Field or JSON RTE, you can integrate Cloudinary with Contentstack directly. You can create content in Contentstack, and Cloudinary will ensure that you are able to view and access your Cloudinary images within your CMS.

", + "dynamic_links": [], + "feature_types": [ + { + "feature_type": [ + { + "uid": "blt6b35fa46f253a6bd", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "cs451ee37dd5248c6e" + }, + "description": "While creating entries, you can select one or more Cloudinary images or videos as the input value for the field." + }, + { + "feature_type": [ + { + "uid": "bltf9b70a5fd3c08c36", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "csa072b2b0b1c4e59b" + }, + "description": "Add assets from Cloudinary app to your JSON Rich Text Editor field using the Cloudinary JSON RTE Plugin" + } + ], + "icon": { + "_version": 1, + "is_dir": false, + "uid": "blt5492471633f54c05", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:10:07.336Z", + "created_by": "blt89b5b90cb9e7c645", + "file_size": "4694", + "filename": "cloudinary_cloud_glyph_512x512.png", + "parent_uid": "blt2eb26851df9c0e2e", + "tags": [], + "title": "cloudinary_cloud_glyph_512x512.png", + "updated_at": "2022-03-29T11:10:07.336Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt5492471633f54c05/6242e90f6aee76195cd5a511/cloudinary_cloud_glyph_512x512.png" + }, + "installation_url": "/#!/apps/61c04dd81ecff10018907bed/install", + "links": { + "source_code": "", + "documentation": "", + "others": [], + "end_user_license_agreement": "https://www.contentstack.com/legal/eula-for-contentstack-proprietary-marketplace-apps/" + }, + "screenshots": [ + { + "_version": 2, + "is_dir": false, + "uid": "blt8148fc15e5e4ea9e", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-09-06T12:39:36.971Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "75712", + "filename": "Cloudinary-Config-1.png", + "parent_uid": "bltb977c7f5e15fa4f5", + "tags": [], + "title": "Cloudinary-Config-Updated.png", + "updated_at": "2023-09-06T12:39:48.250Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt8148fc15e5e4ea9e/64f8730916f77f0e36cbb51b/Cloudinary-Config-1.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "bltfd9d4a80ad36f5de", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:10:08.343Z", + "created_by": "blt89b5b90cb9e7c645", + "file_size": "32748", + "filename": "coudinary_entry_creation.png", + "parent_uid": "bltb977c7f5e15fa4f5", + "tags": [], + "title": "coudinary_entry_creation.png", + "updated_at": "2022-03-29T11:10:08.343Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltfd9d4a80ad36f5de/6242e9109b68390fcd0530cd/coudinary_entry_creation.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt760c78d3f76b4df2", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:10:08.579Z", + "created_by": "blt89b5b90cb9e7c645", + "file_size": "328618", + "filename": "cloudinary_asset_listing.png", + "parent_uid": "bltb977c7f5e15fa4f5", + "tags": [], + "title": "cloudinary_asset_listing.png", + "updated_at": "2022-03-29T11:10:08.579Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt760c78d3f76b4df2/6242e910aeff370de94d90be/cloudinary_asset_listing.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt4e911a20719951a4", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:10:07.606Z", + "created_by": "blt89b5b90cb9e7c645", + "file_size": "222866", + "filename": "cloudinary_asset_loaded.png", + "parent_uid": "bltb977c7f5e15fa4f5", + "tags": [], + "title": "cloudinary_asset_loaded.png", + "updated_at": "2022-03-29T11:10:07.606Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt4e911a20719951a4/6242e90fe1962d6a51cef416/cloudinary_asset_loaded.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "bltf92c82a97fb2b970", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-05-26T06:52:38.068Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "44299", + "filename": "cloudinary-entry-json-rte.png", + "parent_uid": "bltb977c7f5e15fa4f5", + "tags": [], + "title": "cloudinary-entry-json-rte.png", + "updated_at": "2022-05-26T06:52:38.068Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltf92c82a97fb2b970/628f23b640e2312e82dd096b/cloudinary-entry-json-rte.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt7ddc3a7b1103771c", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-05-26T06:52:37.903Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "38567", + "filename": "cloudinary-add-plugin.png", + "parent_uid": "bltb977c7f5e15fa4f5", + "tags": [], + "title": "cloudinary-add-plugin.png", + "updated_at": "2022-05-26T06:52:37.903Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt7ddc3a7b1103771c/628f23b5170c712e88665a7e/cloudinary-add-plugin.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blte3a2863a52337490", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-05-26T06:52:37.925Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "61508", + "filename": "cloudinary-add-plugin-added.png", + "parent_uid": "bltb977c7f5e15fa4f5", + "tags": [], + "title": "cloudinary-add-plugin-added.png", + "updated_at": "2022-05-26T06:52:37.925Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blte3a2863a52337490/628f23b597d6686b1cff96f3/cloudinary-add-plugin-added.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "bltdefa93e028fdb7a4", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-05-26T06:52:37.768Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "18372", + "filename": "cloudinary-rte.png", + "parent_uid": "bltb977c7f5e15fa4f5", + "tags": [], + "title": "cloudinary-rte.png", + "updated_at": "2022-05-26T06:52:37.768Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltdefa93e028fdb7a4/628f23b519611a7d301084a4/cloudinary-rte.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blte93e98a5c1f9b21b", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-05-26T06:52:38.513Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "356728", + "filename": "cloudinary-dashboard.png", + "parent_uid": "bltb977c7f5e15fa4f5", + "tags": [], + "title": "cloudinary-dashboard.png", + "updated_at": "2022-05-26T06:52:38.513Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blte93e98a5c1f9b21b/628f23b6eaff1924caa64c4b/cloudinary-dashboard.png" + } + ], + "seo": { + "canonical_url": "", + "description": "", + "image": null, + "robots": "", + "title": "" + }, + "static_links": [ + { + "type": "Documentation", + "url": "https://www.contentstack.com/docs/developers/marketplace-apps/cloudinary/", + "_metadata": { + "uid": "cs5be545df98c27320" + } + }, + { + "type": "End-User License Agreement", + "url": "https://www.contentstack.com/legal/eula-for-contentstack-proprietary-marketplace-apps/", + "_metadata": { + "uid": "cs2273829bfbdc05f2" + } + } + ], + "summary": "Fetches assets (images and videos) from your Cloudinary account into the field of an entry.", + "tags": [], + "title": "Cloudinary", + "updated_at": "2025-01-24T11:15:04.176Z", + "updated_by": "blt05ac693470ad5ff7", + "url": "/marketplace/cloudinary", + "use_cases": "

One interface for content and asset management: The integration of Contentstack with Cloudinary allows you to manage content and Cloudinary assets in a single interface. Rather than managing two different apps (CMS and Cloudinary),  you save time and effort by accessing Cloudinary assets from the entry page.

Simplified digital asset sharing and distribution: Once you integrate Contentstack with Cloudinary, your geographically dispersed teams can easily create, share, and distribute content across different departments. Thus, the execution of large-scale marketing campaigns with cross-functional teams becomes possible.

Improved control over all your marketing resources: With the integration of Contentstack and Cloudinary, you gain better control over your marketing resources, allowing you to easily manage the entire content lifecycle. You simply have to create content in Contentstack and use the Cloudinary Custom Field Extension to insert digital assets in your entry.

", + "publish_details": { + "time": "2025-01-28T10:55:29.069Z", + "user": "blt05ac693470ad5ff7", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + } + }, + { + "uid": "blteb2e1c080caa44dd", + "_version": 3, + "locale": "en-us", + "ACL": {}, + "_in_progress": false, + "app_type": [ + { + "uid": "blte9d290b2e9e0931e", + "_content_type_uid": "app_types" + } + ], + "app_uid": "6667fb7b2e29d10012f8c342", + "built_by": [ + { + "uid": "bltec929d731b1f9d85", + "_content_type_uid": "organizations" + } + ], + "category": [ + { + "uid": "blt6abfa301df06bf93", + "_content_type_uid": "categories" + } + ], + "coming_soon": false, + "created_at": "2024-06-11T08:25:11.583Z", + "created_by": "blt7314f6e631536c9d", + "description": "

By integrating the Digizuite app from the Contentstack Marketplace into your headless CMS, you can effortlessly manage, share, and collaborate on assets across multiple platforms.

By using the Custom Field and JSON Rich Text Editor plugin, you can integrate Digizuite with Contentstack. You can create an entry in Contentstack and view all your digital assets stored in your Digizuite account inside it.

", + "dynamic_links": [], + "feature_types": [ + { + "feature_type": [ + { + "uid": "blt6b35fa46f253a6bd", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "csb0eb4bdf2eac6490" + }, + "description": "Custom Field allows you to select multiple assets from your Digizuite account and add them to your entry." + }, + { + "feature_type": [ + { + "uid": "bltf9b70a5fd3c08c36", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "cs9b68fe7c8a9a15ee" + }, + "description": "Using the Digizuite JSON RTE Plugin, you can add assets from the Digizuite app to your JSON Rich Text Editor field." + } + ], + "icon": { + "_version": 1, + "is_dir": false, + "uid": "blt1b43d8021f6505c7", + "ACL": {}, + "content_type": "image/svg+xml", + "created_at": "2024-06-11T07:59:32.914Z", + "created_by": "blt7314f6e631536c9d", + "file_size": "1233", + "filename": "digizuite-logo.svg", + "parent_uid": "blt2eb26851df9c0e2e", + "tags": [], + "title": "digizuite-logo.svg", + "updated_at": "2024-06-11T07:59:32.914Z", + "updated_by": "blt7314f6e631536c9d", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt1b43d8021f6505c7/666803e471a11048c94face4/digizuite-logo.svg" + }, + "installation_url": "/#!/apps/6667fb7b2e29d10012f8c342/install", + "links": { + "source_code": "", + "documentation": "https://www.contentstack.com/docs/developers/marketplace-apps/digizuite", + "end_user_license_agreement": "https://www.contentstack.com/legal/eula-for-contentstack-proprietary-marketplace-apps/", + "others": [] + }, + "screenshots": [ + { + "_version": 1, + "is_dir": false, + "uid": "blt66f588d3cff98276", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-06-11T09:07:25.279Z", + "created_by": "blt7314f6e631536c9d", + "file_size": "21793", + "filename": "9-Digizuite-JSONRTE-App-Icon.png", + "parent_uid": "blt613a74bd13dd3512", + "tags": [], + "title": "9-Digizuite-JSONRTE-App-Icon.png", + "updated_at": "2024-06-11T09:07:25.279Z", + "updated_by": "blt7314f6e631536c9d", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt66f588d3cff98276/666813cd88395e06f03965f0/9-Digizuite-JSONRTE-App-Icon.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "bltd34a9b21ede5e552", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-06-11T09:07:25.375Z", + "created_by": "blt7314f6e631536c9d", + "file_size": "49714", + "filename": "2-Digizuite-UI-Locations.png", + "parent_uid": "blt613a74bd13dd3512", + "tags": [], + "title": "2-Digizuite-UI-Locations.png", + "updated_at": "2024-06-11T09:07:25.375Z", + "updated_by": "blt7314f6e631536c9d", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltd34a9b21ede5e552/666813cdd7f5604f7b440a9f/2-Digizuite-UI-Locations.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blta1b7d8842cd0a4bb", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-06-11T09:07:25.527Z", + "created_by": "blt7314f6e631536c9d", + "file_size": "55326", + "filename": "3-Digizuite-Custom-Field-Sample-Entry.png", + "parent_uid": "blt613a74bd13dd3512", + "tags": [], + "title": "3-Digizuite-Custom-Field-Sample-Entry.png", + "updated_at": "2024-06-11T09:07:25.527Z", + "updated_by": "blt7314f6e631536c9d", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blta1b7d8842cd0a4bb/666813cdec03bd02bb15a673/3-Digizuite-Custom-Field-Sample-Entry.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blta00eab11702b4b97", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-06-11T09:07:25.527Z", + "created_by": "blt7314f6e631536c9d", + "file_size": "107874", + "filename": "1-Digizuite-Configuration.png", + "parent_uid": "blt613a74bd13dd3512", + "tags": [], + "title": "1-Digizuite-Configuration.png", + "updated_at": "2024-06-11T09:07:25.527Z", + "updated_by": "blt7314f6e631536c9d", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blta00eab11702b4b97/666813cd7456ddd5b9512c0d/1-Digizuite-Configuration.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt15c09164d59523eb", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-06-11T09:07:25.502Z", + "created_by": "blt7314f6e631536c9d", + "file_size": "40003", + "filename": "4-Digizuite-Selector-Page-Login.png", + "parent_uid": "blt613a74bd13dd3512", + "tags": [], + "title": "4-Digizuite-Selector-Page-Login.png", + "updated_at": "2024-06-11T09:07:25.502Z", + "updated_by": "blt7314f6e631536c9d", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt15c09164d59523eb/666813cd679953d444447fdd/4-Digizuite-Selector-Page-Login.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blta6eaff56b60384ea", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-06-11T09:07:25.546Z", + "created_by": "blt7314f6e631536c9d", + "file_size": "233566", + "filename": "6-Digizuite-Selector-Page-Multi-Insert.png", + "parent_uid": "blt613a74bd13dd3512", + "tags": [], + "title": "6-Digizuite-Selector-Page-Multi-Insert.png", + "updated_at": "2024-06-11T09:07:25.546Z", + "updated_by": "blt7314f6e631536c9d", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blta6eaff56b60384ea/666813cdd7f5606cd2440aa3/6-Digizuite-Selector-Page-Multi-Insert.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt03e3f8cedee5b290", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-06-11T09:07:25.568Z", + "created_by": "blt7314f6e631536c9d", + "file_size": "71705", + "filename": "7-Digizuite-Custom-Field-Assets-Added.png", + "parent_uid": "blt613a74bd13dd3512", + "tags": [], + "title": "7-Digizuite-Custom-Field-Assets-Added.png", + "updated_at": "2024-06-11T09:07:25.568Z", + "updated_by": "blt7314f6e631536c9d", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt03e3f8cedee5b290/666813cde46c314c3d364f88/7-Digizuite-Custom-Field-Assets-Added.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "bltbfc8e81a474ab1c5", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-06-11T09:07:25.435Z", + "created_by": "blt7314f6e631536c9d", + "file_size": "87890", + "filename": "8-Digizuite-Custom-Field-Assets-View-Thumbnail-Features.png", + "parent_uid": "blt613a74bd13dd3512", + "tags": [], + "title": "8-Digizuite-Custom-Field-Assets-View-Thumbnail-Features.png", + "updated_at": "2024-06-11T09:07:25.435Z", + "updated_by": "blt7314f6e631536c9d", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltbfc8e81a474ab1c5/666813cd472c5f1d5fc8768c/8-Digizuite-Custom-Field-Assets-View-Thumbnail-Features.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt12d61d2546171c89", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-06-11T09:07:25.720Z", + "created_by": "blt7314f6e631536c9d", + "file_size": "113812", + "filename": "10-Digizuite-JSONRTE-Assets-Added-Features.png", + "parent_uid": "blt613a74bd13dd3512", + "tags": [], + "title": "10-Digizuite-JSONRTE-Assets-Added-Features.png", + "updated_at": "2024-06-11T09:07:25.720Z", + "updated_by": "blt7314f6e631536c9d", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt12d61d2546171c89/666813cd049fefc578cc26a7/10-Digizuite-JSONRTE-Assets-Added-Features.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt36ee373648b4fa4c", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-06-11T09:07:25.820Z", + "created_by": "blt7314f6e631536c9d", + "file_size": "207392", + "filename": "5-Digizuite-Selector-Page-Placing-Single-Asset.png", + "parent_uid": "blt613a74bd13dd3512", + "tags": [], + "title": "5-Digizuite-Selector-Page-Placing-Single-Asset.png", + "updated_at": "2024-06-11T09:07:25.820Z", + "updated_by": "blt7314f6e631536c9d", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt36ee373648b4fa4c/666813cd599f1a26956ea87d/5-Digizuite-Selector-Page-Placing-Single-Asset.png" + } + ], + "seo": { + "canonical_url": "", + "description": "", + "image": null, + "robots": "", + "title": "" + }, + "static_links": [ + { + "type": "Documentation", + "url": "https://www.contentstack.com/docs/developers/marketplace-apps/digizuite", + "_metadata": { + "uid": "cse4809e1dd2392680" + } + }, + { + "type": "End-User License Agreement", + "url": "https://www.contentstack.com/legal/eula-for-contentstack-proprietary-marketplace-apps/", + "_metadata": { + "uid": "cs8ce0c93fa2b05dc3" + } + } + ], + "summary": "Add digital assets from your Digizuite account into your Contentstack entries.", + "tags": [], + "title": "Digizuite", + "updated_at": "2025-01-24T06:24:10.302Z", + "updated_by": "blt05ac693470ad5ff7", + "url": "/marketplace/digizuite", + "use_cases": "

Manage Digizuite digital media with ease: The Digizuite app lets you manage assets within your entries. With Digizuite, you can easily access all the assets without leaving your headless CMS.

Better marketing campaigns with easy-to-access assets: You can create content in Contentstack and insert assets in your entry using the Digizuite Custom Field or JSON RTE field. This allows cross-functional teams to work on large-scale campaigns easily.

Easy distribution of digital assets: The Digizuite app allows geographically dispersed teams to readily store, share, and fetch digital media assets. 

", + "publish_details": { + "time": "2025-01-28T10:56:57.063Z", + "user": "blt05ac693470ad5ff7", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + } + }, + { + "uid": "blt802cfeedb40e4e3c", + "_version": 4, + "locale": "en-us", + "ACL": {}, + "_in_progress": false, + "app_type": [ + { + "uid": "blte9d290b2e9e0931e", + "_content_type_uid": "app_types" + } + ], + "app_uid": "65eec8748ae77300126404c3", + "built_by": [ + { + "uid": "bltec929d731b1f9d85", + "_content_type_uid": "organizations" + } + ], + "category": [ + { + "uid": "blt6abfa301df06bf93", + "_content_type_uid": "categories" + } + ], + "coming_soon": false, + "created_at": "2024-03-11T11:10:31.717Z", + "created_by": "blt78cea3a89ceb6137", + "description": "

By integrating the Egnyte app from the Contentstack Marketplace into your headless CMS, you can effortlessly manage, share, and collaborate on assets across multiple platforms.

By using the Custom Field and JSON Rich Text Editor plugin, you can integrate Egnyte with Contentstack. You can create an entry in Contentstack and view all your digital assets stored in Egnyte inside it.

", + "dynamic_links": [], + "feature_types": [ + { + "feature_type": [ + { + "uid": "blt6b35fa46f253a6bd", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "cs1ded11395e73ee37" + }, + "description": "Custom Field allows you to select multiple assets from your Egnyte account and add them to your entry." + }, + { + "feature_type": [ + { + "uid": "bltf9b70a5fd3c08c36", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "csdd8734e35c172e8a" + }, + "description": "You can add assets from the Egnyte app to your JSON Rich Text Editor field using the Egnyte JSON RTE Plugin." + } + ], + "icon": { + "_version": 1, + "is_dir": false, + "uid": "blt9bbc2541009fcd2f", + "ACL": {}, + "content_type": "image/svg+xml", + "created_at": "2024-03-11T11:02:36.047Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "1467", + "filename": "Egnyte_512.svg", + "parent_uid": "blt2eb26851df9c0e2e", + "tags": [], + "title": "Egnyte_512.svg", + "updated_at": "2024-03-11T11:02:36.047Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt9bbc2541009fcd2f/65eee4cc6b2184040adf6434/Egnyte_512.svg" + }, + "installation_url": "/#!/apps/65eec8748ae77300126404c3/install", + "links": { + "source_code": "", + "documentation": "", + "end_user_license_agreement": "", + "others": [] + }, + "screenshots": [ + { + "_version": 1, + "is_dir": false, + "uid": "blt39e69a5b368a44e2", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-03-11T11:23:56.868Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "94158", + "filename": "SelectAssets.png", + "parent_uid": "blt2c3df632773487d4", + "tags": [], + "title": "SelectAssets.png", + "updated_at": "2024-03-11T11:23:56.868Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt39e69a5b368a44e2/65eee9cc2d7272040a9be3b5/SelectAssets.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blta1c3e7ab01f46c79", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-03-11T11:23:56.802Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "54525", + "filename": "JSONRTEModeling.png", + "parent_uid": "blt2c3df632773487d4", + "tags": [], + "title": "JSONRTEModeling.png", + "updated_at": "2024-03-11T11:23:56.802Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blta1c3e7ab01f46c79/65eee9cc9b3861040aba306a/JSONRTEModeling.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "bltee77f8357082a1bf", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-03-11T11:23:56.774Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "79398", + "filename": "SelectExtensionFinal.png", + "parent_uid": "blt2c3df632773487d4", + "tags": [], + "title": "SelectExtensionFinal.png", + "updated_at": "2024-03-11T11:23:56.774Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltee77f8357082a1bf/65eee9cc9b01c5040a371747/SelectExtensionFinal.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt34b26dc503b5e85c", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-03-11T11:23:56.682Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "20095", + "filename": "JsonRTE_Select.png", + "parent_uid": "blt2c3df632773487d4", + "tags": [], + "title": "JsonRTE_Select.png", + "updated_at": "2024-03-11T11:23:56.682Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt34b26dc503b5e85c/65eee9cc00aa28040ab71676/JsonRTE_Select.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blta5e4ff04ec5dcee3", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-03-11T11:23:43.006Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "54241", + "filename": "EnterPassword.png", + "parent_uid": "blt2c3df632773487d4", + "tags": [], + "title": "EnterPassword.png", + "updated_at": "2024-03-11T11:23:43.006Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blta5e4ff04ec5dcee3/65eee9bf32eb75040a886817/EnterPassword.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "bltb9da556882e17694", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-03-11T11:23:43.005Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "73834", + "filename": "AppExtensionSelect.png", + "parent_uid": "blt2c3df632773487d4", + "tags": [], + "title": "AppExtensionSelect.png", + "updated_at": "2024-03-11T11:23:43.005Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltb9da556882e17694/65eee9bff2e3cc040ac912c1/AppExtensionSelect.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt428d15bb4444686f", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-03-11T11:23:43.002Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "85393", + "filename": "ConfigScreen.png", + "parent_uid": "blt2c3df632773487d4", + "tags": [], + "title": "ConfigScreen.png", + "updated_at": "2024-03-11T11:23:43.002Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt428d15bb4444686f/65eee9bf6c9f9b040a4d470e/ConfigScreen.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt4d94b995ba81c23e", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-03-11T11:23:42.992Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "88287", + "filename": "ConfigScreenWithData.png", + "parent_uid": "blt2c3df632773487d4", + "tags": [], + "title": "ConfigScreenWithData.png", + "updated_at": "2024-03-11T11:23:42.992Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt4d94b995ba81c23e/65eee9beacd14e0407fb942f/ConfigScreenWithData.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt78f68811cc9e7f85", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-03-11T11:23:42.766Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "144988", + "filename": "AppEntryCreated.png", + "parent_uid": "blt2c3df632773487d4", + "tags": [], + "title": "AppEntryCreated.png", + "updated_at": "2024-03-11T11:23:42.766Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt78f68811cc9e7f85/65eee9bef40463040a6f7c13/AppEntryCreated.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt15b5d6ef0c1ee73d", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-03-11T11:23:42.707Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "67969", + "filename": "CustomField1.png", + "parent_uid": "blt2c3df632773487d4", + "tags": [], + "title": "CustomField1.png", + "updated_at": "2024-03-11T11:23:42.707Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt15b5d6ef0c1ee73d/65eee9be5aa9cf0407b08f75/CustomField1.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt838f8692be88b3fb", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-03-11T11:23:42.696Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "47709", + "filename": "AllowAccess.png", + "parent_uid": "blt2c3df632773487d4", + "tags": [], + "title": "AllowAccess.png", + "updated_at": "2024-03-11T11:23:42.696Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt838f8692be88b3fb/65eee9bef40463040a6f7c0f/AllowAccess.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt7108e5839f206055", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-03-11T11:23:42.677Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "73095", + "filename": "ContentModelingApp.png", + "parent_uid": "blt2c3df632773487d4", + "tags": [], + "title": "ContentModelingApp.png", + "updated_at": "2024-03-11T11:23:42.677Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt7108e5839f206055/65eee9be5b334a040a1ebffe/ContentModelingApp.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt61671b65d7b3f75a", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-03-11T11:23:42.670Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "41425", + "filename": "AddEmail.png", + "parent_uid": "blt2c3df632773487d4", + "tags": [], + "title": "AddEmail.png", + "updated_at": "2024-03-11T11:23:42.670Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt61671b65d7b3f75a/65eee9be9b3861040aba3066/AddEmail.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt8fb40eb9558601fb", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-03-11T11:23:42.609Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "17201", + "filename": "AppEntryCreation.png", + "parent_uid": "blt2c3df632773487d4", + "tags": [], + "title": "AppEntryCreation.png", + "updated_at": "2024-03-11T11:23:42.609Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt8fb40eb9558601fb/65eee9be2d7272040a9be3b1/AppEntryCreation.png" + } + ], + "seo": { + "canonical_url": "", + "description": "", + "image": null, + "robots": "", + "title": "" + }, + "static_links": [ + { + "type": "End-User License Agreement", + "url": "https://www.contentstack.com/legal/eula-for-contentstack-proprietary-marketplace-apps/", + "_metadata": { + "uid": "csc0ffeffc343abc82" + } + }, + { + "type": "Documentation", + "url": "https://www.contentstack.com/docs/developers/marketplace-apps/egnyte", + "_metadata": { + "uid": "csfdebc9f93c49b957" + } + } + ], + "summary": "Fetch digital assets from your Egnyte account into your Contentstack entries. ", + "tags": [], + "title": "Egnyte", + "updated_at": "2025-01-24T06:28:44.659Z", + "updated_by": "blt05ac693470ad5ff7", + "url": "/marketplace/egnyte", + "use_cases": "

Manage Egnyte digital media with ease: The Egnyte app lets you manage assets within your entries. With Egnyte, you can easily access all the assets without leaving your headless CMS.

Better marketing campaigns with ease-to-access assets: You can create content in Contentstack and use the Egnyte Custom Field or JSON RTE field to insert assets in your entry. With this, cross-functional teams can work on large-scale campaigns with ease.

Easy distribution of digital assets: The Egnyte app allows geographically dispersed teams to readily store, share, and fetch digital media assets.

", + "publish_details": { + "time": "2025-01-28T10:55:57.436Z", + "user": "blt05ac693470ad5ff7", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + } + }, + { + "uid": "blt049c7b2db4db67a4", + "_version": 7, + "locale": "en-us", + "ACL": {}, + "_in_progress": false, + "app_type": [ + { + "uid": "blte9d290b2e9e0931e", + "_content_type_uid": "app_types" + } + ], + "app_uid": "65e997d02adeba0012c6e00f", + "built_by": [ + { + "uid": "blt4ecd9b73e0a6eef7", + "_content_type_uid": "organizations" + } + ], + "category": [ + { + "uid": "blt6abfa301df06bf93", + "_content_type_uid": "categories" + } + ], + "coming_soon": false, + "created_at": "2024-05-09T05:28:47.806Z", + "created_by": "blt78cea3a89ceb6137", + "description": "

Filerobot by Scaleflex is the single source of truth for all your digital assets, allowing for seamless collaboration across platforms via the Asset Hub and through a lively ecosystem of plugins and connectors.

Don't bother with multiple cloud storages, image editors, file compression, and CDN setup anymore, Filerobot takes care of it all.

Upload your digital assets (images, videos, and more) to Contentstack from Scaleflex’s Filerobot platform via the integrated modular widget.

Your media will then be natively accelerated by a top tier, multi-tenant, Content Delivery Network (CDN) architecture.

Deliver beautiful digital experiences thanks to seamless content operations:

Filerobot is a scalable, performance-oriented digital asset management platform. It has integrated image and video optimizers that store, organize, optimize, and deliver all your media assets lightning-fast around the world, to all device types.

Filerobot has a multi-tenant, scalable, and flexible file system, with an intuitive interface that simplifies complex workflows and integrations within your content operations. It uses AI-powered uploads with automatic workflows to enrich media assets for better categorization and retrieval.

Our overall mission?

To give meaning to your brand assets.

With Filerobot DAM, you’ll be able to:

1. Upload, manage, and enrich all of your digital assets from a single source of truth.

2. Collaborate with internal and external teams within our one cloud collaboration platform.

3. Optimize your images to boost SEO rankings, create beautiful digital experiences, and push device-friendly content.

4. Publish and accelerate the impact of your images thanks to real-time insights on the usage and performance of your media assets.

There’s no development needed to start reaping the benefits of Filerobot. Simply set up your account and start enjoying fast and responsive images now.

", + "dynamic_links": [], + "feature_types": [], + "icon": { + "_version": 1, + "is_dir": false, + "uid": "blta4e22048c78a4aa8", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-05-09T05:27:27.778Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "14374", + "filename": "Scaleflex_Filerobot.png", + "parent_uid": "blt2eb26851df9c0e2e", + "tags": [], + "title": "Scaleflex_Filerobot.png", + "updated_at": "2024-05-09T05:27:27.778Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blta4e22048c78a4aa8/663c5ebfa4f240e5875f3dfc/Scaleflex_Filerobot.png" + }, + "installation_url": "/#!/apps/65e997d02adeba0012c6e00f/install", + "links": { + "source_code": "", + "documentation": "https://docs.scaleflex.com/filerobot-documentation/headless-dam-and-api/plugins-and-connectors/plugins/contentstack", + "end_user_license_agreement": "https://legal.scaleflex.com/privacy/global-privacy-policy", + "others": [ + { + "title": "Support & Feedback", + "href": "hiep.le@scaleflex.com" + } + ] + }, + "screenshots": [ + { + "_version": 1, + "is_dir": false, + "uid": "blt652e924ceb8551f9", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-05-09T05:34:14.893Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "65506", + "filename": "json-rte-4.png", + "parent_uid": "blt60b81c8c84a94a5f", + "tags": [], + "title": "json-rte-4.png", + "updated_at": "2024-05-09T05:34:14.893Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt652e924ceb8551f9/663c605685e08b68b4718c7a/json-rte-4.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt20a45772230850d6", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-05-09T05:34:15.045Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "168564", + "filename": "config-screen-1.png", + "parent_uid": "blt60b81c8c84a94a5f", + "tags": [], + "title": "config-screen-1.png", + "updated_at": "2024-05-09T05:34:15.045Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt20a45772230850d6/663c60573af9ac0cd499b85b/config-screen-1.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt61bcba33de8f290f", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-05-09T05:34:15.142Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "68051", + "filename": "customfield-2.png", + "parent_uid": "blt60b81c8c84a94a5f", + "tags": [], + "title": "customfield-2.png", + "updated_at": "2024-05-09T05:34:15.142Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt61bcba33de8f290f/663c6057ece60c9134b95ce0/customfield-2.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt8190f96123ca9a48", + "ACL": {}, + "content_type": "image/png", + "created_at": "2024-05-09T05:34:15.727Z", + "created_by": "blt78cea3a89ceb6137", + "file_size": "808508", + "filename": "selector-page-3.png", + "parent_uid": "blt60b81c8c84a94a5f", + "tags": [], + "title": "selector-page-3.png", + "updated_at": "2024-05-09T05:34:15.727Z", + "updated_by": "blt78cea3a89ceb6137", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt8190f96123ca9a48/663c605793aa0d4b8c45683a/selector-page-3.png" + } + ], + "seo": { + "canonical_url": "", + "description": "", + "image": null, + "robots": "", + "title": "" + }, + "static_links": [ + { + "type": "Documentation", + "url": "https://docs.scaleflex.com/filerobot-documentation", + "_metadata": { + "uid": "cs9dc661efd6bcd427" + } + } + ], + "summary": "Filerobot allows you to store, enrich, normalize, resize, optimize, & distribute your media files rocket fast worldwide!", + "tags": [], + "title": "Filerobot by Scaleflex", + "updated_at": "2025-01-24T06:24:48.781Z", + "updated_by": "blt05ac693470ad5ff7", + "url": "/marketplace/filerobot-by-scaleflex", + "use_cases": "

With Filerobot DAM you’ll be able to:

1. Upload, manage, and enrich all of your digital assets from a single source of truth.

2. Collaborate with internal and external teams within our one cloud collaboration platform.

3. Optimize your images to boost SEO rankings, create beautiful digital experiences, and push device-friendly content.

4. Publish and accelerate the impact of your images thanks to real-time insights on the usage and performance of your media assets.

", + "publish_details": { + "time": "2025-01-28T10:56:57.075Z", + "user": "blt05ac693470ad5ff7", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + } + }, + { + "uid": "blt34a14a6e22c82feb", + "_version": 4, + "locale": "en-us", + "_in_progress": false, + "app_type": [ + { + "uid": "blte9d290b2e9e0931e", + "_content_type_uid": "app_types" + } + ], + "app_uid": "6311ab2b92549700190fe29a", + "built_by": [ + { + "uid": "bltec929d731b1f9d85", + "_content_type_uid": "organizations" + } + ], + "category": [ + { + "uid": "blt6abfa301df06bf93", + "_content_type_uid": "categories" + } + ], + "coming_soon": false, + "created_at": "2022-09-02T06:58:07.096Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "description": "

Contentstack Marketplace offers a variety of ways to integrate third-party applications, such as Frontify, into your headless CMS. You can easily manage and share digital assets across multiple platforms.

Using the Custom Field and JSON RTE plugin, you can integrate Frontify with Contentstack. You can create an entry in Contentstack and, with the integration, view all the digital assets in Contentstack.

", + "dynamic_links": [], + "feature_types": [ + { + "feature_type": [ + { + "uid": "blt6b35fa46f253a6bd", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "cs7af6aebd48894a93" + }, + "description": "Custom Field allows you to select multiple images, videos, logos, and documents to add them to your entry from your Frontify account." + }, + { + "feature_type": [ + { + "uid": "bltf9b70a5fd3c08c36", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "cs770641304ceb4a2c" + }, + "description": "You can add assets from the Frontify app to your JSON Rich Text Editor field using the Frontify JSON RTE Plugin." + } + ], + "icon": { + "_version": 1, + "is_dir": false, + "uid": "blt736048058b05d53a", + "ACL": {}, + "content_type": "image/svg+xml", + "created_at": "2022-09-02T11:32:41.976Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "3182", + "filename": "frontify-icon.svg", + "parent_uid": "blt2eb26851df9c0e2e", + "tags": [], + "title": "frontify-icon.svg", + "updated_at": "2022-09-02T11:32:41.976Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt736048058b05d53a/6311e9d9ebabda6520edad38/frontify-icon.svg" + }, + "installation_url": "/#!/apps/6311ab2b92549700190fe29a/install", + "links": { + "source_code": "", + "documentation": "", + "end_user_license_agreement": "", + "others": [] + }, + "screenshots": [ + { + "_version": 3, + "is_dir": false, + "uid": "blt1b9cdb2da864eca7", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-02T11:35:06.796Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "description": "", + "file_size": "40411", + "filename": "config_screen.png", + "parent_uid": "bltaefa20ab37acae9e", + "tags": [], + "title": "config_screen.png", + "updated_at": "2022-09-09T09:41:53.388Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt1b9cdb2da864eca7/631b0a6153c6564a15946c06/config_screen.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt295f242046e3a77e", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-02T11:35:08.161Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "38255", + "filename": "customField_appOptions.png", + "parent_uid": "bltaefa20ab37acae9e", + "tags": [], + "title": "customField_appOptions.png", + "updated_at": "2022-09-02T11:35:08.161Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt295f242046e3a77e/6311ea6ce6bd866fcfd99c15/customField_appOptions.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt22016a475135edfc", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-02T11:35:24.081Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "48796", + "filename": "customField_contentType.png", + "parent_uid": "bltaefa20ab37acae9e", + "tags": [], + "title": "customField_contentType.png", + "updated_at": "2022-09-02T11:35:24.081Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt22016a475135edfc/6311ea7c6304777e0f86d8fa/customField_contentType.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt45be00b9ef6619f5", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-02T11:35:18.310Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "26512", + "filename": "customField_noData_entry.png", + "parent_uid": "bltaefa20ab37acae9e", + "tags": [], + "title": "customField_noData_entry.png", + "updated_at": "2022-09-02T11:35:18.310Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt45be00b9ef6619f5/6311ea761004b759387c7376/customField_noData_entry.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt5f3939ea525cd94b", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-02T11:35:53.957Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "367075", + "filename": "selector_page.png", + "parent_uid": "bltaefa20ab37acae9e", + "tags": [], + "title": "selector_page.png", + "updated_at": "2022-09-02T11:35:53.957Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt5f3939ea525cd94b/6311ea9980f5d16e5c0da367/selector_page.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt2c7e3042f2394058", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-02T11:35:53.716Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "350610", + "filename": "selectorPage_selectAssets.png", + "parent_uid": "bltaefa20ab37acae9e", + "tags": [], + "title": "selectorPage_selectAssets.png", + "updated_at": "2022-09-02T11:35:53.716Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt2c7e3042f2394058/6311ea99f948c06df05c2caf/selectorPage_selectAssets.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt239f09512de2930f", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-02T11:35:30.319Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "119946", + "filename": "customField_entry.png", + "parent_uid": "bltaefa20ab37acae9e", + "tags": [], + "title": "customField_entry.png", + "updated_at": "2022-09-02T11:35:30.319Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt239f09512de2930f/6311ea82fc0b1963dcef285a/customField_entry.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt5665105a7d6053f5", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-02T11:35:40.654Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "301475", + "filename": "customField_assetHover.png", + "parent_uid": "bltaefa20ab37acae9e", + "tags": [], + "title": "customField_assetHover.png", + "updated_at": "2022-09-02T11:35:40.654Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt5665105a7d6053f5/6311ea8cde82716cc171c83e/customField_assetHover.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt6cab308f19f4b548", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-02T11:35:28.330Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "65667", + "filename": "rte_contentType.png", + "parent_uid": "bltaefa20ab37acae9e", + "tags": [], + "title": "rte_contentType.png", + "updated_at": "2022-09-02T11:35:28.330Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt6cab308f19f4b548/6311ea80b21f130163e54441/rte_contentType.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt43631bb8c1505ae9", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-02T11:35:26.095Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "38223", + "filename": "rte_plugin_options.png", + "parent_uid": "bltaefa20ab37acae9e", + "tags": [], + "title": "rte_plugin_options.png", + "updated_at": "2022-09-02T11:35:26.095Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt43631bb8c1505ae9/6311ea7e31bdfe6df1c20a37/rte_plugin_options.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt3cf5039ad42d2ae4", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-02T11:35:26.007Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "35223", + "filename": "rte_icon_button.png", + "parent_uid": "bltaefa20ab37acae9e", + "tags": [], + "title": "rte_icon_button.png", + "updated_at": "2022-09-02T11:35:26.007Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt3cf5039ad42d2ae4/6311ea7ede82716cc171c83a/rte_icon_button.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blte2a4d111c21187f1", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-09-02T11:35:30.246Z", + "created_by": "bltd5e1f9e7bbbf5fff", + "file_size": "112368", + "filename": "rte_entry.png", + "parent_uid": "bltaefa20ab37acae9e", + "tags": [], + "title": "rte_entry.png", + "updated_at": "2022-09-02T11:35:30.246Z", + "updated_by": "bltd5e1f9e7bbbf5fff", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blte2a4d111c21187f1/6311ea82e6bd866fcfd99c19/rte_entry.png" + } + ], + "seo": { + "canonical_url": "", + "description": "", + "image": null, + "robots": "", + "title": "" + }, + "static_links": [ + { + "type": "End-User License Agreement", + "url": "https://www.contentstack.com/legal/eula-for-contentstack-proprietary-marketplace-apps/", + "_metadata": { + "uid": "csbfcfd116d3094522" + } + }, + { + "type": "Documentation", + "url": "https://www.contentstack.com/docs/developers/marketplace-apps/frontify/", + "_metadata": { + "uid": "csd0a7675e2345489a" + } + } + ], + "summary": "Fetch digital assets (images/documents/video/music etc.) from your Frontify account into a Contentstack entry.", + "tags": [], + "title": "Frontify", + "updated_at": "2025-01-24T11:32:55.556Z", + "updated_by": "blt05ac693470ad5ff7", + "url": "/marketplace/frontify", + "use_cases": "

Manage Frontify digital media with ease: The Frontify app lets you manage images, videos, icons, and documents within your entries. With Frontify, you can easily link all the digital assets without leaving your headless CMS.

Better marketing campaigns with ease-to-access assets: You can create content in Contentstack and use the Frontify Custom Field or JSON RTE Field to insert digital assets in your content. With this, cross-functional teams can work on large-scale campaigns with ease.

Easy distribution of digital assets: The Frontify app allows geographically dispersed teams to easily store, share and fetch digital media assets.

", + "ACL": {}, + "publish_details": { + "time": "2025-01-28T10:53:49.663Z", + "user": "blt05ac693470ad5ff7", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + } + }, + { + "uid": "blt5efc6f55432a0878", + "_version": 10, + "locale": "en-us", + "ACL": {}, + "_in_progress": false, + "app_type": [ + { + "uid": "blte9d290b2e9e0931e", + "_content_type_uid": "app_types" + } + ], + "app_uid": "621640ff4671880018efec74", + "built_by": [ + { + "uid": "bltec929d731b1f9d85", + "_content_type_uid": "organizations" + } + ], + "category": [ + { + "uid": "blt6abfa301df06bf93", + "_content_type_uid": "categories" + } + ], + "coming_soon": false, + "created_at": "2022-03-29T11:13:09.115Z", + "created_by": "blt89b5b90cb9e7c645", + "description": "

The Image Preset Builder app is a collection of formatting tools that help transform and optimize assets in the stack. You can build customized presets for any image file to tailor your assets as per your business requirements.

Image Preset Builder allows you to use varied preset versions of assets across your entries. With the help of this app, you can add image presets to fields such as Custom Field extensions or the JSON Rich Text Editor.

Developer Documentation:

To render the image presets built from the Image Preset Builder app in your project using the App Utils functions, please refer to the Render Images using Image Preset Builder section of the installation guide.

", + "dynamic_links": [], + "feature_types": [ + { + "feature_type": [ + { + "uid": "blt7cf992884f5eca3d", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "csa7d3f06c094c7174" + }, + "description": "Create presets for your image files using the Image Preset Builder extension app." + }, + { + "feature_type": [ + { + "uid": "bltf9b70a5fd3c08c36", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "csdf8c11290f11ee6b" + }, + "description": "Add customized presets to your JSON Rich Text Editor fields using the Preset Builder JSON RTE Plugin" + }, + { + "feature_type": [ + { + "uid": "blt6b35fa46f253a6bd", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "cs33da0ec99b1b5425" + }, + "description": "Add optimized image presets to Custom fields that accept assets as input." + } + ], + "icon": { + "_version": 1, + "is_dir": false, + "uid": "bltfe85fe6163c6371e", + "ACL": {}, + "content_type": "image/svg+xml", + "created_at": "2022-03-29T11:09:04.361Z", + "created_by": "blt89b5b90cb9e7c645", + "file_size": "3155", + "filename": "preset.svg", + "parent_uid": "blt2eb26851df9c0e2e", + "tags": [], + "title": "preset.svg", + "updated_at": "2022-03-29T11:09:04.361Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltfe85fe6163c6371e/6242e8d081b559640a7f7475/preset.svg" + }, + "installation_url": "#!/apps/621640ff4671880018efec74/install", + "links": { + "source_code": "", + "documentation": "", + "others": [], + "end_user_license_agreement": "" + }, + "screenshots": [ + { + "_version": 1, + "is_dir": false, + "uid": "bltfd4c1b10fbadf2c8", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:09:03.273Z", + "created_by": "blt89b5b90cb9e7c645", + "description": "", + "file_size": "175853", + "filename": "new_preset.png", + "parent_uid": "bltc827a00118a50f18", + "tags": [], + "title": "new_preset.png", + "updated_at": "2022-03-29T11:09:03.273Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltfd4c1b10fbadf2c8/6242e8cfed063b096d929843/new_preset.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt9becbd9461e8f109", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:09:02.272Z", + "created_by": "blt89b5b90cb9e7c645", + "description": "", + "file_size": "435362", + "filename": "presets.png", + "parent_uid": "bltc827a00118a50f18", + "tags": [], + "title": "presets.png", + "updated_at": "2022-03-29T11:09:02.272Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt9becbd9461e8f109/6242e8cea7f9130fc619972c/presets.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "bltf66a57fbcf9adbb5", + "ACL": {}, + "content_type": "image/png", + "created_at": "2022-03-29T11:09:03.498Z", + "created_by": "blt89b5b90cb9e7c645", + "description": "", + "file_size": "502254", + "filename": "formatting_options.png", + "parent_uid": "bltc827a00118a50f18", + "tags": [], + "title": "formatting_options.png", + "updated_at": "2022-03-29T11:09:03.498Z", + "updated_by": "blt89b5b90cb9e7c645", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltf66a57fbcf9adbb5/6242e8cf077d7c10fb7aba67/formatting_options.png" + } + ], + "seo": { + "canonical_url": "", + "description": "", + "image": null, + "robots": "", + "title": "" + }, + "static_links": [ + { + "type": "End-User License Agreement", + "url": "https://www.contentstack.com/legal/eula-for-contentstack-proprietary-marketplace-apps/", + "_metadata": { + "uid": "cs6277649029047c65" + } + }, + { + "type": "Documentation", + "url": "https://www.contentstack.com/docs/developers/marketplace-apps/image-preset-builder/", + "_metadata": { + "uid": "cs01d17b2955b24d9d" + } + } + ], + "summary": "Allows you to transform and enhance image files by creating presets to suit your business needs.", + "tags": [], + "title": "Image Preset Builder", + "updated_at": "2025-01-24T11:20:29.187Z", + "updated_by": "blt05ac693470ad5ff7", + "url": "/marketplace/image-preset-builder", + "use_cases": "

Image optimization: Easily edit images and presets to increase productivity. Businesses can also use this to quickly customize and brand their products.

Media functionality: You can use the Image Preset Builder app in conjunction with the API functionality to apply campaign branding to images without having to do any frontend development.

Consistent solution for your work: You can maintain consistency across all your image files by using presets, thereby defining your brand image.

", + "publish_details": { + "time": "2025-01-28T10:55:14.129Z", + "user": "blt05ac693470ad5ff7", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + } + }, + { + "uid": "blt4bc1c01faaa9515b", + "_version": 4, + "locale": "en-us", + "ACL": {}, + "_in_progress": false, + "app_type": [ + { + "uid": "blte9d290b2e9e0931e", + "_content_type_uid": "app_types" + } + ], + "app_uid": "6762b269e4edd60012729b3f", + "built_by": [ + { + "uid": "blt5dfea44cb76ed4ca", + "_content_type_uid": "organizations" + } + ], + "category": [ + { + "uid": "blt6abfa301df06bf93", + "_content_type_uid": "categories" + } + ], + "coming_soon": false, + "created_at": "2025-01-29T13:46:00.929Z", + "created_by": "blt78cea3a89ceb6137", + "description": "

Pull assets like images, videos, and product sheets straight from Kontainer.

The app allows download templates, which reformat images as they are fetched, and naming templates that can automize SEO optimization. Find out more here: https://helpdesk.kontainer.com/article/a-guide-to-download-templates/

", + "dynamic_links": [ + { + "title": "Support", + "href": "mailto:mail@kontainer.com" + } + ], + "feature_types": [ + { + "feature_type": [ + { + "uid": "blt6b35fa46f253a6bd", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "cs0796754e1f71a5bb" + }, + "description": "Custom Field allows you to select multiple assets from your Digizuite account and add them to your entry." + } + ], + "icon": { + "uid": "bltd8d9475a7210809e", + "_version": 1, + "title": "Kontainer_logo.png", + "parent_uid": "blt2eb26851df9c0e2e", + "created_by": "blt78cea3a89ceb6137", + "updated_by": "blt78cea3a89ceb6137", + "created_at": "2025-01-29T13:39:48.911Z", + "updated_at": "2025-01-29T13:39:48.911Z", + "content_type": "image/png", + "file_size": "9031", + "filename": "Kontainer_logo.png", + "ACL": {}, + "is_dir": false, + "tags": [], + "publish_details": { + "time": "2025-01-30T07:20:45.856Z", + "user": "blt78cea3a89ceb6137", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltd8d9475a7210809e/679a2fa4137f136ac25f8570/Kontainer_logo.png" + }, + "installation_url": "#!/apps/6762b269e4edd60012729b3f/install", + "links": { + "source_code": "", + "documentation": "https://helpdesk.kontainer.com", + "end_user_license_agreement": "https://kontainer.com/terms-and-conditions", + "others": [] + }, + "screenshots": [ + { + "uid": "blt98932a04b31784e7", + "_version": 1, + "title": "config-1.png", + "parent_uid": "blt18080c44b39acda5", + "created_by": "blt78cea3a89ceb6137", + "updated_by": "blt78cea3a89ceb6137", + "created_at": "2025-01-29T13:44:44.442Z", + "updated_at": "2025-01-29T13:44:44.442Z", + "content_type": "image/png", + "file_size": "285152", + "filename": "config-1.png", + "ACL": {}, + "is_dir": false, + "tags": [], + "publish_details": { + "time": "2025-01-30T07:20:45.871Z", + "user": "blt78cea3a89ceb6137", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt98932a04b31784e7/679a30cca44add02754fe973/config-1.png" + }, + { + "uid": "bltdfe09822f7b23800", + "_version": 1, + "title": "contenttype-2.png", + "parent_uid": "blt18080c44b39acda5", + "created_by": "blt78cea3a89ceb6137", + "updated_by": "blt78cea3a89ceb6137", + "created_at": "2025-01-29T13:44:44.426Z", + "updated_at": "2025-01-29T13:44:44.426Z", + "content_type": "image/png", + "file_size": "150034", + "filename": "contenttype-2.png", + "ACL": {}, + "is_dir": false, + "tags": [], + "publish_details": { + "time": "2025-01-30T07:20:45.840Z", + "user": "blt78cea3a89ceb6137", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltdfe09822f7b23800/679a30ccc3e1cba508da025f/contenttype-2.png" + }, + { + "uid": "blt891a39db9979903a", + "_version": 1, + "title": "custom-field-3.png", + "parent_uid": "blt18080c44b39acda5", + "created_by": "blt78cea3a89ceb6137", + "updated_by": "blt78cea3a89ceb6137", + "created_at": "2025-01-29T13:44:45.076Z", + "updated_at": "2025-01-29T13:44:45.076Z", + "content_type": "image/png", + "file_size": "1375271", + "filename": "custom-field-3.png", + "ACL": {}, + "is_dir": false, + "tags": [], + "publish_details": { + "time": "2025-01-30T07:20:45.885Z", + "user": "blt78cea3a89ceb6137", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt891a39db9979903a/679a30cdae49134467f88c26/custom-field-3.png" + } + ], + "seo": { + "title": "", + "description": "", + "image": null, + "robots": "", + "canonical_url": "" + }, + "static_links": [ + { + "type": "End-User License Agreement", + "url": "https://kontainer.com/terms-and-conditions", + "_metadata": { + "uid": "csbc44d7d224052d67" + } + }, + { + "type": "Documentation", + "url": "https://helpdesk.kontainer.com", + "_metadata": { + "uid": "cs283de2028059c0e1" + } + } + ], + "summary": "Pull assets like images, videos, and product sheets straight from Kontainer.", + "tags": [], + "title": "Kontainer", + "updated_at": "2025-02-07T19:39:19.147Z", + "updated_by": "blt78cea3a89ceb6137", + "url": "/marketplace/kontainer", + "use_cases": "

Connects assets from Kontainer Dam to ContentStack custom fields. Can be used all places there assets is needed

", + "publish_details": { + "time": "2025-02-07T19:39:26.552Z", + "user": "blt78cea3a89ceb6137", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + } + }, + { + "uid": "bltf3ea0046122ec160", + "_version": 8, + "locale": "en-us", + "ACL": {}, + "_in_progress": false, + "app_type": [ + { + "uid": "blte9d290b2e9e0931e", + "_content_type_uid": "app_types" + } + ], + "app_uid": "6422122285be5a001944ad6d", + "built_by": [ + { + "uid": "blt31680c4e3b524fd2", + "_content_type_uid": "organizations" + } + ], + "category": [ + { + "uid": "blt6abfa301df06bf93", + "_content_type_uid": "categories" + } + ], + "coming_soon": false, + "created_at": "2023-05-05T07:59:03.313Z", + "created_by": "bltf389fd468f32cd6e", + "description": "

A minimal integration of the Ocavu 3d platform. Entries in Content Stack can be connected to an asset in Ocavu by inputting various fields. If the app is configured correctly and a valid asset key is entered into the Ocavu Asset Key field the model will be visible from the Entry edit view. This helps users stay within the Content Stack ecosystem if all they want to do is browse or preview their 3d models.

", + "dynamic_links": [ + { + "title": "Privacy policy", + "href": "https://www.ocavu.com/privacy-policy/" + }, + { + "title": "Support", + "href": "mailto:support@ocavu.com" + } + ], + "feature_types": [ + { + "feature_type": [ + { + "uid": "blt6b35fa46f253a6bd", + "_content_type_uid": "app_features" + } + ], + "_metadata": { + "uid": "csf814c1f317212546" + }, + "description": "Custom field enables the viewing of Ocavu-hosted assets. embeds a basic 3D viewer for a 3D model using the models." + } + ], + "icon": { + "_version": 1, + "is_dir": false, + "uid": "bltd6915163f1454188", + "ACL": {}, + "content_type": "image/svg+xml", + "created_at": "2023-05-05T09:24:59.115Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "24491", + "filename": "Ocavu.svg", + "parent_uid": "blt2eb26851df9c0e2e", + "tags": [], + "title": "Ocavu.svg", + "updated_at": "2023-05-05T09:24:59.115Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltd6915163f1454188/6454cb6b2215a3bedde70c78/Ocavu.svg" + }, + "installation_url": "/#!/apps/6422122285be5a001944ad6d/install", + "links": { + "source_code": "", + "documentation": "", + "end_user_license_agreement": "", + "others": [] + }, + "screenshots": [ + { + "_version": 1, + "is_dir": false, + "uid": "bltb2fa3286f0ef991a", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-05-05T09:17:05.871Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "503999", + "filename": "ConfigScreen-1.png", + "parent_uid": "bltb6c089404f41984f", + "tags": [], + "title": "ConfigScreen-1.png", + "updated_at": "2023-05-05T09:17:05.871Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/bltb2fa3286f0ef991a/6454c99197a1175f49721c63/ConfigScreen-1.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt1b3fbca5df1f4d86", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-05-05T09:17:06.237Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "217453", + "filename": "CustomField-2.png", + "parent_uid": "bltb6c089404f41984f", + "tags": [], + "title": "CustomField-2.png", + "updated_at": "2023-05-05T09:17:06.237Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt1b3fbca5df1f4d86/6454c99291026c2e20d77bb3/CustomField-2.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt9934f583008e9bb6", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-05-05T09:17:05.680Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "267212", + "filename": "AssetSelect-3.png", + "parent_uid": "bltb6c089404f41984f", + "tags": [], + "title": "AssetSelect-3.png", + "updated_at": "2023-05-05T09:17:05.680Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt9934f583008e9bb6/6454c991e0636f608d89159a/AssetSelect-3.png" + }, + { + "_version": 1, + "is_dir": false, + "uid": "blt9b5c5739cdb6453e", + "ACL": {}, + "content_type": "image/png", + "created_at": "2023-05-05T09:17:05.848Z", + "created_by": "bltf389fd468f32cd6e", + "file_size": "544019", + "filename": "AssetViewer-4.png", + "parent_uid": "bltb6c089404f41984f", + "tags": [], + "title": "AssetViewer-4.png", + "updated_at": "2023-05-05T09:17:05.848Z", + "updated_by": "bltf389fd468f32cd6e", + "publish_details": { + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us", + "time": "2025-01-28T10:07:08.871Z", + "user": "blt05ac693470ad5ff7" + }, + "url": "https://eu-images.contentstack.com/v3/assets/blt8f94ebff857fe1ae/blt9b5c5739cdb6453e/6454c99129a8592bf4189a9a/AssetViewer-4.png" + } + ], + "seo": { + "canonical_url": "", + "description": "", + "image": null, + "robots": "", + "title": "" + }, + "static_links": [ + { + "type": "Documentation", + "url": "https://api.seekxr.com/docs/cs-ocavu-viewer/", + "_metadata": { + "uid": "cse3e92eac0906a9a2" + } + }, + { + "type": "End-User License Agreement", + "url": "https://www.contentstack.com/legal/eula-for-contentstack-proprietary-marketplace-apps/", + "_metadata": { + "uid": "cs6b5ada60b8f6d4d6" + } + } + ], + "summary": "Allows viewing assets hosted in Ocavu. Embeds a simple 3d viewer of a 3d model using the Ocavu asset key of that model.", + "tags": [], + "title": "Ocavu Asset Viewer", + "updated_at": "2025-01-31T05:54:41.867Z", + "updated_by": "blt05ac693470ad5ff7", + "url": "/marketplace/ocavu", + "use_cases": "

The app provides a way to view 3d models hosted in Ocavu without leaving Content Stack.

", + "publish_details": { + "time": "2025-01-31T05:54:48.273Z", + "user": "blt05ac693470ad5ff7", + "environment": "blt0b5712e5b06ac8b6", + "locale": "en-us" + } + } + ], + "count": 11 +} \ No newline at end of file diff --git a/api/src/constants/index.ts b/api/src/constants/index.ts index 11b63147d..8e311ed26 100644 --- a/api/src/constants/index.ts +++ b/api/src/constants/index.ts @@ -1,4 +1,11 @@ export const CS_REGIONS = ["NA", "EU", "AZURE_NA", "AZURE_EU", "GCP_NA"]; +export const DEVURLS: any = { + 'NA': 'developerhub-api.contentstack.com', + 'EU': 'eu-developerhub-api.contentstack.com', + 'AZURE_NA': 'azure-na-developerhub-api.contentstack.com', + 'AZURE_EU': 'azure-eu-developerhub-api.contentstack.com', + 'GCP_NA': 'gcp-na-developerhub-api.contentstack.com', +} export const CMS = { CONTENTFUL: "contentful", SITECORE_V8: "sitecore v8", @@ -176,6 +183,13 @@ export const LOCALE_MAPPER: any = { } export const CHUNK_SIZE = 1048576; +export const LIST_EXTENSION_UID = 'bltc44e51cc9f4b0d80'; + +export const KEYTOREMOVE = [ + "update", "fetch", "delete", "oauth", "hosting", "install", "reinstall", + "upgrade", "getRequests", "authorize", "authorization", "listInstallations" +] + export const MIGRATION_DATA_CONFIG = { DATA: "./cmsMigrationData", @@ -196,10 +210,15 @@ export const MIGRATION_DATA_CONFIG = { ENVIRONMENTS_FILE_NAME: "environments.json", CONTENT_TYPES_DIR_NAME: "content_types", + EXTENSIONS_MAPPER_DIR_NAME: "extension-mapper.json", + CUSTOM_MAPPER_FILE_NAME: "custmon-mapper.json", CONTENT_TYPES_FILE_NAME: "contenttype.json", CONTENT_TYPES_MASTER_FILE: "contenttypes.json", CONTENT_TYPES_SCHEMA_FILE: "schema.json", - + MARKETPLACE_APPS_DIR_NAME: "marketplace_apps", + MARKETPLACE_APPS_FILE_NAME: "marketplace_apps.json", + EXTENSION_APPS_DIR_NAME: "extensions", + EXTENSION_APPS_FILE_NAME: "extensions.json", REFERENCES_DIR_NAME: "reference", REFERENCES_FILE_NAME: "reference.json", @@ -217,15 +236,15 @@ export const MIGRATION_DATA_CONFIG = { ENTRIES_DIR_NAME: "entries", ENTRIES_MASTER_FILE: "index.json", - AUTHORS_DIR_NAME : "authors", - AUTHORS_FILE_NAME : "en-us.json", - AUTHORS_MASTER_FILE : "authors.json", + AUTHORS_DIR_NAME: "authors", + AUTHORS_FILE_NAME: "en-us.json", + AUTHORS_MASTER_FILE: "authors.json", CATEGORIES_DIR_NAME: "categories", CATEGORIES_FILE_NAME: "en-us.json", CATEGORIES_MASTER_FILE: "categories.json", - TAG_DIR_NAME: "tags", + TAG_DIR_NAME: "tag", TAG_FILE_NAME: "en-us.json", TAG_MASTER_FILE: "tag.json", diff --git a/api/src/services/contentMapper.service.ts b/api/src/services/contentMapper.service.ts index de08fbeb0..2af4c371d 100644 --- a/api/src/services/contentMapper.service.ts +++ b/api/src/services/contentMapper.service.ts @@ -631,7 +631,7 @@ const resetToInitialMapping = async (req: Request) => { [ NEW_PROJECT_STATUS[0], NEW_PROJECT_STATUS[5], - NEW_PROJECT_STATUS[4], + //NEW_PROJECT_STATUS[4], ].includes(project.status) || project.current_step < STEPPER_STEPS.CONTENT_MAPPING ) { diff --git a/api/src/services/contentful.service.ts b/api/src/services/contentful.service.ts index f73aaac07..a56c12fad 100644 --- a/api/src/services/contentful.service.ts +++ b/api/src/services/contentful.service.ts @@ -6,15 +6,17 @@ import _ from "lodash"; import axios from "axios"; import jsonpath from "jsonpath"; import pLimit from 'p-limit'; +import { JSDOM } from "jsdom"; +import { jsonToHtml, jsonToMarkdown, htmlToJson } from '@contentstack/json-rte-serializer'; -import {CHUNK_SIZE, MIGRATION_DATA_CONFIG } from "../constants/index.js"; +import { CHUNK_SIZE, MIGRATION_DATA_CONFIG } from "../constants/index.js"; import { Locale } from "../models/types.js"; import jsonRTE from "./contentful/jsonRTE.js"; import { getAllLocales, getLogMessage } from "../utils/index.js"; import customLogger from "../utils/custom-logger.utils.js"; -const { +const { DATA, // DIR LOCALE_DIR_NAME, @@ -41,7 +43,7 @@ const { WEBHOOKS_FILE_NAME, RTE_REFERENCES_FILE_NAME, GLOBAL_FIELDS_FILE_NAME, - + } = MIGRATION_DATA_CONFIG; interface AssetMetaData { @@ -84,6 +86,45 @@ function makeChunks(assetData: any) { return chunks; } +const transformCloudinaryObject = (input: any) => { + const result: any = []; + for (const metaData of input ?? []) { + result?.push({ + public_id: metaData.public_id, + resource_type: metaData.resource_type, + type: metaData.type, + format: metaData.format, + version: metaData.version, + url: metaData.original_url, + secure_url: metaData.original_secure_url, + width: metaData.width, + height: metaData.height, + bytes: metaData.bytes, + duration: metaData.duration, + tags: metaData.tags, + metadata: metaData.metadata, + created_at: metaData.created_at, + access_mode: "public", + access_control: [], + created_by: { + type: "", + id: "" + }, + uploaded_by: { + type: "", + id: "" + }, + folder_id: "", + id: "", + folder: "", + cs_metadata: { + config_label: "config" + } + }); + } + return result; +} + /** * Reads a file from the given file path and returns its JSON content. * @param {string} filePath - The path to the file to read. @@ -92,7 +133,12 @@ function makeChunks(assetData: any) { * @throws {Error} - If there is an error reading the file. */ async function readFile(filePath: string, fileName: string) { - return JSON.parse(await fs.promises.readFile(path.join(filePath, fileName), "utf8")); + try { + const data = await fs.promises.readFile(path.join(filePath, fileName), "utf8"); + return JSON.parse(data); + } catch (err) { + return undefined; + } } /** @@ -134,36 +180,145 @@ async function writeFile(dirPath: string, filename: string, data: any) { console.error('Error writing ${dirPath}/${filename}::', err); } } +const damApp = (type: string, data: any) => { + switch (type) { + case 'zjcnWgBknf9zB7IM9HZjE': + return transformCloudinaryObject(data); + } +} const processField = ( lang_value: any, entryId: any, assetId: any, lang: any, - destination_stack_id:string + destination_stack_id: string, + fieldData: any ) => { - // If lang_value is not an object - if (typeof lang_value !== "object") { - return typeof lang_value === "number" ? lang_value - : cleanBrackets(lang_value); - } + switch (fieldData?.contentstackFieldType) { + case 'multi_line_text': + case 'single_line_text': + case 'text': { + return lang_value; + } + + case 'json': { + return processRTEOrNestedObject(lang_value, lang, destination_stack_id); + } - // Check if it's a location (lat/lon) - if (lang_value.lat) return lang_value; + case 'dropdown': + case 'radio': { + const isPresent = fieldData?.advanced?.options?.find((option: any) => lang_value === option?.value); + return isPresent?.value ?? fieldData?.advanced?.default_value; + } - // Check if it contains sys field for Entry or Asset - if (lang_value.sys) { - const { linkType, id } = lang_value.sys; + case 'file': { + if (fieldData?.advanced?.multiple) { + const assetsData: any = []; + for (const asset of lang_value) { + if (asset?.sys?.id in assetId) { + assetsData?.push(assetId?.[asset?.sys?.id]) + } + } + return assetsData; + } else { + const id = lang_value?.sys?.id; + if (id in assetId) { + return assetId?.[id] + } + return null; + } + } - if (linkType === "Entry" && id in entryId) return [entryId?.[id]]; - if (linkType === "Asset" && id in assetId) return assetId?.[id]; - } + case 'reference': { + if (Array?.isArray?.(lang_value) && fieldData?.advanced?.multiple) { + const refs = []; + for (const entry of lang_value) { + const id = entry?.sys?.id; + if (id in entryId) { + refs?.push(entryId?.[id]); + } + } + return refs; + } + const id = lang_value?.sys?.id; + if (id in entryId) return [[entryId?.[id]]]; + return null; + } - // Handle arrays and nested objects - if (Array.isArray(lang_value)) { - return processArrayFields(lang_value, entryId, assetId); - } else { - return processRTEOrNestedObject(lang_value, lang, destination_stack_id); + case 'app': { + return damApp(fieldData?.otherCmsType, lang_value) + } + + case 'boolean': { + return lang_value; + } + + case 'number': { + if (typeof lang_value === 'string') { + return parseInt?.(lang_value) + } + return lang_value; + } + + case 'isodate': { + return lang_value; + } + + case 'checkbox': { + return lang_value; + } + + case 'html': { + const jsonValue = processRTEOrNestedObject(lang_value, lang, destination_stack_id); + return jsonToHtml( + jsonValue, + { + customElementTypes: { + "social-embed": (attrs, child, jsonBlock) => { + return `${child}`; + }, + }, + customTextWrapper: { + "color": (child, value) => { + return `${child}`; + }, + }, + } + ) ?? '

'; + } + + case 'markdown': { + const jsonValue = processRTEOrNestedObject(lang_value, lang, destination_stack_id); + return jsonToMarkdown(jsonValue); + } + + case 'extension': { + if (['listInput', 'tagEditor']?.includes(fieldData?.otherCmsType)) { + if (Array.isArray(lang_value) && lang_value?.length) { + return { value: lang_value?.map((element: any) => ({ key: element, value: element })) } + } + return { value: [] }; + } + break; + } + + case 'group': { + if (lang_value.lat) return lang_value; + break; + } + + default: { + if (Array.isArray(lang_value)) { + return processArrayFields(lang_value, entryId, assetId); + } + if (typeof lang_value !== "object") { + return typeof lang_value === "number" ? lang_value + : cleanBrackets(lang_value); + } + console.info("Missing ===>", fieldData?.contentstackFieldType) + break; + } } }; @@ -193,17 +348,18 @@ const processArrayFields = (array: any, entryId: any, assetId: any) => { .replace(/,{}/g, "") .replace(/,{},/g, "") .replace(/{}/g, ""); - - const result = JSON.parse(cleanedArray); + const result = typeof cleanedArray === 'string' && JSON.parse(cleanedArray); return result.length > 0 ? result : undefined; }; // Helper function to process Rich Text Editor (RTE) or nested object -const processRTEOrNestedObject = (lang_value: any, lang: any, destination_stack_id:string) => { - if (lang_value.data) { +const processRTEOrNestedObject = (lang_value: any, lang: any, destination_stack_id: string) => { + if (lang_value?.data) { return jsonRTE(lang_value, lang.toLowerCase(), destination_stack_id); } else { - return lang_value; + const dom = new JSDOM(lang_value); + const htmlDoc = dom.window.document.querySelector("body"); + return htmlToJson(htmlDoc); } }; @@ -260,8 +416,8 @@ const saveAsset = async ( failedJSON: any, assetData: any, metadata: AssetMetaData[], - projectId:string, - destination_stack_id:string, + projectId: string, + destination_stack_id: string, retryCount = 0 ): Promise => { try { @@ -269,8 +425,8 @@ const saveAsset = async ( const publishDetails: { environment: any; version: number; locale: any }[] = []; const assetsSave = path.join(DATA, destination_stack_id, ASSETS_DIR_NAME); - const environmentsId = await readFile(path.join(DATA, destination_stack_id,ENVIRONMENTS_DIR_NAME), ENVIRONMENTS_FILE_NAME); - const localeId = await readFile(path.join(DATA, destination_stack_id,LOCALE_DIR_NAME), LOCALE_CF_LANGUAGE); + const environmentsId = await readFile(path.join(DATA, destination_stack_id, ENVIRONMENTS_DIR_NAME), ENVIRONMENTS_FILE_NAME); + const localeId = await readFile(path.join(DATA, destination_stack_id, LOCALE_DIR_NAME), LOCALE_CF_LANGUAGE); if (assets.fields.file && assets.fields.title) { Object.values(environmentsId).forEach((env: any) => { @@ -322,10 +478,10 @@ const saveAsset = async ( Object.values(assets?.fields?.file)[0] as { contentType: string } ).contentType, file_size: `${( - Object.values(assets?.fields?.file)[0] as { - details: { size: string }; - } - )?.details.size + Object.values(assets?.fields?.file)[0] as { + details: { size: string }; + } + )?.details.size }`, tag: assets?.metadata?.tags, filename: fileName, @@ -356,15 +512,15 @@ const saveAsset = async ( name: assetTitle, url: fileUrl, file_size: `${( - Object.values(assets?.fields?.file)[0] as { - details: { size: string }; - } - ).details.size + Object.values(assets?.fields?.file)[0] as { + details: { size: string }; + } + ).details.size }`, reason_for_error: err?.message, }; } else { - return await saveAsset(assets, failedJSON, assetData,metadata, projectId, destination_stack_id, 1); + return await saveAsset(assets, failedJSON, assetData, metadata, projectId, destination_stack_id, 1); } } } @@ -374,40 +530,43 @@ const saveAsset = async ( } }; - /** - * Creates and processes assets from a given package file, saving them to the destination stack directory. - * - * @param {any} packagePath - The path to the package file containing asset data. - * @param {string} destination_stack_id - The ID of the destination stack where assets will be saved. - * @param {string} projectId - The ID of the current project for logging purposes. - * @returns {Promise} Resolves when all assets have been successfully created or errors have been logged. - * - * @description - * This function performs the following tasks: - * 1. Reads and parses the package file containing asset data. - * 2. Creates and processes each asset using the `saveAsset` function, handling failures in `failedJSON`. - * 3. Saves the processed asset data, metadata, and chunked references to the destination directory. - * 4. Generates and writes the following files: - * - Schema file with complete asset data. - * - Chunked files for asset references. - * - Metadata file containing additional information about the assets. - * - A file to track failed assets, if any. - * 5. Logs appropriate messages if no assets are found or if an error occurs during processing. - * - * @throws Will log errors encountered during file reading, writing, or asset processing. - */ -const createAssets = async (packagePath: any, destination_stack_id:string, projectId:string,) => { - const srcFunc = 'createAssets'; +/** +* Creates and processes assets from a given package file, saving them to the destination stack directory. +* +* @param {any} packagePath - The path to the package file containing asset data. +* @param {string} destination_stack_id - The ID of the destination stack where assets will be saved. +* @param {string} projectId - The ID of the current project for logging purposes. +* @returns {Promise} Resolves when all assets have been successfully created or errors have been logged. +* +* @description +* This function performs the following tasks: +* 1. Reads and parses the package file containing asset data. +* 2. Creates and processes each asset using the `saveAsset` function, handling failures in `failedJSON`. +* 3. Saves the processed asset data, metadata, and chunked references to the destination directory. +* 4. Generates and writes the following files: +* - Schema file with complete asset data. +* - Chunked files for asset references. +* - Metadata file containing additional information about the assets. +* - A file to track failed assets, if any. +* 5. Logs appropriate messages if no assets are found or if an error occurs during processing. +* +* @throws Will log errors encountered during file reading, writing, or asset processing. +*/ +const createAssets = async (packagePath: any, destination_stack_id: string, projectId: string, isTest = false) => { + const srcFunc = 'createAssets'; try { - const assetsSave = path.join(DATA, destination_stack_id, ASSETS_DIR_NAME); - const data = await fs.promises.readFile(packagePath, "utf8"); + const assetsSave = path?.join?.(DATA, destination_stack_id, ASSETS_DIR_NAME); + const data = await fs?.promises?.readFile?.(packagePath, "utf8"); const failedJSON: any = {}; const assetData: any = {}; const metadata: AssetMetaData[] = []; const fileMeta = { "1": ASSETS_SCHEMA_FILE }; - const assets = JSON.parse(data)?.assets; + let assets = JSON.parse(data)?.assets; if (assets && assets.length > 0) { + if (isTest) { + assets = assets?.slice(0, 10); + } const limit = pLimit(10); // Limit concurrent operations to 10 const tasks = assets.map((asset: any) => limit(() => saveAsset(asset, failedJSON, assetData, metadata, projectId, destination_stack_id, 0)) @@ -418,7 +577,7 @@ const createAssets = async (packagePath: any, destination_stack_id:string, proje await writeOneFile(path.join(assetsSave, ASSETS_SCHEMA_FILE), assetData); // This code is intentionally commented out - + // const chunks: { [key: string]: any } = makeChunks(assetData); // const refs: any = {}; @@ -452,32 +611,32 @@ const createAssets = async (packagePath: any, destination_stack_id:string, proje } }; - /** - * Creates environment configurations from a given package file and saves them to the destination stack directory. - * - * @param {any} packagePath - The path to the package file containing environment data. - * @param {string} destination_stack_id - The ID of the destination stack where environments will be saved. - * @param {string} projectId - The ID of the current project for logging purposes. - * @returns {Promise} Resolves when the environments are successfully created or errors have been logged. - * - * @description - * This function performs the following tasks: - * 1. Reads and parses the package file to extract environment data (`editorInterfaces`). - * 2. Retrieves the master locale for the destination stack from the saved locale data. - * 3. Processes and creates unique environment configurations by: - * - Extracting titles and names from the parsed data. - * - Ensuring each environment has a unique name. - * - Associating each environment with the master locale. - * 4. Writes the consolidated environment configurations to a JSON file in the destination stack directory. - * 5. Logs a message if no environments are found in the package file. - * 6. Handles errors gracefully by logging them with relevant details. - * - * @throws Will log errors encountered during file reading, writing, or processing of environments. - */ -const createEnvironment = async (packagePath: any, destination_stack_id:string, projectId:string,) => { - const srcFunc = 'createEnvironment'; +/** +* Creates environment configurations from a given package file and saves them to the destination stack directory. +* +* @param {any} packagePath - The path to the package file containing environment data. +* @param {string} destination_stack_id - The ID of the destination stack where environments will be saved. +* @param {string} projectId - The ID of the current project for logging purposes. +* @returns {Promise} Resolves when the environments are successfully created or errors have been logged. +* +* @description +* This function performs the following tasks: +* 1. Reads and parses the package file to extract environment data (`editorInterfaces`). +* 2. Retrieves the master locale for the destination stack from the saved locale data. +* 3. Processes and creates unique environment configurations by: +* - Extracting titles and names from the parsed data. +* - Ensuring each environment has a unique name. +* - Associating each environment with the master locale. +* 4. Writes the consolidated environment configurations to a JSON file in the destination stack directory. +* 5. Logs a message if no environments are found in the package file. +* 6. Handles errors gracefully by logging them with relevant details. +* +* @throws Will log errors encountered during file reading, writing, or processing of environments. +*/ +const createEnvironment = async (packagePath: any, destination_stack_id: string, projectId: string,) => { + const srcFunc = 'createEnvironment'; try { - const localeSave = path.join(DATA, destination_stack_id,LOCALE_DIR_NAME); + const localeSave = path.join(DATA, destination_stack_id, LOCALE_DIR_NAME); const environmentSave = path.join(DATA, destination_stack_id, ENVIRONMENTS_DIR_NAME); const data = await fs.promises.readFile(packagePath, "utf8"); const environments = JSON.parse(data)?.editorInterfaces; @@ -552,21 +711,20 @@ const createEnvironment = async (packagePath: any, destination_stack_id:string, * * @throws Will log errors encountered during file reading, processing, or writing of entries. */ -const createEntry = async (packagePath: any, destination_stack_id:string, projectId:string) => { - const srcFunc = 'createEntry'; +const createEntry = async (packagePath: any, destination_stack_id: string, projectId: string, contentTypes: any, mapperKeys: any): Promise => { + const srcFunc = 'createEntry'; try { const entriesSave = path.join(DATA, destination_stack_id, ENTRIES_DIR_NAME); const assetsSave = path.join(DATA, destination_stack_id, ASSETS_DIR_NAME); const environmentSave = path.join(DATA, destination_stack_id, ENVIRONMENTS_DIR_NAME); const data = await fs.promises.readFile(packagePath, "utf8"); const entries = JSON.parse(data)?.entries; - const content = JSON.parse(data)?.contentTypes + const content = JSON.parse(data)?.contentTypes; if (entries && entries.length > 0) { - const assetId = await readFile(assetsSave, ASSETS_SCHEMA_FILE) - const entryId = await readFile(path.join(DATA, destination_stack_id, REFERENCES_DIR_NAME), REFERENCES_FILE_NAME) - const environmentsId = await readFile(environmentSave, ENVIRONMENTS_FILE_NAME) - + const assetId = await readFile(assetsSave, ASSETS_SCHEMA_FILE) ?? []; + const entryId = await readFile(path.join(DATA, destination_stack_id, REFERENCES_DIR_NAME), REFERENCES_FILE_NAME); + const environmentsId = await readFile(environmentSave, ENVIRONMENTS_FILE_NAME); const displayField: { [key: string]: any } = {} content.map((item: any) => { displayField[item.name.toLowerCase().replace(/[^a-z0-9]+/g, "_")] = @@ -593,29 +751,26 @@ const createEntry = async (packagePath: any, destination_stack_id:string, projec entryData[name] ??= {}; Object.entries(fields).forEach(([key, value]) => { + const currentCT = contentTypes?.find((ct: any) => ct?.otherCmsUid === name); const locales: string[] = []; - Object.entries(value as object).forEach(([lang, langValue]) => { entryData[name][lang] ??= {}; entryData[name][lang][id] ??= {}; - locales.push(lang); - - const newId = `${key}`.replace(/[^a-zA-Z0-9]+/g, "_"); - + const fieldData = currentCT?.fieldMapping?.find((item: any) => key === item?.uid); + const newId = fieldData?.contentstackFieldUid ?? `${key}`.replace(/[^a-zA-Z0-9]+/g, "_"); entryData[name][lang][id][newId] = processField( langValue, entryId, assetId, lang, - destination_stack_id + destination_stack_id, + fieldData ); }); - const pathName = getDisplayName(name, displayField); - locales.forEach((locale) => { - const publishDetails = Object.values(environmentsId) + const publishDetails = Object?.values?.(environmentsId) .filter((env: any) => env?.name === environment_id) ?.map((env: any) => ({ environment: env?.uid, @@ -625,15 +780,15 @@ const createEntry = async (packagePath: any, destination_stack_id:string, projec const title = entryData[name][locale][id][pathName] || ""; const urlTitle = title - .replace(/[^a-zA-Z0-9]+/g, "-") - .toLowerCase(); + ?.replace?.(/[^a-zA-Z0-9]+/g, "-") + ?.toLowerCase?.(); entryData[name][locale][id] = { - title: title.trim() === "" ? urlTitle || id : title, + ...entryData[name][locale][id], + title: title?.trim?.() === "" ? (urlTitle || id) : title, uid: id, - url: `/${name.toLowerCase()}/${urlTitle}`, - locale: locale.toLowerCase(), + url: `/${name?.toLowerCase?.()}/${urlTitle}`, + locale: locale?.toLowerCase?.(), publish_details: publishDetails, - ...entryData[name][locale][id], }; // Format object keys to snake_case Object.entries(entryData[name][locale][id]).forEach( @@ -655,15 +810,18 @@ const createEntry = async (packagePath: any, destination_stack_id:string, projec ); const writePromises = []; - for (const [key, values] of Object.entries(result)) { + for (const [newKey, values] of Object.entries(result)) { + const currentCT = contentTypes?.find((ct: any) => ct?.otherCmsUid === newKey); + const ctName = currentCT?.contentstackUid in mapperKeys ? + mapperKeys?.[currentCT?.contentstackUid] : (currentCT?.contentstackUid ?? newKey.replace(/([A-Z])/g, "_$1").toLowerCase()); for (const [localeKey, localeValues] of Object.entries( values as { [key: string]: any } )) { const chunks = await makeChunks(localeValues); - for (const [entryKey, entryValue] of Object.entries(localeValues)){ + for (const [entryKey, entryValue] of Object.entries(localeValues)) { const message = getLogMessage( srcFunc, - `Entry title "${(entryValue as { title: string })?.title}"(${key}) in the ${localeKey} locale has been successfully transformed.`, + `Entry title "${(entryValue as { title: string })?.title}"(${ctName}) in the ${localeKey} locale has been successfully transformed.`, {} ); await customLogger(projectId, destination_stack_id, "info", message); @@ -672,7 +830,7 @@ const createEntry = async (packagePath: any, destination_stack_id:string, projec let chunkIndex = 1; const filePath = path.join( entriesSave, - key.replace(/([A-Z])/g, "_$1").toLowerCase(), localeKey.toLowerCase() + ctName, localeKey.toLowerCase() ); for (const [chunkId, chunkData] of Object.entries(chunks)) { refs[chunkIndex++] = `${chunkId}-entries.json`; @@ -691,6 +849,7 @@ const createEntry = async (packagePath: any, destination_stack_id:string, projec await customLogger(projectId, destination_stack_id, 'info', message); } } catch (err) { + console.info("🚀 ~ createEntry ~ err:", err) const message = getLogMessage( srcFunc, `Error encountered while creating entries.`, @@ -725,7 +884,7 @@ const createEntry = async (packagePath: any, destination_stack_id:string, projec * * @throws Will log errors encountered during file reading, processing, or writing of locale configurations. */ -const createLocale = async (packagePath: string, destination_stack_id:string, projectId:string) => { +const createLocale = async (packagePath: string, destination_stack_id: string, projectId: string) => { const srcFunc = 'createLocale'; const localeSave = path.join(DATA, destination_stack_id, LOCALE_DIR_NAME); const globalFieldSave = path.join(DATA, destination_stack_id, GLOBAL_FIELDS_DIR_NAME); @@ -740,7 +899,7 @@ const createLocale = async (packagePath: string, destination_stack_id:string, pr const locales = JSON.parse(data)?.locales; const [err, localeCodes] = await getAllLocales(); - if(err){ + if (err) { const message = getLogMessage( srcFunc, `Error encountered while fetching locales list.`, @@ -800,33 +959,33 @@ const createLocale = async (packagePath: string, destination_stack_id:string, pr } }; - /** - * Processes and transforms webhook configurations from a given package file and saves them to the destination stack directory. - * - * @param {string} packagePath - The path to the package file containing webhook data. - * @param {string} destination_stack_id - The ID of the destination stack where webhooks will be saved. - * @param {string} projectId - The ID of the current project for logging purposes. - * @returns {Promise} Resolves when all webhooks have been successfully processed and saved, or errors have been logged. - * - * @description - * This function performs the following tasks: - * 1. Reads and parses the package file to extract webhook data. - * 2. Iterates through the webhooks, transforming their configurations: - * - Processes `topics` for webhook events and constructs appropriate channel topics. - * - Handles data transformation based on the type of webhook event (`contentType`, `entries`, `assets`, `releases`). - * - Filters out ignored events and applies custom transformations to topics. - * 3. Builds webhook objects with necessary attributes like `urlPath`, `channels`, `destinations`, etc. - * 4. Logs success messages for each webhook transformation. - * 5. Saves the processed webhooks to a JSON file in the destination stack directory. - * 6. Logs a message confirming the successful transformation of webhooks or logs errors encountered during processing. - * - * @throws Will log errors encountered during file reading, processing, or writing of webhook configurations. - * - * @example - * // Example usage - * await createWebhooks('/path/to/package.json', 'stack123', 'project456'); - */ -const createWebhooks = async (packagePath: string, destination_stack_id:string, projectId:string,) => { +/** +* Processes and transforms webhook configurations from a given package file and saves them to the destination stack directory. +* +* @param {string} packagePath - The path to the package file containing webhook data. +* @param {string} destination_stack_id - The ID of the destination stack where webhooks will be saved. +* @param {string} projectId - The ID of the current project for logging purposes. +* @returns {Promise} Resolves when all webhooks have been successfully processed and saved, or errors have been logged. +* +* @description +* This function performs the following tasks: +* 1. Reads and parses the package file to extract webhook data. +* 2. Iterates through the webhooks, transforming their configurations: +* - Processes `topics` for webhook events and constructs appropriate channel topics. +* - Handles data transformation based on the type of webhook event (`contentType`, `entries`, `assets`, `releases`). +* - Filters out ignored events and applies custom transformations to topics. +* 3. Builds webhook objects with necessary attributes like `urlPath`, `channels`, `destinations`, etc. +* 4. Logs success messages for each webhook transformation. +* 5. Saves the processed webhooks to a JSON file in the destination stack directory. +* 6. Logs a message confirming the successful transformation of webhooks or logs errors encountered during processing. +* +* @throws Will log errors encountered during file reading, processing, or writing of webhook configurations. +* +* @example +* // Example usage +* await createWebhooks('/path/to/package.json', 'stack123', 'project456'); +*/ +const createWebhooks = async (packagePath: string, destination_stack_id: string, projectId: string,) => { const srcFunc = 'createWebhooks'; const webhooksSave = path.join(DATA, destination_stack_id, WEBHOOKS_DIR_NAME); @@ -1035,31 +1194,31 @@ const createWebhooks = async (packagePath: string, destination_stack_id:string, } }; - /** - * Processes and generates reference and rich-text editor (RTE) reference mappings from entries in a given package file. - * - * @param {string} packagePath - The path to the package file containing entry data. - * @param {string} destination_stack_id - The ID of the destination stack where references will be saved. - * @param {string} projectId - The ID of the current project for logging purposes. - * @returns {Promise} Resolves when reference and RTE reference files are successfully generated and saved. - * - * @description - * This function performs the following tasks: - * 1. Reads and parses the package file to extract entries. - * 2. Iterates through the entries to: - * - Construct a mapping of `references`, associating entry IDs with their content type and UID. - * - Construct a mapping of `rteReferences`, associating language-specific references for each entry ID and content type. - * 3. Saves the generated mappings to separate JSON files: - * - `references.json` for general references. - * - `rte_references.json` for rich-text editor-specific references. - * 4. Logs an error message if any issue occurs during file processing or saving. - * - * @throws Will log errors encountered during file reading, data transformation, or file writing. - */ -const createRefrence = async (packagePath: string, destination_stack_id:string, projectId:string,) => { +/** +* Processes and generates reference and rich-text editor (RTE) reference mappings from entries in a given package file. +* +* @param {string} packagePath - The path to the package file containing entry data. +* @param {string} destination_stack_id - The ID of the destination stack where references will be saved. +* @param {string} projectId - The ID of the current project for logging purposes. +* @returns {Promise} Resolves when reference and RTE reference files are successfully generated and saved. +* +* @description +* This function performs the following tasks: +* 1. Reads and parses the package file to extract entries. +* 2. Iterates through the entries to: +* - Construct a mapping of `references`, associating entry IDs with their content type and UID. +* - Construct a mapping of `rteReferences`, associating language-specific references for each entry ID and content type. +* 3. Saves the generated mappings to separate JSON files: +* - `references.json` for general references. +* - `rte_references.json` for rich-text editor-specific references. +* 4. Logs an error message if any issue occurs during file processing or saving. +* +* @throws Will log errors encountered during file reading, data transformation, or file writing. +*/ +const createRefrence = async (packagePath: string, destination_stack_id: string, projectId: string,) => { const srcFunc = 'createRefrence'; - const refrencesSave = path.join(DATA, destination_stack_id,REFERENCES_DIR_NAME); - const rteRefrencesSave = path.join(DATA, destination_stack_id,RTE_REFERENCES_DIR_NAME); + const refrencesSave = path.join(DATA, destination_stack_id, REFERENCES_DIR_NAME); + const rteRefrencesSave = path.join(DATA, destination_stack_id, RTE_REFERENCES_DIR_NAME); try { const data = await fs.promises.readFile(packagePath, "utf8"); const entries = JSON.parse(data)?.entries; diff --git a/api/src/services/contentful/jsonRTE.ts b/api/src/services/contentful/jsonRTE.ts index c04f522fb..90d168b9b 100755 --- a/api/src/services/contentful/jsonRTE.ts +++ b/api/src/services/contentful/jsonRTE.ts @@ -2,7 +2,7 @@ import path from 'path'; import fs from 'fs'; import { MIGRATION_DATA_CONFIG } from '../../constants/index.js'; -const { +const { DATA, // DIR LOCALE_DIR_NAME, @@ -12,18 +12,20 @@ const { LOCALE_MASTER_LOCALE, ASSETS_SCHEMA_FILE, RTE_REFERENCES_FILE_NAME, - -} = MIGRATION_DATA_CONFIG; +} = MIGRATION_DATA_CONFIG; type NodeType = string; type LangType = string; type StackId = string; -function readFile (filePath:string) { - return JSON.parse(fs.readFileSync(filePath).toString()) +function readFile(filePath: string) { + if (fs.existsSync(filePath)) { + return JSON.parse(fs.readFileSync(filePath, 'utf-8')); + } + return undefined; } -const parsers: Map any> = new Map([ +const parsers: Map any> = new Map([ ['document', parseDocument], ['paragraph', parseParagraph], ['text', parseText], @@ -53,7 +55,7 @@ const parsers: Map parsers.get(e.nodeType)?.(e, lang, destination_stack_id)).filter(Boolean); return { @@ -85,7 +87,7 @@ function parseDocument(obj: any, lang?: LangType, destination_stack_id?:StackId) }; } -function parseTable(obj: any, lang?: LangType, destination_stack_id?:StackId): any { +function parseTable(obj: any, lang?: LangType, destination_stack_id?: StackId): any { const rowCount = obj.content.length; const colCount = Math.max(...obj.content.map((e: any) => e.content.length)); const attrs = { @@ -103,18 +105,18 @@ function parseTable(obj: any, lang?: LangType, destination_stack_id?:StackId): a }; } -function parseTableRow(obj: any,lang?: LangType, destination_stack_id?:StackId): any { +function parseTableRow(obj: any, lang?: LangType, destination_stack_id?: StackId): any { const types = new Set(); const children = obj.content.map((e: any) => { types.add(e.nodeType); return parsers.get(e.nodeType)?.(e, lang, destination_stack_id); }).filter(Boolean); - const type = types.has('table-header-cell') ? 'thead' : ''; + const type = types.has('table-header-cell') ? 'thead' : 'th'; return children.length ? { type, attrs: {}, uid: generateUID('tabletype'), children } : null; } -function parseHeadTR(obj: any[],lang?: LangType, destination_stack_id?:StackId): any { +function parseHeadTR(obj: any[], lang?: LangType, destination_stack_id?: StackId): any { const children = obj.map((e: any) => parsers.get(e.nodeType)?.(e, lang, destination_stack_id)).filter(Boolean); return { type: 'tr', @@ -124,7 +126,7 @@ function parseHeadTR(obj: any[],lang?: LangType, destination_stack_id?:StackId): }; } -function parseTableHead(obj: any, lang?: LangType, destination_stack_id?:StackId): any { +function parseTableHead(obj: any, lang?: LangType, destination_stack_id?: StackId): any { const children = obj.content.map((e: any) => parsers.get(e.nodeType)?.(e, lang, destination_stack_id)).filter(Boolean); return { type: 'th', @@ -134,7 +136,7 @@ function parseTableHead(obj: any, lang?: LangType, destination_stack_id?:StackId }; } -function parseTBody(obj: any,lang?: LangType, destination_stack_id?:StackId ): any { +function parseTBody(obj: any, lang?: LangType, destination_stack_id?: StackId): any { const children = obj.content.map((e: any) => parsers.get('body-tr')?.(e, lang, destination_stack_id)).filter(Boolean); return { type: 'tbody', @@ -144,17 +146,17 @@ function parseTBody(obj: any,lang?: LangType, destination_stack_id?:StackId ): a }; } -function parseBodyTR(obj: any, lang?: LangType, destination_stack_id?:StackId): any { +function parseBodyTR(obj: any, lang?: LangType, destination_stack_id?: StackId): any { const children = obj.content.filter((e: any) => e.nodeType === 'table-cell').map((e: any) => parsers.get('table-cell')?.(e, lang, destination_stack_id)).filter(Boolean); return children.length ? { type: 'tr', attrs: {}, uid: generateUID('tr'), children } : null; } -function parseTableBody(obj: any, lang?: LangType, destination_stack_id?:StackId): any { +function parseTableBody(obj: any, lang?: LangType, destination_stack_id?: StackId): any { const children = obj.content.map((e: any) => parsers.get(e.nodeType)?.(e, lang, destination_stack_id)).filter(Boolean); return children.length ? { type: 'td', attrs: {}, uid: generateUID('td'), children } : null; } -function parseParagraph(obj: any, lang?: LangType, destination_stack_id?:StackId): any { +function parseParagraph(obj: any, lang?: LangType, destination_stack_id?: StackId): any { const children = obj.content.map((e: any) => parsers.get(e.nodeType)?.(e, lang, destination_stack_id)).filter(Boolean); return { type: 'p', @@ -165,7 +167,7 @@ function parseParagraph(obj: any, lang?: LangType, destination_stack_id?:StackId } function parseText(obj: any): any { - const result: { text: string; [key: string]: boolean | string } = { text: obj.value }; + const result: { text: string;[key: string]: boolean | string } = { text: obj.value }; obj.marks.forEach((e: any) => { result[e.type.replace('code', 'inlineCode')] = true; }); @@ -181,8 +183,8 @@ function parseHR(): any { }; } -function parseUL(obj: any, lang?: LangType, destination_stack_id?:StackId): any { - const children = obj.content.map((e: any) => parsers.get(e.nodeType)?.(e,lang, destination_stack_id)).filter(Boolean); +function parseUL(obj: any, lang?: LangType, destination_stack_id?: StackId): any { + const children = obj.content.map((e: any) => parsers.get(e.nodeType)?.(e, lang, destination_stack_id)).filter(Boolean); return { type: 'ul', attrs: {}, @@ -192,7 +194,7 @@ function parseUL(obj: any, lang?: LangType, destination_stack_id?:StackId): any }; } -function parseOL(obj: any, lang?: LangType, destination_stack_id?:StackId): any { +function parseOL(obj: any, lang?: LangType, destination_stack_id?: StackId): any { const children = obj.content.map((e: any) => parsers.get(e.nodeType)?.(e, lang, destination_stack_id)).filter(Boolean); return { type: 'ol', @@ -203,7 +205,7 @@ function parseOL(obj: any, lang?: LangType, destination_stack_id?:StackId): any }; } -function parseLI(obj: any, lang?: LangType, destination_stack_id?:StackId): any { +function parseLI(obj: any, lang?: LangType, destination_stack_id?: StackId): any { const children = obj.content.map((e: any) => parsers.get(e.nodeType)?.(e, lang, destination_stack_id)).flat().filter(Boolean); return { type: 'li', @@ -213,7 +215,7 @@ function parseLI(obj: any, lang?: LangType, destination_stack_id?:StackId): any }; } -function parseBlockReference(obj: any, lang?: LangType, destination_stack_id?:StackId): any { +function parseBlockReference(obj: any, lang?: LangType, destination_stack_id?: StackId): any { const entryId: { [key: string]: any } = destination_stack_id && readFile(path.join(process.cwd(), DATA, destination_stack_id, RTE_REFERENCES_DIR_NAME, RTE_REFERENCES_FILE_NAME)); const defaultLocale = destination_stack_id && readFile(path.join(process.cwd(), DATA, destination_stack_id, LOCALE_DIR_NAME, LOCALE_MASTER_LOCALE)); const masterLocale = Object.values(defaultLocale).map((localeId: any) => localeId.code).join(); @@ -221,7 +223,7 @@ function parseBlockReference(obj: any, lang?: LangType, destination_stack_id?:St if (masterLocale === lang || lang) { for (const [arrayKey, arrayValue] of Object.entries(entryId)) { - if (arrayValue[obj.data.target.sys.id] && lang === arrayKey) { + if (arrayValue?.[obj?.data?.target?.sys?.id]?._content_type_uid && lang === arrayKey) { return { type: 'reference', attrs: { @@ -239,57 +241,65 @@ function parseBlockReference(obj: any, lang?: LangType, destination_stack_id?:St } } return { - type: 'reference', + type: 'p', attrs: {}, uid: generateUID('reference'), children: [{ text: '' }], }; } -function parseInlineReference(obj: any, lang?: LangType, destination_stack_id?:StackId): any { +function parseInlineReference(obj: any, lang?: LangType, destination_stack_id?: StackId): any { const entryId: { [key: string]: any } = destination_stack_id && readFile(path.join(process.cwd(), DATA, destination_stack_id, RTE_REFERENCES_DIR_NAME, RTE_REFERENCES_FILE_NAME)); const entry = Object.entries(entryId).find(([arrayKey, arrayValue]) => arrayKey === lang && arrayValue[obj.data.target.sys.id]); if (entry) { const [arrayKey, arrayValue] = entry; - return { - type: 'reference', - attrs: { - 'display-type': 'block', - type: 'entry', - 'class-name': 'embedded-entry redactor-component block-entry', - 'entry-uid': obj.data.target.sys.id, - locale: arrayKey, - 'content-type-uid': arrayValue._content_type_uid, - }, - uid: generateUID('reference'), - children: [{ text: '' }], - }; + if (arrayValue?.[obj?.data?.target?.sys?.id]?._content_type_uid && arrayKey) { + return { + type: 'reference', + attrs: { + 'display-type': 'block', + type: 'entry', + 'class-name': 'embedded-entry redactor-component block-entry', + 'entry-uid': obj.data.target.sys.id, + locale: arrayKey, + 'content-type-uid': arrayValue?.[obj?.data?.target?.sys?.id]?._content_type_uid, + }, + uid: generateUID('reference'), + children: [{ text: '' }], + }; + } } return { - type: 'reference', + type: 'p', attrs: {}, uid: generateUID('reference'), children: [{ text: '' }], }; } -function parseBlockAsset(obj: any, lang?: LangType, destination_stack_id?:StackId): any { +function parseBlockAsset(obj: any, lang?: LangType, destination_stack_id?: StackId): any { const assetId = destination_stack_id && readFile(path.join(process.cwd(), DATA, destination_stack_id, ASSETS_DIR_NAME, ASSETS_SCHEMA_FILE)); - const asset = assetId[obj.data.target.sys.id]; - // const attrs: any = {}; + const asset = assetId?.[obj?.data?.target?.sys?.id]; if (asset) { return { type: 'reference', attrs: { 'display-type': 'download', + "type": "asset", 'asset-uid': obj.data.target.sys.id, 'class-name': 'embedded-asset redactor-component block-asset', - title: asset.title, - filename: asset.fileName, - locale: asset.locale, + "asset-type": asset?.content_type, + title: asset?.title, + filename: asset?.fileName, + locale: asset?.locale, + "content-type-uid": "sys_assets", + "asset-link": asset?.url, + "asset-name": asset?.title, + "alt": "", + "asset-alt": "", }, uid: generateUID('reference'), children: [{ text: '' }], @@ -297,7 +307,7 @@ function parseBlockAsset(obj: any, lang?: LangType, destination_stack_id?:StackI } return { - type: 'reference', + type: 'p', attrs: {}, uid: generateUID('reference'), children: [{ text: '' }], @@ -383,8 +393,8 @@ function parseEntryHyperlink(obj: any, lang?: LangType): any { }; } -function parseAssetHyperlink(obj: any, lang?: LangType, destination_stack_id?:StackId): any { - const assetId = destination_stack_id && readFile(path.join(process.cwd(), DATA, destination_stack_id,ASSETS_DIR_NAME , ASSETS_SCHEMA_FILE)); +function parseAssetHyperlink(obj: any, lang?: LangType, destination_stack_id?: StackId): any { + const assetId = destination_stack_id && readFile(path.join(process.cwd(), DATA, destination_stack_id, ASSETS_DIR_NAME, ASSETS_SCHEMA_FILE)); const asset = assetId[obj.data.target.sys.id]; if (asset) { return { diff --git a/api/src/services/extension.service.ts b/api/src/services/extension.service.ts new file mode 100644 index 000000000..ed9fbb8ce --- /dev/null +++ b/api/src/services/extension.service.ts @@ -0,0 +1,75 @@ +import path from "path"; +import fs from 'fs'; +import { MIGRATION_DATA_CONFIG, LIST_EXTENSION_UID } from "../constants/index.js"; + +const { + CUSTOM_MAPPER_FILE_NAME, + EXTENSION_APPS_DIR_NAME, + EXTENSION_APPS_FILE_NAME +} = MIGRATION_DATA_CONFIG; + +const writeExtFile = async ({ destinationStackId, extensionData }: any) => { + const dirPath = path.join(process.cwd(), MIGRATION_DATA_CONFIG.DATA, destinationStackId, EXTENSION_APPS_DIR_NAME); + try { + await fs.promises.access(dirPath); + } catch (err) { + try { + await fs.promises.mkdir(dirPath, { recursive: true }); + } catch (mkdirErr) { + console.error("🚀 ~ fs.mkdir ~ err:", mkdirErr); + return; + } + } + try { + const filePath = path.join(dirPath, EXTENSION_APPS_FILE_NAME); + await fs.promises.writeFile(filePath, JSON.stringify(extensionData, null, 2)); + } catch (writeErr) { + console.error("🚀 ~ fs.writeFile ~ err:", writeErr); + } +} + +const getExtension = ({ uid, destinationStackId }: any) => { + if (uid === LIST_EXTENSION_UID) { + return { + "stackHeaders": { "api_key": destinationStackId }, + "urlPath": `/extensions/${destinationStackId}`, + "uid": LIST_EXTENSION_UID, + "created_at": "2025-02-18T14:45:22.630Z", + "updated_at": "2025-02-18T14:45:22.630Z", + "created_by": "bltba052dc70a273dd2", + "updated_by": "bltba052dc70a273dd2", + "tags": [], + "_version": 1, + "title": "Key-value Field", + "config": {}, + "type": "field", + "data_type": "json", + "multiple": false, + "srcdoc": "\n\n\n\n \n \n \n \n \n \n \n\n\n\n
\n
\n
\n
\n :\n \n \n \n
\n
\n
\n
\n\n
\n \n\n\n" + } + } + return null; +} + +const createExtension = async ({ destinationStackId }: any) => { + const extensionPath = path.join(MIGRATION_DATA_CONFIG.DATA, destinationStackId, CUSTOM_MAPPER_FILE_NAME); + const extMapper: any = await fs.promises.readFile(extensionPath, "utf-8").catch(async () => { }); + if (extMapper !== undefined) { + const extensionData: any = {}; + const extJson = JSON?.parse(extMapper); + const uniqueExtUids: any = [...new Set(extJson?.map?.((item: any) => item.extensionUid))]; + for await (const extUid of uniqueExtUids ?? []) { + const extData = getExtension({ uid: extUid, destinationStackId }); + if (extData) { + extensionData[extUid] = extData; + } + } + await writeExtFile({ destinationStackId, extensionData }) + } +} + + + +export const extensionService = { + createExtension +} \ No newline at end of file diff --git a/api/src/services/marketplace.service.ts b/api/src/services/marketplace.service.ts new file mode 100644 index 000000000..e7f5301a1 --- /dev/null +++ b/api/src/services/marketplace.service.ts @@ -0,0 +1,105 @@ +import path from 'path'; +import fs from 'fs'; +import getAuthtoken from "../utils/auth.utils.js"; +import { MIGRATION_DATA_CONFIG, KEYTOREMOVE } from '../constants/index.js'; +import { getAppManifestAndAppConfig } from '../utils/market-app.utils.js'; +import { v4 as uuidv4 } from "uuid"; + + +const { + EXTENSIONS_MAPPER_DIR_NAME, + MARKETPLACE_APPS_DIR_NAME, + MARKETPLACE_APPS_FILE_NAME +} = MIGRATION_DATA_CONFIG; + + +const groupByAppUid = (data: any) => { + return data?.reduce?.((acc: any, item: any) => { + if (!acc[item.appUid]) { + acc[item.appUid] = []; + } + acc[item.appUid].push(item.extensionUid); + return acc; + }, {}); +} +const removeKeys = (obj: any, keysToRemove: any) => { + return Object.fromEntries( + Object.entries(obj).filter(([key]) => !keysToRemove.includes(key)) + ); +} + +const writeManifestFile = async ({ destinationStackId, appManifest }: any) => { + const dirPath = path.join(process.cwd(), MIGRATION_DATA_CONFIG.DATA, destinationStackId, MARKETPLACE_APPS_DIR_NAME); + try { + await fs.promises.access(dirPath); + } catch (err) { + try { + await fs.promises.mkdir(dirPath, { recursive: true }); + } catch (mkdirErr) { + console.error("🚀 ~ fs.mkdir ~ err:", mkdirErr); + return; + } + } + try { + const filePath = path.join(dirPath, MARKETPLACE_APPS_FILE_NAME); + await fs.promises.writeFile(filePath, JSON.stringify(appManifest, null, 2)); + } catch (writeErr) { + console.error("🚀 ~ fs.writeFile ~ err:", writeErr); + } +} + + + +const createAppManifest = async ({ destinationStackId, region, userId, orgId }: any) => { + const authtoken = await getAuthtoken(region, userId); + const marketPlacePath = path.join(MIGRATION_DATA_CONFIG.DATA, destinationStackId, EXTENSIONS_MAPPER_DIR_NAME); + const AppMapper: any = await fs.promises.readFile(marketPlacePath, "utf-8").catch(async () => { }); + if (AppMapper !== undefined) { + const appManifest: any = []; + const groupUids: any = groupByAppUid(JSON.parse(AppMapper)); + for await (const [key, value] of Object?.entries?.(groupUids) || {}) { + const data: any = await getAppManifestAndAppConfig({ organizationUid: orgId, authtoken, region, manifestUid: key }); + data.manifest = removeKeys(data, KEYTOREMOVE); + const extensionUids: any = new Set(value) ?? []; + const locations: any = []; + for (const ext of extensionUids ?? []) { + const seprateUid = ext?.split?.('-'); + const type: string = seprateUid?.[1]; + const extUid: string = seprateUid?.[0]; + for (const loc of data?.ui_location?.locations ?? []) { + if (loc?.type === type) { + const isPresent = locations?.meta?.findIndex((item: any) => item?.extension_uid === extUid); + if (isPresent === undefined) { + locations?.push({ + type, + meta: [{ ...(loc?.meta?.[0] || {}), extension_uid: extUid }] + }) + } + } + } + } + const configData = data?.ui_location?.locations?.find((ele: any) => ele?.type === 'cs.cm.stack.config'); + if (configData) { + locations?.push({ + type: configData?.type, + meta: [{ ...(configData?.meta?.[0] || {}), name: 'Config', extension_uid: uuidv4() }] + }) + } + data.ui_location.locations = locations; + data.status = "installed"; + data.target = { + "type": "stack", + "uid": destinationStackId + }; + data.installation_uid = data?.uid; + data.configuration = ""; + data.server_configuration = ""; + appManifest?.push(removeKeys(data, KEYTOREMOVE)); + } + await writeManifestFile({ destinationStackId, appManifest }); + } +} + +export const marketPlaceAppService = { + createAppManifest +} \ No newline at end of file diff --git a/api/src/services/migration.service.ts b/api/src/services/migration.service.ts index 4d234b7db..5b7a6c781 100644 --- a/api/src/services/migration.service.ts +++ b/api/src/services/migration.service.ts @@ -18,6 +18,9 @@ import customLogger from "../utils/custom-logger.utils.js"; import { setLogFilePath } from "../server.js"; import fs from 'fs'; import { contentfulService } from "./contentful.service.js"; +import { marketPlaceAppService } from "./marketplace.service.js"; +import { extensionService } from "./extension.service.js"; + @@ -220,6 +223,8 @@ const startTestMigration = async (req: Request): Promise => { await customLogger(projectId, project?.current_test_stack_id, 'info', message); await setLogFilePath(loggerPath); const contentTypes = await fieldAttacher({ orgId, projectId, destinationStackId: project?.current_test_stack_id, region, user_id }); + await marketPlaceAppService?.createAppManifest({ orgId, destinationStackId: project?.current_test_stack_id, region, userId: user_id }); + await extensionService?.createExtension({ destinationStackId: project?.current_test_stack_id }); switch (cms) { case CMS.SITECORE_V8: case CMS.SITECORE_V9: @@ -253,8 +258,8 @@ const startTestMigration = async (req: Request): Promise => { await contentfulService?.createRefrence(file_path, project?.current_test_stack_id, projectId); await contentfulService?.createWebhooks(file_path, project?.current_test_stack_id, projectId); await contentfulService?.createEnvironment(file_path, project?.current_test_stack_id, projectId); - await contentfulService?.createAssets(file_path, project?.current_test_stack_id, projectId); - await contentfulService?.createEntry(file_path, project?.current_test_stack_id, projectId); + await contentfulService?.createAssets(file_path, project?.current_test_stack_id, projectId, true); + await contentfulService?.createEntry(file_path, project?.current_test_stack_id, projectId, contentTypes, project?.mapperKeys); await contentfulService?.createVersionFile(project?.current_test_stack_id, projectId); break; } @@ -294,7 +299,8 @@ const startMigration = async (req: Request): Promise => { await customLogger(projectId, project?.destination_stack_id, 'info', message); await setLogFilePath(loggerPath); const contentTypes = await fieldAttacher({ orgId, projectId, destinationStackId: project?.destination_stack_id, region, user_id }); - + await marketPlaceAppService?.createAppManifest({ orgId, destinationStackId: project?.current_test_stack_id, region, userId: user_id }); + await extensionService?.createExtension({ destinationStackId: project?.current_test_stack_id }); switch (cms) { case CMS.SITECORE_V8: case CMS.SITECORE_V9: @@ -331,7 +337,7 @@ const startMigration = async (req: Request): Promise => { await contentfulService?.createWebhooks(file_path, project?.destination_stack_id, projectId); await contentfulService?.createEnvironment(file_path, project?.destination_stack_id, projectId); await contentfulService?.createAssets(file_path, project?.destination_stack_id, projectId); - await contentfulService?.createEntry(file_path, project?.destination_stack_id, projectId); + await contentfulService?.createEntry(file_path, project?.current_test_stack_id, projectId, contentTypes, project?.mapperKeys); await contentfulService?.createVersionFile(project?.destination_stack_id, projectId); break; } diff --git a/api/src/services/wordpress.service.ts b/api/src/services/wordpress.service.ts index 7a45cdb1d..561fb32e9 100644 --- a/api/src/services/wordpress.service.ts +++ b/api/src/services/wordpress.service.ts @@ -763,6 +763,7 @@ async function saveAuthors(authorDetails: any[], destinationStackId: string, pro uid: customId, ...mapContentTypeToEntry(contentType, authordata), }; + acc[customId].publish_details = []; return acc; @@ -1463,7 +1464,7 @@ async function saveTerms(termsDetails: any[], destinationStackId: string, projec uid: customId, ...mapContentTypeToEntry(contentType, data), // Pass individual term object }; - + acc[customId].publish_details = []; return acc; }, {} @@ -1587,6 +1588,7 @@ async function saveTags(tagDetails: any[], destinationStackId: string, projectId uid:customId, ...mapContentTypeToEntry(contenttype,data), }; + acc[customId].publish_details = []; return acc; }, {}); @@ -1729,6 +1731,7 @@ async function saveCategories(categoryDetails: any[], destinationStackId:string, uid:customId, ...mapContentTypeToEntry(contenttype,data), } + acc[customId].publish_details = []; return acc; }, @@ -1873,10 +1876,13 @@ const limit = limitConcurrency(5); async function featuredImageMapping(postid: string, post: any, postdata: any) { try { - - const assetsId = JSON.parse(fs.readFileSync( - path.join(process.cwd(), assetsSave, MIGRATION_DATA_CONFIG.ASSETS_SCHEMA_FILE),'utf8' - )); + const filePath = path.join(process.cwd(), assetsSave, MIGRATION_DATA_CONFIG.ASSETS_SCHEMA_FILE); + const fileContent = fs.readFileSync(filePath, 'utf8').trim(); + + if (!fileContent) { + throw new Error(`File ${filePath} is empty or missing`); + } + const assetsId = JSON?.parse(fileContent); if (!post["wp:postmeta"] || !assetsId) return; const postmetaArray = Array.isArray(post["wp:postmeta"]) ? post["wp:postmeta"] @@ -2018,14 +2024,15 @@ async function processChunkData( category: postCategories, terms: postTerms, tag: postTags, - featured_image: '' + featured_image: '', + publish_details:[] }; const formatted_posts = await featuredImageMapping( `posts_${data["wp:post_id"]}`, data, postdata ); - const formattedPosts = Object.entries(formatted_posts).reduce( + const formattedPosts = Object?.entries(formatted_posts)?.reduce( (acc: { [key: string]: any }, data:any) => { const customId = idCorrector(data["uid"]) @@ -2035,7 +2042,8 @@ async function processChunkData( ...acc[customId], uid: customId, ...mapContentTypeToEntry(contenttype,data), - } + }; + acc[customId].publish_details = []; return acc; }, diff --git a/api/src/utils/content-type-creator.utils.ts b/api/src/utils/content-type-creator.utils.ts index c8540cd58..6e3c754eb 100644 --- a/api/src/utils/content-type-creator.utils.ts +++ b/api/src/utils/content-type-creator.utils.ts @@ -3,14 +3,17 @@ import path from 'path'; import _ from 'lodash'; import customLogger from './custom-logger.utils.js'; import { getLogMessage } from './index.js'; -import { MIGRATION_DATA_CONFIG } from '../constants/index.js'; +import { LIST_EXTENSION_UID, MIGRATION_DATA_CONFIG } from '../constants/index.js'; import { contentMapperService } from "../services/contentMapper.service.js"; +import appMeta from '../constants/app/index.json'; const { GLOBAL_FIELDS_FILE_NAME, GLOBAL_FIELDS_DIR_NAME, CONTENT_TYPES_DIR_NAME, - CONTENT_TYPES_SCHEMA_FILE + CONTENT_TYPES_SCHEMA_FILE, + EXTENSIONS_MAPPER_DIR_NAME, + CUSTOM_MAPPER_FILE_NAME } = MIGRATION_DATA_CONFIG; interface Group { @@ -71,7 +74,29 @@ const arrangGroups = ({ schema, newStack }: any) => { return dtSchema; } -const convertToSchemaFormate = ({ field, advanced = true }: any) => { +const saveAppMapper = async ({ marketPlacePath, data, fileName }: any) => { + try { + await fs.promises.access(marketPlacePath); + } catch (err) { + try { + await fs.promises.mkdir(marketPlacePath, { recursive: true }); + } catch (mkdirErr) { + console.error("🚀 ~ fs.mkdir ~ err:", mkdirErr); + return; + } + } + const marketPlaceFilePath = path.join(marketPlacePath, fileName); + const newData: any = await fs.promises.readFile(marketPlaceFilePath, "utf-8").catch(async () => { + await fs.promises.writeFile(marketPlaceFilePath, JSON.stringify([data])); + }); + if (newData !== "" && newData !== undefined) { + const parseData: any = JSON.parse(newData); + parseData?.push(data); + await fs.promises.writeFile(marketPlaceFilePath, JSON.stringify(parseData)); + } +} + +const convertToSchemaFormate = ({ field, advanced = true, marketPlacePath }: any) => { switch (field?.contentstackFieldType) { case 'single_line_text': { return { @@ -446,6 +471,60 @@ const convertToSchemaFormate = ({ field, advanced = true }: any) => { return htmlField; } + case 'app': { + const appName = field?.otherCmsField?.replace?.(/[()-App]/g, '')?.trim?.()?.split?.(' ')?.[1]; + const title = field?.title?.split?.(' ')?.[0]; + const appDetails = appMeta?.entries?.find?.((item: any) => item?.title === appName); + if (appDetails?.uid) { + saveAppMapper({ + marketPlacePath, + data: { appUid: appDetails?.app_uid, extensionUid: `${appDetails?.uid}-cs.cm.stack.custom_field` }, + fileName: EXTENSIONS_MAPPER_DIR_NAME + }); + return { + "display_name": title, + "extension_uid": appDetails?.uid, + "field_metadata": { + "extension": true + }, + "uid": field?.uid, + "config": {}, + "data_type": "json", + "multiple": field?.advanced?.multiple ?? false, + "mandatory": field?.advanced?.mandatory ?? false, + "unique": field?.advanced?.unique ?? false, + "non_localizable": field.advanced?.nonLocalizable ?? false, + } + } + break; + } + + case 'extension': { + if (['listInput', 'tagEditor']?.includes(field?.otherCmsType)) { + const extensionUid = LIST_EXTENSION_UID; + saveAppMapper({ + marketPlacePath, + data: { extensionUid }, + fileName: CUSTOM_MAPPER_FILE_NAME + }); + return { + "display_name": field?.title, + "uid": field?.uid, + "extension_uid": extensionUid, + "field_metadata": { + "extension": true + }, + "config": {}, + "multiple": field?.advanced?.multiple ?? false, + "mandatory": field?.advanced?.mandatory ?? false, + "unique": field?.advanced?.unique ?? false, + "non_localizable": field.advanced?.nonLocalizable ?? false, + "data_type": "json", + } + } + break; + } + default: { if (field?.contentstackFieldType) { return { @@ -598,6 +677,7 @@ const mergeTwoCts = async (ct: any, mergeCts: any) => { } export const contenTypeMaker = async ({ contentType, destinationStackId, projectId, newStack, keyMapper, region, user_id }: any) => { + const marketPlacePath = path.join(process.cwd(), MIGRATION_DATA_CONFIG.DATA, destinationStackId); const srcFunc = 'contenTypeMaker'; let ct: ContentType = { title: contentType?.contentstackTitle, @@ -629,7 +709,7 @@ export const contenTypeMaker = async ({ contentType, destinationStackId, project uid: extractValue(element?.contentstackFieldUid, item?.contentstackFieldUid, '.'), title: extractValue(element?.contentstackField, item?.contentstackField, ' >')?.trim(), } - const schema: any = convertToSchemaFormate({ field }); + const schema: any = convertToSchemaFormate({ field, marketPlacePath }); if (typeof schema === 'object' && Array.isArray(group?.schema) && element?.isDeleted === false) { group.schema.push(schema); } @@ -641,7 +721,8 @@ export const contenTypeMaker = async ({ contentType, destinationStackId, project ...item, title: item?.contentstackField, uid: item?.contentstackFieldUid - } + }, + marketPlacePath }); if (dt && item?.isDeleted === false) { ct?.schema?.push(dt); diff --git a/api/src/utils/market-app.utils.ts b/api/src/utils/market-app.utils.ts new file mode 100644 index 000000000..8a5fbe699 --- /dev/null +++ b/api/src/utils/market-app.utils.ts @@ -0,0 +1,25 @@ +import contentstack from '@contentstack/marketplace-sdk'; +import { DEVURLS } from '../constants/index.js'; + + + + +export const getAllApps = async ({ organizationUid, authtoken, region }: any) => { + try { + const client = contentstack.client({ authtoken, host: DEVURLS?.[region] ?? DEVURLS?.NA }); + const data = await client.marketplace(organizationUid).findAllApps(); + return data?.items; + } catch (err) { + console.info("🚀 ~ getAllApps ~ err:", err) + } +} + +export const getAppManifestAndAppConfig = async ({ organizationUid, authtoken, region, manifestUid }: any) => { + try { + const client = contentstack.client({ authtoken, host: DEVURLS?.[region] ?? DEVURLS?.NA }); + const data = await client.marketplace(organizationUid).app(manifestUid).fetch(); + return data; + } catch (err: any) { + console.info("🚀 ~ getAppManifestAndAppConfig ~ err:", err) + } +} \ No newline at end of file diff --git a/api/src/utils/test-folder-creator.utils.ts b/api/src/utils/test-folder-creator.utils.ts index 2b7268f84..bda13e11c 100644 --- a/api/src/utils/test-folder-creator.utils.ts +++ b/api/src/utils/test-folder-creator.utils.ts @@ -192,15 +192,20 @@ const lookForReference = async ( const sortAssets = async (baseDir: string) => { const assetsPath = path.join(process.cwd(), baseDir, ASSETS_DIR_NAME); - const assetsFilesPath = path.join(assetsPath, 'files'); - const assetsJson = JSON.parse(await fs.promises.readFile(path.join(assetsPath, ASSETS_SCHEMA_FILE), 'utf8')); - const sortAsset = Object?.values?.(assetsJson)?.slice(0, 10); - const assetsMeta: any = {}; - sortAsset?.forEach((item: any) => { - assetsMeta[item?.uid] = item; - }) - await cleanDirectory(assetsFilesPath, sortAsset); - await fs.promises.writeFile(path.join(assetsPath, ASSETS_SCHEMA_FILE), JSON?.stringify?.(assetsMeta)); + try { + await fs.promises.access(assetsPath); + const assetsFilesPath = path.join(assetsPath, 'files'); + const assetsJson = JSON.parse(await fs.promises.readFile(path.join(assetsPath, ASSETS_SCHEMA_FILE), 'utf8') ?? {}); + const sortAsset = Object?.values?.(assetsJson)?.slice(0, 10); + const assetsMeta: any = {}; + sortAsset?.forEach((item: any) => { + assetsMeta[item?.uid] = item; + }) + await cleanDirectory(assetsFilesPath, sortAsset); + await fs.promises.writeFile(path.join(assetsPath, ASSETS_SCHEMA_FILE), JSON?.stringify?.(assetsMeta)); + } catch (err) { + console.error('assest not exits on Path:', assetsPath); + } } const writeGlobalField = async (schema: any, globalSave: string, filePath: string) => { @@ -271,7 +276,7 @@ export const testFolderCreator = async ({ destinationStackId }: any) => { } } } - const sortData = allData?.length > 3 ? allData.sort((a, b) => b?.count - a?.count).slice?.(1, 4) : allData; + const sortData = allData?.length > 3 ? allData.sort((a, b) => b?.count - a?.count).slice?.(0, 3) : allData; const finalData: any = []; sortData.forEach((et: any) => { const entryObj: any = {}; diff --git a/ui/src/cmsData/projects.json b/ui/src/cmsData/projects.json index 7c8516da2..bde386e2b 100644 --- a/ui/src/cmsData/projects.json +++ b/ui/src/cmsData/projects.json @@ -56,20 +56,6 @@ "text": "Begin your content migration journey by creating a new project. Follow the steps to ensure a smooth and efficient transfer of your content." } ] - }, - { - "type": "p", - "attrs": { - "style": {}, - "redactor-attributes": {}, - "dir": "ltr" - }, - "uid": "9ecb9e032e8844f0be6414dd13e318a4", - "children": [ - { - "text": "Happy migrating!" - } - ] } ], "type": "doc", diff --git a/ui/src/components/AdvancePropertise/index.tsx b/ui/src/components/AdvancePropertise/index.tsx index 4bd709a99..add4d4472 100644 --- a/ui/src/components/AdvancePropertise/index.tsx +++ b/ui/src/components/AdvancePropertise/index.tsx @@ -53,7 +53,7 @@ const AdvancePropertise = (props: SchemaProps) => { mandatory: props?.value?.mandatory, allowImagesOnly: props?.value?.allowImagesOnly, nonLocalizable: props?.value?.nonLocalizable, - embedObject: true, + embedObject: (props?.value?.embedObjects?.length ?? 0) > 0, embedAssests: true, multiple: props?.value?.multiple, embedObjects: props?.value?.embedObjects, @@ -537,12 +537,12 @@ const AdvancePropertise = (props: SchemaProps) => { label="Embed Object(s)" labelColor="primary" labelPosition="right" - checked={toggleStates?.embedObject} + checked={(ctValue?.length ?? 0) > 0 || toggleStates?.embedObject} onChange={handleToggleChange && ((e: React.MouseEvent) => handleToggleChange('embedObject', (e.target as HTMLInputElement)?.checked, true))} /> - {toggleStates?.embedObject && ( + {(ctValue && ctValue?.length > 0 || toggleStates?.embedObject) && (