From 3b89971af7886dabc3d10944d60a9a822eb65503 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Wed, 26 Jun 2024 12:36:19 -0600 Subject: [PATCH] fix: Issue 28943 storyblock bug (#29008) Previously this fix, when a wyswyg editor contains already information on the db and makes the migration from wyswyg to block editor. --- .../business/StoryBlockAPIImpl.java | 64 +++++++++++-------- .../business/StoryBlockAPITest.java | 18 +++++- 2 files changed, 54 insertions(+), 28 deletions(-) diff --git a/dotCMS/src/main/java/com/dotcms/contenttype/business/StoryBlockAPIImpl.java b/dotCMS/src/main/java/com/dotcms/contenttype/business/StoryBlockAPIImpl.java index 5935b501ba6c..6376f4b13883 100644 --- a/dotCMS/src/main/java/com/dotcms/contenttype/business/StoryBlockAPIImpl.java +++ b/dotCMS/src/main/java/com/dotcms/contenttype/business/StoryBlockAPIImpl.java @@ -66,7 +66,7 @@ public StoryBlockReferenceResult refreshReferences(final Contentlet contentlet) .forEach(field -> { final Object storyBlockValue = contentlet.get(field.variable()); - if (null != storyBlockValue && JsonUtil.isValidJSON(storyBlockValue.toString())) { + if (null != storyBlockValue) { final StoryBlockReferenceResult result = this.refreshStoryBlockValueReferences(storyBlockValue, contentlet.getIdentifier()); if (result.isRefreshed()) { @@ -85,40 +85,50 @@ public StoryBlockReferenceResult refreshReferences(final Contentlet contentlet) @SuppressWarnings("unchecked") public StoryBlockReferenceResult refreshStoryBlockValueReferences(final Object storyBlockValue, final String parentContentletIdentifier) { boolean refreshed = false; - if (ThreadUtils.isMethodCallCountEqualThan(this.getClass().getName(), - "refreshStoryBlockValueReferences", MAX_RECURSION_LEVEL)) { - Logger.debug(this, () -> "This method has been called more than " + MAX_RECURSION_LEVEL + - " times in the same thread. This could be a sign of circular reference in the Story Block field. Data will NOT be refreshed."); - return new StoryBlockReferenceResult(false, storyBlockValue); - } - try { - final LinkedHashMap blockEditorMap = this.toMap(storyBlockValue); - final Object contentsMap = blockEditorMap.get(CONTENT_KEY); - if(!UtilMethods.isSet(contentsMap) || !(contentsMap instanceof List)) { - return new StoryBlockReferenceResult(true, storyBlockValue); + if (null != storyBlockValue && JsonUtil.isValidJSON(storyBlockValue.toString())) { + if (ThreadUtils.isMethodCallCountEqualThan(this.getClass().getName(), + "refreshStoryBlockValueReferences", MAX_RECURSION_LEVEL)) { + Logger.debug(this, () -> "This method has been called more than " + MAX_RECURSION_LEVEL + + " times in the same thread. This could be a sign of circular reference in the Story Block field. Data will NOT be refreshed."); + return new StoryBlockReferenceResult(false, storyBlockValue); } - for (final Map contentMap : (List>) contentsMap) { - if (UtilMethods.isSet(contentMap)) { - final String type = contentMap.get(TYPE_KEY).toString(); - if (allowedTypes.contains(type)) { // if somebody adds a story block to itself, we don't want to refresh it - - refreshed |= this.refreshStoryBlockMap(contentMap, parentContentletIdentifier); - } + try { + final LinkedHashMap blockEditorMap = this.toMap(storyBlockValue); + final Object contentsMap = blockEditorMap.get(CONTENT_KEY); + if (!UtilMethods.isSet(contentsMap) || !(contentsMap instanceof List)) { + return new StoryBlockReferenceResult(true, storyBlockValue); } + refreshed = isRefreshed(parentContentletIdentifier, (List>) contentsMap); + if (refreshed) { + return new StoryBlockReferenceResult(true, this.toJson(blockEditorMap)); + } + } catch (final Exception e) { + final String errorMsg = String.format("An error occurred when refreshing Story Block Contentlet references in parent Content " + + "'%s': %s", parentContentletIdentifier, ExceptionUtil.getErrorMessage(e)); + Logger.warnAndDebug(StoryBlockAPIImpl.class, errorMsg, e); + throw new DotRuntimeException(errorMsg, e); } - if (refreshed) { - return new StoryBlockReferenceResult(true, this.toJson(blockEditorMap)); - } - } catch (final Exception e) { - final String errorMsg = String.format("An error occurred when refreshing Story Block Contentlet references in parent Content " + - "'%s': %s", parentContentletIdentifier, ExceptionUtil.getErrorMessage(e)); - Logger.warnAndDebug(StoryBlockAPIImpl.class, errorMsg, e); - throw new DotRuntimeException(errorMsg, e); } // Return the original value in case no data was refreshed return new StoryBlockReferenceResult(false, storyBlockValue); } + private boolean isRefreshed(final String parentContentletIdentifier, + final List> contentsMap) { + + boolean refreshed = false; + for (final Map contentMap : contentsMap) { + if (UtilMethods.isSet(contentMap)) { + final String type = contentMap.get(TYPE_KEY).toString(); + if (allowedTypes.contains(type)) { // if somebody adds a story block to itself, we don't want to refresh it + + refreshed |= this.refreshStoryBlockMap(contentMap, parentContentletIdentifier); + } + } + } + return refreshed; + } + /** * Takes the current data map of the referenced Contentlet in the Story Block field and checks whether it matches * its latest live version or not. If it doesn't, then it means it must be refreshed with the data map from the diff --git a/dotcms-integration/src/test/java/com/dotcms/contenttype/business/StoryBlockAPITest.java b/dotcms-integration/src/test/java/com/dotcms/contenttype/business/StoryBlockAPITest.java index 23b3ac9f69d9..ad36b35906b9 100644 --- a/dotcms-integration/src/test/java/com/dotcms/contenttype/business/StoryBlockAPITest.java +++ b/dotcms-integration/src/test/java/com/dotcms/contenttype/business/StoryBlockAPITest.java @@ -24,6 +24,7 @@ import java.util.Optional; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -407,6 +408,21 @@ public void test_get_refreshStoryBlockValueReferences_with_bad_content_value() } - + + /** + * Method to test: {@link StoryBlockAPI#refreshStoryBlockValueReferences(Object, String)} + * Given Scenario: Test a story block value that is not a json + * ExpectedResult: Do not throw exception and must return zero dependencies + */ + @Test + public void test_refreshStoryBlockValueReferences_with_json_value() { + + final Object newStoryBlockJson1 = "bu bu bu}"; + + StoryBlockReferenceResult result = APILocator.getStoryBlockAPI().refreshStoryBlockValueReferences(newStoryBlockJson1, "xxx"); + assertNotNull(result); + assertFalse(result.isRefreshed()); + + } }