/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.cms.core.content.imports; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.joda.time.DateTime; import com.enonic.cms.core.content.ContentEntity; import com.enonic.cms.core.content.ContentKey; import com.enonic.cms.core.content.ContentStatus; import com.enonic.cms.core.content.ContentStorer; import com.enonic.cms.core.content.ContentVersionEntity; import com.enonic.cms.core.content.UpdateContentResult; import com.enonic.cms.core.content.binary.BinaryDataAndBinary; import com.enonic.cms.core.content.binary.BinaryDataKey; import com.enonic.cms.core.content.category.CategoryEntity; import com.enonic.cms.core.content.command.AssignContentCommand; import com.enonic.cms.core.content.command.CreateContentCommand; import com.enonic.cms.core.content.command.UpdateContentCommand; import com.enonic.cms.core.content.contentdata.custom.BinaryDataEntry; import com.enonic.cms.core.content.contentdata.custom.CustomContentData; import com.enonic.cms.core.content.contentdata.custom.DataEntry; import com.enonic.cms.core.content.contenttype.CtyImportBlockConfig; import com.enonic.cms.core.content.contenttype.CtyImportConfig; import com.enonic.cms.core.content.contenttype.CtyImportMappingConfig; import com.enonic.cms.core.content.contenttype.CtyImportUpdateStrategyConfig; import com.enonic.cms.core.content.imports.sourcevalueholders.AbstractSourceValue; import com.enonic.cms.core.portal.PrettyPathNameCreator; import com.enonic.cms.core.security.user.UserEntity; import com.enonic.cms.store.dao.ContentDao; public class ContentImporterImpl { private RelatedContentFinder relatedContentFinder; private ContentDao contentDao; private ContentStorer contentStorer; private final CtyImportConfig importConfig; private final UserEntity importer; private final CategoryEntity category; private final DateTime defaultPublishFrom; private final DateTime defaultPublishTo; private final ImportJob importJob; private final ImportDataEntry importDataEntry; private boolean transliterate; private final ImportResult importResult; private final Set<String> usedSyncValues = new HashSet<String>(); public ContentImporterImpl( ImportJob importJob, ImportDataEntry importDataEntry, boolean transliterate ) { this.importJob = importJob; this.importDataEntry = importDataEntry; this.transliterate = transliterate; this.category = importJob.getCategoryToImportTo(); this.defaultPublishFrom = importJob.getDefaultPublishFrom(); this.defaultPublishTo = importJob.getDefaultPublishTo(); this.importer = importJob.getImporter(); this.importResult = importJob.getImportResult(); this.importConfig = importJob.getImportConfig(); } public boolean importData() { if ( importConfig.isSyncEnabled() ) { checkSyncValueExist( importDataEntry ); checkSyncValueUnused( importDataEntry ); usedSyncValues.add( importDataEntry.getSyncValue() ); } importDataEntry( importDataEntry ); return true; } private void checkSyncValueExist( final ImportDataEntry importDataEntry ) { if ( importDataEntry.getSyncValue() == null ) { throw new InvalidImportDataException( "Sync value not found: " + importDataEntry ); } } private void checkSyncValueUnused( final ImportDataEntry importDataEntry ) { if ( usedSyncValues.contains( importDataEntry.getSyncValue() ) ) { throw new InvalidImportDataException( "Import data did not contain unique entries as defined by the sync field, found duplicate with sync value: \"" + importDataEntry.getSyncValue() + "\"" ); } } private void importDataEntry( final ImportDataEntry importDataEntry ) { if ( importConfig.isSyncEnabled() ) { final ContentKey existingContentKey = importJob.resolveExistingContentBySyncValue( importDataEntry ); if ( existingContentKey == null ) { importByInsertingNewContent( importDataEntry ); } else { importByChangeExistingContent( importDataEntry, existingContentKey ); } } else { importByInsertingNewContent( importDataEntry ); } } private void importByInsertingNewContent( final ImportDataEntry importDataEntry ) { // create new content final ContentEntity newContent = storeNewContent( importDataEntry ); importResult.addInserted( newContent ); final boolean isDraft = newContent.getMainVersion().getStatus().equals( ContentStatus.DRAFT ); if ( isDraft && importJob.getAssignee() != null ) { doAssignContent( newContent ); } importJob.registerImportedContent( newContent.getKey() ); } private void doAssignContent( ContentEntity newContent ) { UserEntity assignee = importJob.getAssignee(); AssignContentCommand assignContentCommand = new AssignContentCommand(); assignContentCommand.setAssigneeKey( assignee.getKey() ); assignContentCommand.setContentKey( newContent.getKey() ); assignContentCommand.setAssignerKey( importJob.getImporter().getKey() ); assignContentCommand.setAssignmentDescription( importJob.getAssignmentDescription() ); assignContentCommand.setAssignmentDueDate( importJob.getAssignmentDueDate() ); contentStorer.assignContent( assignContentCommand ); importResult.addAssigned( newContent ); } private void importByChangeExistingContent( final ImportDataEntry importDataEntry, final ContentKey existingContentKey ) { final ContentEntity existingContent = contentDao.findByKey( existingContentKey ); // update existing content final boolean anyChangesMade = updateExistingContent( existingContent, importDataEntry ); if ( anyChangesMade ) { importResult.addUpdated( existingContent ); importJob.registerImportedContent( existingContent.getKey() ); } else { importResult.addUnchanged( existingContent ); importJob.registerImportedContent( existingContent.getKey() ); } } private boolean updateExistingContent( final ContentEntity existingContent, final ImportDataEntry importDataDataEntry ) { // Content Data ImportDataEntryParser importDataEntryParser = new ImportDataEntryParser( relatedContentFinder, category.getContentType().getContentTypeConfig() ); final CustomContentData newContentData = importDataEntryParser.parseToCustomContentData( importDataDataEntry, existingContent ); final ContentVersionEntity versionToBaseNewVersionOn = existingContent.getMainVersion(); final Set<BinaryDataKey> binaryDataToRemove = getBinaryDataToRemove( versionToBaseNewVersionOn ); final List<BinaryDataAndBinary> binaryDataToAdd = getBinaryData( newContentData ); // Command final UpdateContentCommand command = createUpdateContentCommand( existingContent, versionToBaseNewVersionOn ); if ( importConfig.getUpdateContentName() ) { command.setContentName( new PrettyPathNameCreator( transliterate ).generatePrettyPathName( newContentData.getTitle() ) ); } command.setModifier( importer ); command.setContentKey( existingContent.getKey() ); command.setLanguage( existingContent.getLanguage() ); command.setOwner( existingContent.getOwner().getKey() ); command.setPriority( existingContent.getPriority() ); command.addContentAccessRights( existingContent.getContentAccessRights(), existingContent ); command.setContentData( newContentData ); for ( CtyImportBlockConfig blockConfig : importConfig.getBlocks() ) { if ( blockConfig.purgeRemainingEntries() ) { command.addBlockGroupToPurge( blockConfig.getDestination() ); } } command.setBinaryDataToAdd( binaryDataToAdd ); command.setUseCommandsBinaryDataToAdd( true ); command.setBinaryDataToRemove( binaryDataToRemove ); command.setUseCommandsBinaryDataToRemove( true ); command.setUpdateStrategy( UpdateContentCommand.UpdateStrategy.MODIFY ); final UpdateContentResult updateResult = contentStorer.updateContent( command ); return updateResult.isAnyChangesMade(); } private UpdateContentCommand createUpdateContentCommand( final ContentEntity existingContent, ContentVersionEntity versionToBaseNewVersionOn ) { ContentStatus existingContentStatus = existingContent.getMainVersion().getStatus(); final Date keepAvailableFrom = existingContent.getAvailableFrom(); final Date updateAvailableFrom = existingContent.getAvailableFrom() == null ? ( defaultPublishFrom == null ? null : defaultPublishFrom.toDate() ) : existingContent.getAvailableFrom(); final Date keepAvailableTo = existingContent.getAvailableTo(); final Date updateAvailableTo = existingContent.getAvailableTo() == null ? ( defaultPublishTo == null ? null : defaultPublishTo.toDate() ) : existingContent.getAvailableTo(); if ( importConfig.getUpdateStrategy().equals( CtyImportUpdateStrategyConfig.UPDATE_CONTENT_KEEP_STATUS ) ) { final UpdateContentCommand command; if ( existingContentStatus.equals( ContentStatus.DRAFT ) ) { command = UpdateContentCommand.updateExistingVersion2( versionToBaseNewVersionOn.getKey() ); command.setStatus( existingContentStatus ); command.setUpdateAsMainVersion( true ); command.setAvailableFrom( keepAvailableFrom ); command.setAvailableTo( keepAvailableTo ); } else { command = UpdateContentCommand.storeNewVersionIfChanged( versionToBaseNewVersionOn.getKey() ); command.setStatus( existingContentStatus ); command.setUpdateAsMainVersion( true ); if ( existingContentStatus.equals( ContentStatus.APPROVED ) ) { command.setAvailableFrom( updateAvailableFrom ); command.setAvailableTo( updateAvailableTo ); } else { command.setAvailableFrom( keepAvailableFrom ); command.setAvailableTo( keepAvailableTo ); } } return command; } else if ( importConfig.getUpdateStrategy().equals( CtyImportUpdateStrategyConfig.UPDATE_CONTENT_DRAFT ) ) { final UpdateContentCommand command; if ( existingContentStatus.equals( ContentStatus.DRAFT ) ) { command = UpdateContentCommand.updateExistingVersion2( versionToBaseNewVersionOn.getKey() ); command.setStatus( ContentStatus.DRAFT ); command.setUpdateAsMainVersion( true ); } else if ( existingContentStatus.equals( ContentStatus.ARCHIVED ) ) { command = UpdateContentCommand.storeNewVersionIfChanged( versionToBaseNewVersionOn.getKey() ); command.setStatus( ContentStatus.DRAFT ); command.setUpdateAsMainVersion( true ); } else { // main version is APPROVED if ( existingContent.hasDraft() ) { command = UpdateContentCommand.updateExistingVersion2( existingContent.getDraftVersion().getKey() ); command.setStatus( ContentStatus.DRAFT ); command.setUpdateAsMainVersion( false ); } else { command = UpdateContentCommand.storeNewVersionIfChanged( versionToBaseNewVersionOn.getKey() ); command.setStatus( ContentStatus.DRAFT ); command.setUpdateAsMainVersion( false ); } } command.setAvailableFrom( keepAvailableFrom ); command.setAvailableTo( keepAvailableTo ); return command; } else if ( importConfig.getUpdateStrategy().equals( CtyImportUpdateStrategyConfig.UPDATE_AND_APPROVE_CONTENT ) ) { final UpdateContentCommand command; if ( existingContentStatus == ContentStatus.DRAFT ) { command = UpdateContentCommand.updateExistingVersion2( versionToBaseNewVersionOn.getKey() ); } else if ( existingContentStatus == ContentStatus.APPROVED ) { command = UpdateContentCommand.storeNewVersionIfChanged( versionToBaseNewVersionOn.getKey() ); } else // Status == ARCHIVED { command = UpdateContentCommand.storeNewVersionEvenIfUnchanged( versionToBaseNewVersionOn.getKey() ); } command.setStatus( ContentStatus.APPROVED ); command.setUpdateAsMainVersion( true ); command.setAvailableFrom( updateAvailableFrom ); command.setAvailableTo( updateAvailableTo ); return command; } else if ( importConfig.getUpdateStrategy().equals( CtyImportUpdateStrategyConfig.UPDATE_AND_ARCHIVE_CONTENT ) ) { final UpdateContentCommand command; command = UpdateContentCommand.storeNewVersionIfChanged( versionToBaseNewVersionOn.getKey() ); command.setStatus( ContentStatus.ARCHIVED ); command.setUpdateAsMainVersion( true ); command.setAvailableFrom( keepAvailableFrom ); command.setAvailableTo( keepAvailableTo ); return command; } throw new UnsupportedOperationException( "Unsupported update strategy: " + importConfig.getUpdateStrategy() ); } private Set<BinaryDataKey> getBinaryDataToRemove( final ContentVersionEntity existingVersion ) { return existingVersion.getContentBinaryDataKeys(); } private ContentEntity storeNewContent( final ImportDataEntry importData ) { // Content Data final CustomContentData newContentData = new ImportDataEntryParser( relatedContentFinder, category.getContentType().getContentTypeConfig() ).parseToCustomContentData( importData, null ); final List<BinaryDataAndBinary> binaryData = getBinaryData( newContentData ); // Command final CreateContentCommand createCommand = new CreateContentCommand(); createCommand.setCreator( importer ); createCommand.setAccessRightsStrategy( CreateContentCommand.AccessRightsStrategy.INHERIT_FROM_CATEGORY ); createCommand.setContentName( new PrettyPathNameCreator( transliterate ).generatePrettyPathName( newContentData.getTitle() ) ); createCommand.setCategory( category ); final DateTime availableFrom = resolveAvailableFrom( importData ); if ( availableFrom != null ) { createCommand.setAvailableFrom( availableFrom.toDate() ); } final DateTime availableTo = resolveAvailableTo( importData ); if ( availableTo != null ) { createCommand.setAvailableTo( availableTo.toDate() ); } createCommand.setPriority( 0 ); createCommand.setLanguage( category.getLanguage() ); createCommand.setContentData( newContentData ); createCommand.setStatus( importConfig.getStatus().toContentStatus() ); createCommand.setBinaryDatas( binaryData ); createCommand.setUseCommandsBinaryDataToAdd( true ); return contentStorer.createContent( createCommand ); } private List<BinaryDataAndBinary> getBinaryData( final CustomContentData newContentData ) { final List<BinaryDataAndBinary> binaryDatas = new ArrayList<BinaryDataAndBinary>(); for ( final DataEntry entry : newContentData.getEntries() ) { if ( entry instanceof BinaryDataEntry ) { binaryDatas.add( BinaryDataAndBinary.convertFromBinaryEntry( (BinaryDataEntry) entry ) ); } } return binaryDatas; } private DateTime resolveAvailableFrom( final ImportDataEntry importData ) { final DateTime value = getMetadataDateValue( importData, "@publishfrom" ); return value != null ? value : defaultPublishFrom; } private DateTime resolveAvailableTo( final ImportDataEntry importData ) { final DateTime value = getMetadataDateValue( importData, "@publishto" ); return value != null ? value : defaultPublishTo; } private DateTime getMetadataDateValue( final ImportDataEntry importData, final String destination ) { for ( final Map.Entry<CtyImportMappingConfig, AbstractSourceValue> configAndMetadataValue : importData.getConfigAndMetadataValueMap().entrySet() ) { final CtyImportMappingConfig config = configAndMetadataValue.getKey(); if ( config.getDestination().equals( destination ) ) { final Date date = ImportValueFormater.getDate( configAndMetadataValue.getValue(), config.getFormat() ); if ( date == null ) { return null; } return new DateTime( date ); } } return null; } public void setContentDao( final ContentDao value ) { this.contentDao = value; } public void setContentStorer( final ContentStorer value ) { this.contentStorer = value; } public void setRelatedContentFinder( final RelatedContentFinder value ) { this.relatedContentFinder = value; } }