/*
* 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;
}
}