I have solved pretty much the exact same problem using this SQL script. You need to modify it to fit your problem and accept there is a high amount of risk involved.
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
SELECT @FromPageType = pkID FROM tblContentType WHERE ContentTypeGUID = '0a89e464-56d4-449f-aea8-2bf774ab8730'
SELECT @ToPageType = pkID FROM tblContentType WHERE ContentTypeGUID = '40ed0421-2d37-4751-97dc-c7ea7033c3a5'
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] = 'MediumThumbnail'
SELECT @ToPropertyID1 = pkID FROM tblPropertyDefinition WHERE fkContentTypeID = @ToPageType AND [Name] = 'Copyright'
SELECT @ToPropertyID2 = pkID FROM tblPropertyDefinition WHERE fkContentTypeID = @ToPageType AND [Name] = 'Description'
SELECT @ToPropertyID3 = pkID FROM tblPropertyDefinition WHERE fkContentTypeID = @ToPageType AND [Name] = 'MediumThumbnail'
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
Has anyone had luck doing this using code only?
I may find myself going the DB route but I am hoping to avoid that if possible.
I think you can workaround by skipping the validation - it's probably not worth doing in this case. You can use
SaveAction.Publish | SaveAction.SkipValidation
I had to do somehing similar for PDFS, my scheduled jon did utilise SQL as Johan suggested, the code is below and believe I found it somewhere in these forums
[ScheduledPlugIn(DisplayName = "Convert PDF Files", GUID = "d6619008-3e76-4886-b3c7-9a025a0c2603")]
public class FixPdfMediaType : ScheduledJobBase
{
private readonly IContentRepository ContentRepository;
private readonly IContentTypeRepository ContentTypeRepository;
private readonly ContentMediaResolver MediaDataResolver;
public FixPdfMediaType()
{
ContentRepository = ServiceLocator.Current.GetInstance<IContentRepository>();
ContentTypeRepository = ServiceLocator.Current.GetInstance<IContentTypeRepository>();
MediaDataResolver = ServiceLocator.Current.GetInstance<ContentMediaResolver>();
}
private List<BasicContent> GetPdfs()
{
var pdfs = new List<BasicContent>();
var assets = ContentRepository.GetDescendents(SiteDefinition.Current.GlobalAssetsRoot);
OnStatusChanged("Found " + assets.Count() + " assets");
foreach (var asset in assets)
{
BasicContent pdf = null;
if (ContentRepository.TryGet(asset, out pdf) && pdf.Name.EndsWith(".pdf"))
{
pdfs.Add(pdf);
}
}
return pdfs;
}
public override string Execute()
{
var pdfs = GetPdfs();
OnStatusChanged("Handling " + pdfs.Count() + " PDF files");
var output = new StringBuilder();
int i = 0;
//Get a suitable MediaData type from extension
var mediaType = MediaDataResolver.GetFirstMatching(".pdf");
var contentType = ContentTypeRepository.Load(mediaType);
foreach (var pdf in pdfs)
{
if (pdf.ContentTypeID != contentType.ID)
{
OnStatusChanged("Changing type for " + pdf.Name);
try
{
using (SqlConnection conn =
new SqlConnection(ConfigurationManager.ConnectionStrings["EPiServerDB"].ConnectionString))
{
conn.Open();
using (SqlCommand cmd =
new SqlCommand("UPDATE tblPage SET fkPageTypeID=@ContentTypeID WHERE PkId = @ID", conn))
{
cmd.Parameters.AddWithValue("@ContentTypeID", contentType.ID);
cmd.Parameters.AddWithValue("@ID", pdf.ContentLink.ID);
cmd.ExecuteNonQuery();
i++;
}
}
}
catch (Exception ex)
{
output.AppendLine(string.Format("ID: {0}, Error: {1}",
pdf.ContentLink.ID,
ex.Message));
}
}
}
return string.Format(
"Fixed {0} pdfs. \n Errors:\n{1}",
i,
output);
}
}
}
For the pages admin tool DefaultPageTypeConverter is used for coverting pages which is just IContent so very similar. If you look under the covers the following happens
private DataTable ConvertPageTypeProperties(
int pageLinkId,
int fromPageTypeId,
List<KeyValuePair<int, int>> propertyTypeMap,
bool recursive,
bool isTest)
{
DataTable dataTable = new DataTable("Properties");
dataTable.Locale = CultureInfo.InvariantCulture;
dataTable.Columns.Add("FromPropertyID");
dataTable.Columns.Add("ToPropertyID");
dataTable.Columns.Add("Count");
int id = this._languageBranchRepository.Load(this._contentRepository.Get<PageData>(new ContentReference(pageLinkId)).MasterLanguage).ID;
foreach (KeyValuePair<int, int> propertyType in propertyTypeMap)
{
DbCommand command = this.CreateCommand("netConvertPropertyForPageType");
command.Parameters.Add((object) this.CreateReturnParameter());
command.Parameters.Add((object) this.CreateParameter("PageID", (object) pageLinkId));
command.Parameters.Add((object) this.CreateParameter("FromPageType", (object) fromPageTypeId));
command.Parameters.Add((object) this.CreateParameter("FromPropertyID", (object) propertyType.Key));
command.Parameters.Add((object) this.CreateParameter("ToPropertyID", (object) propertyType.Value));
command.Parameters.Add((object) this.CreateParameter("Recursive", (object) recursive));
command.Parameters.Add((object) this.CreateParameter("MasterLanguageID", (object) id));
command.Parameters.Add((object) this.CreateParameter("IsTest", (object) isTest));
command.ExecuteNonQuery();
DataRow row = dataTable.NewRow();
row[0] = (object) propertyType.Key;
row[1] = (object) propertyType.Value;
row[2] = (object) this.GetReturnValue(command);
dataTable.Rows.Add(row);
if (this._propertyDefinitionRepository.Load(propertyType.Key).Type.DataType == PropertyDataType.Category)
{
command.CommandText = "netConvertCategoryPropertyForPageType";
command.ExecuteNonQuery();
}
}
return dataTable;
}
private DataTable ConvertPageType(
int pageLinkId,
int fromPageTypeId,
int toPageTypeId,
bool recursive,
bool isTest)
{
DataTable dataTable = new DataTable("Pages");
dataTable.Locale = CultureInfo.InvariantCulture;
dataTable.Columns.Add("Count");
DbCommand command = this.CreateCommand("netConvertPageType");
command.Parameters.Add((object) this.CreateReturnParameter());
command.Parameters.Add((object) this.CreateParameter("PageID", (object) pageLinkId));
command.Parameters.Add((object) this.CreateParameter("FromPageType", (object) fromPageTypeId));
command.Parameters.Add((object) this.CreateParameter("ToPageType", (object) toPageTypeId));
command.Parameters.Add((object) this.CreateParameter("Recursive", (object) recursive));
command.Parameters.Add((object) this.CreateParameter("IsTest", (object) isTest));
command.ExecuteNonQuery();
DataRow row = dataTable.NewRow();
row["Count"] = (object) this.GetReturnValue(command);
dataTable.Rows.Add(row);
return dataTable;
}
This suggest using the database is the correct approach. I would suggest seeing if you can alter this code/stored procedures to create a more generic converter helper. But using SQL such as how Johan has suggested would likely be the best approach.
Testing out Johan's SQL I found the first part makes the conversion successfully but the second part fails with the following:
The DELETE statement conflicted with the REFERENCE constraint "FK_tblWorkContentProperty_tblPropertyDefinition". The conflict occurred in table "dbo.tblWorkContentProperty", column 'fkPropertyDefinitionID'.
I am digging into it further but I am wondering if Johan, or anyone else who has tried something similar, ran into this issue.
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
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.
Any thoughts, help, guidance would be greatly appreciated