Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion dotCMS/src/main/java/com/dotmarketing/util/ImportUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -3094,7 +3094,17 @@ can manage multilingual inserts for already stored records but not for the case
value = getURLFromFolderAndAssetName(siteAndFolder, urlValueAssetName);
conValue = getURLFromContentId(contentlet.getIdentifier());
}
if (!conValue.equals(value)) {

if (new LegacyFieldTransformer(field).from() instanceof HostFolderField) {
final Pair<Host, Folder> siteOrFolder = getSiteAndFolderFromIdOrName((String) value, user);
final String valueAsHostId = siteOrFolder != null ? siteOrFolder.getLeft().getIdentifier() : null;
final String valueAsFolderId = siteOrFolder != null ? siteOrFolder.getRight().getIdentifier() : null;

// Check if either host or folder matches
if (!conValue.equals(valueAsHostId) && !conValue.equals(valueAsFolderId)) {
match = false;
}
} else if (!conValue.equals(value)) {
match = false;
}
}
Expand Down
198 changes: 198 additions & 0 deletions dotcms-integration/src/test/java/com/dotcms/util/ImportUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5038,4 +5038,202 @@ private void cleanupTestData(ContentType contentType, Category testCategory, Hos
}
}

/**
* Method to test: {@link ImportUtil#importFile(Long, String, String, String[], boolean, boolean, User, long, String[], CsvReader, int, int, Reader, String, HttpServletRequest)}
* Given Scenario:
* - ContentType with a text field (slug) and a HostFolderField (site)
* - Single multilingual CSV file with 2 languages (default language + Spanish) using site NAME (not identifier) as key field
* - Both "slug" and "site" fields are set as key fields
* - Both CSV rows have the same slug value and same site name
* Expected result:
* - Create only ONE contentlet with the same identifier (2 language versions of same content)
* - Create 2 different inodes (one per language version)
* - Both versions should be published
*
* This test validates that the HostFolderField comparison works correctly when comparing
* site names (from CSV) with site identifiers (stored in contentlets) during multilingual import.
*
* @throws DotSecurityException
* @throws DotDataException
* @throws IOException
*/
@Test
public void testImportMultilingualWithSiteNameAsKeyField() throws DotSecurityException, DotDataException, IOException {
ContentType contentType = null;
Host testSite = null;
Language lang1 = null;
Language lang2 = null;

try {
// Create a test site with a name
String siteName = "testsite" + System.currentTimeMillis();
testSite = new SiteDataGen()
.name(siteName)
.nextPersisted();

lang1 = defaultLanguage;



lang2 = APILocator.getLanguageAPI().getLanguage("es", "ES");
if (lang2 == null) {
lang2 = new LanguageDataGen().languageCode("es").countryCode("ES")
.languageName("Spanish").country("Spain").nextPersisted();
}

// Create ContentType with slug and site fields
com.dotcms.contenttype.model.field.Field slugField = new FieldDataGen()
.name("slug")
.velocityVarName("slug")
.type(TextField.class)
.next();

com.dotcms.contenttype.model.field.Field siteField = new FieldDataGen()
.name("site")
.velocityVarName("site")
.type(HostFolderField.class)
.next();

contentType = new ContentTypeDataGen()
.field(slugField)
.field(siteField)
.nextPersisted();

slugField = fieldAPI.byContentTypeAndVar(contentType, slugField.variable());
siteField = fieldAPI.byContentTypeAndVar(contentType, siteField.variable());

String csvContent = "languageCode,countryCode,slug,site\r\n" +
lang1.getLanguageCode().trim() + "," + lang1.getCountryCode().trim() + ",test-article," + testSite.getHostname().trim() + "\r\n" +
lang2.getLanguageCode().trim() + "," + lang2.getCountryCode().trim() + ",test-article," + testSite.getHostname().trim() + "\r\n";

Logger.info(this, "CSV Content:\n" + csvContent);

final Reader reader = createTempFile(csvContent);
final CsvReader csvreader = new CsvReader(reader);
csvreader.setSafetySwitch(false);

final String[] csvHeaders = csvreader.getHeaders();

final HashMap<String, List<String>> imported = ImportUtil.importFile(
0L,
testSite.getIdentifier(),
contentType.inode(),
new String[]{slugField.id(), siteField.id()}, // Key fields: slug + site
false,
true,
user,
-1,
csvHeaders,
csvreader,
0,
1,
reader,
schemeStepActionResult1.getAction().getId(),
getHttpRequest()
);

// Validate import results
final List<String> results = imported.get("results");
assertNotNull("Import results should not be null", results);
assertFalse("Import results should not be empty", results.isEmpty());

final List<String> errors = imported.get("errors");
assertTrue("Import should have no errors: " + errors, errors == null || errors.isEmpty());


final List<Contentlet> contentlets = contentletAPI.findByStructure(
contentType.inode(),
user,
false,
0,
-1
);

assertEquals("Should have 2 language versions", 2, contentlets.size());

final String firstIdentifier = contentlets.get(0).getIdentifier();

for (Contentlet contentlet : contentlets) {
assertEquals("All contentlets should have the same identifier",
firstIdentifier,
contentlet.getIdentifier());

assertEquals("Slug should be 'test-article'",
"test-article",
contentlet.getStringProperty(slugField.variable()));
}


final List<Long> languageIds = contentlets.stream()
.map(Contentlet::getLanguageId)
.sorted()
.collect(Collectors.toList());

assertTrue("Should have default language version", languageIds.contains(lang1.getId()));
assertTrue("Should have Spanish version", languageIds.contains(lang2.getId()));


final List<String> inodes = contentlets.stream()
.map(Contentlet::getInode)
.distinct()
.collect(Collectors.toList());

assertEquals("Both versions should have unique inodes", 2, inodes.size());

} finally {
// Cleanup
if (contentType != null) {
try {
List<Contentlet> contentlets = contentletAPI.findByStructure(
contentType.inode(),
user,
false,
0,
-1
);
for (Contentlet contentlet : contentlets) {
contentletAPI.archive(contentlet, user, false);
contentletAPI.delete(contentlet, user, false);
}
contentTypeApi.delete(contentType);
} catch (Exception e) {
Logger.warn(ImportUtilTest.class, "Error cleaning up content type", e);
}
}

if (testSite != null) {
try {
APILocator.getHostAPI().archive(testSite, user, false);
APILocator.getHostAPI().delete(testSite, user, false);
} catch (Exception e) {
Logger.warn(ImportUtilTest.class, "Error cleaning up test site", e);
}
}

// Clean up languages if we created them (only if they didn't exist before)
if (lang1 != null) {
try {
// Check if this language has ID > 1 (meaning we created it, not a default language)
if (lang1.getId() > 1) {
APILocator.getLanguageAPI().deleteLanguage(lang1);
}
} catch (Exception e) {
Logger.warn(ImportUtilTest.class, "Error cleaning up language 1", e);
}
}

if (lang2 != null) {
try {
// Spanish is typically ID 2, but if we created it, clean it up
// Check if this is not a system default language
if (lang2.getId() > 2) {
APILocator.getLanguageAPI().deleteLanguage(lang2);
}
} catch (Exception e) {
Logger.warn(ImportUtilTest.class, "Error cleaning up language 2", e);
}
}
}
}

}