Search code examples
episerver

EPiServer 11: Convert media type programatically


Has anyone attempted to convert a media file type programmatically in EPiServer?

We have a type we modeled for images, ImageFile, and a type that was added via a plugin. We don't use the type added by the plugin but found that images uploaded will, from time to time, be created as the wrong type. I am hoping to figure out how to make the conversion so I can run a scheduled job to batch convert them all then remove the plugin.

So far, I can programmatically make a copy of A as type B then delete A. What I would really like to do is convert A to B for a seamless transition.

First thing I tried is cloning the image as the proper type but that comes up null

var media = _contentRepository.Service.GetDescendents(SiteDefinition.Current.GlobalAssetsRoot)
                .Where(i => _contentRepository.Service.Get<IContent>(i) is MediaData);
var imageFileTypeId = new ImageFile().ContentTypeID;
foreach (var img in media)
{
    if (img.Get<IContent>() is GcEpiImageFile)
    {
         count++ // for summary
         var item = _contentLoader.Service.Get<GcEpiImageFile>(img.Get<IContent>().ContentLink);
         var cloneItem = item.CreateWritableClone() as ImageFile;
         if (cloneItem == null) 
         {
             var msg = $"{item.Name}: {item.ContentLink.ID} came up null";
             OnStatusChanged(msg);
             summaryMessage.AppendLine(msg);
             continue;
         }
         cloneItem.ContentTypeID = imageFileTypeId;
         _contentRepository.Service.Save(cloneItem, SaveAction.Publish, AccessLevel.NoAccess);

    }

Next thing I tried is cloning it as the base class ImageData which seems to create the object but throws a Object reference not set to an instance of an object exception when saving.

var media = _contentRepository.Service.GetDescendents(SiteDefinition.Current.GlobalAssetsRoot)
                .Where(i => _contentRepository.Service.Get<IContent>(i) is MediaData);
var imageFileTypeId = new ImageFile().ContentTypeID;
foreach (var img in media)
{
    if (img.Get<IContent>() is GcEpiImageFile)
    {
         count++ // for summary
         var item = _contentLoader.Service.Get<GcEpiImageFile>(img.Get<IContent>().ContentLink);
         var cloneItem = item.CreateWritableClone() as ImageData;
         if (cloneItem == null) 
         {
             var msg = $"{item.Name}: {item.ContentLink.ID} came up null";
             OnStatusChanged(msg);
             summaryMessage.AppendLine(msg);
             continue;
         }
         try
         { 
             cloneItem.ContentTypeID = imageFileTypeId;
         }
         catch (Exception ex)
         {
             summaryMessage.AppendLine($"exception triggered by id assignment {ex.Message}<br />{ex.InnerException}");
         }
         try
         { 
             _contentRepository.Service.Save(cloneItem, SaveAction.Publish, AccessLevel.NoAccess);
         }
         catch (Exception ex)
         {
             summaryMessage.AppendLine(
                            $"exception triggered by save {ex.Message}<br />{ex.InnerException}");
         }
    }

here is my stack trace:

Object reference not set to an instance of an object.
at EPiServer.Validation.Internal.RoutingSegmentValidator.Validate(IContent instance) at

EPiServer.Validation.Internal.ContextValidatorWrapper`2.Validate(Object instance, Object context) 

at EPiServer.Validation.Internal.ValidationService.ValidateRecursively(Object instance, Object context, HashSet`1 visitedInstances) 

at EPiServer.Validation.Internal.ValidationService.Validate[T](Object instance, T context) 

at EPiServer.Core.ContentProvider.Validate(IContent content, ContentSaveValidationContext saveValidationContext) 

at EPiServer.Core.Internal.DefaultContentRepository.Save(IContent content, SaveAction action, AccessLevel access) at Web.Business.ScheduledJobs.ImageConversion.Execute()

Any thoughts, help, guidance would be greatly appreciated


Solution

  • Hat tip to Johann Kronberg on the EPiServer developer forum for his help.

    BEGIN TRANSACTION [ConvertTransaction]
    BEGIN TRY
      DECLARE @FromPropertyID1 int
      DECLARE @ToPropertyID1 int
      DECLARE @FromPropertyID2 int
      DECLARE @ToPropertyID2 int
      DECLARE @FromPropertyID3 int
      DECLARE @ToPropertyID3 int
    
      DECLARE @RC int
      DECLARE @PageID int
      DECLARE @FromPageType int
      DECLARE @ToPageType int
      DECLARE @Recursive bit
      DECLARE @IsTest bit
    
      SELECT @PageID = 1
    #get your type ids from content type guid
      SELECT @FromPageType = pkID FROM tblContentType WHERE ContentTypeGUID = 'guid of the media type being changed'
      SELECT @ToPageType = pkID FROM tblContentType WHERE ContentTypeGUID = 'guid of the media type it is being change to'
    
    
    #mapping properties THIS IS IMPORTANT!!!
    
      SELECT @FromPropertyID1 = pkID FROM tblPropertyDefinition WHERE fkContentTypeID = @FromPageType AND [Name] = 'Copyright'
      SELECT @FromPropertyID2 = pkID FROM tblPropertyDefinition WHERE fkContentTypeID = @FromPageType AND [Name] = 'Description'
      SELECT @FromPropertyID3 = pkID FROM tblPropertyDefinition WHERE fkContentTypeID = @FromPageType AND [Name] = 'Notes'
    
      SELECT @ToPropertyID1 = pkID FROM tblPropertyDefinition WHERE fkContentTypeID = @ToPageType AND [Name] = 'Copyright'
      SELECT @ToPropertyID2 = pkID FROM tblPropertyDefinition WHERE fkContentTypeID = @ToPageType AND [Name] = 'Alt Text'
      SELECT @ToPropertyID3 = pkID FROM tblPropertyDefinition WHERE fkContentTypeID = @ToPageType AND [Name] = 'Notes'
    
      SET @Recursive = 1
      SET @IsTest = 0
    
      print @PageID
      print @FromPageType
      print @ToPageType
    
      DECLARE @MasterLanguageID int
    
      SET @MasterLanguageID = 8
    
      EXECUTE @RC = [dbo].[netConvertPropertyForPageType]
         @PageID
        ,@FromPageType
        ,@FromPropertyID1
        ,@ToPropertyID1
        ,@Recursive
        ,@MasterLanguageID
        ,@IsTest
    
      print @RC
    
      EXECUTE @RC = [dbo].[netConvertPropertyForPageType]
         @PageID
        ,@FromPageType
        ,@FromPropertyID2
        ,@ToPropertyID2
        ,@Recursive
        ,@MasterLanguageID
        ,@IsTest
    
      print @RC
    
      EXECUTE @RC = [dbo].[netConvertPropertyForPageType]
         @PageID
        ,@FromPageType
        ,@FromPropertyID3
        ,@ToPropertyID3
        ,@Recursive
        ,@MasterLanguageID
        ,@IsTest
    
      print @RC
    
      EXECUTE @RC = [dbo].[netConvertPageType]
         @PageID
        ,@FromPageType
        ,@ToPageType
        ,@Recursive
        ,@IsTest
    
      print @RC
    
      COMMIT TRANSACTION [ConvertTransaction]
    END TRY
    BEGIN CATCH
      ROLLBACK TRANSACTION [ConvertTransaction]
    END CATCH
    
    -- Run separately
    DECLARE @FromPageType int
    SELECT @FromPageType = pkID FROM tblContentType WHERE ContentTypeGUID = '0a89e464-56d4-449f-aea8-2bf774ab8730'
    EXECUTE netContentTypeDelete @ContentTypeID = @FromPageType
    

    Note If your source type has more properties than your destination type you may need to add properties to your destination. In my first run at this, I was able to convert the types but I could not delete the destination type.