When listing instances of a custom content item type, you will see that next to the Preview, Publish, Edit and Delete operations, there is also a Clone operation.
When using it on a custom content item, you will also see that it’s merely creating an empty content item, not taking over any of the actual data.
Unfortunately, in all tutorials about custom content items I have read, I haven’t seen anything pointing to how to make Orchard actually create a real copy of the selected content item.
It’s actually pretty simple. The ContentPartDriver class your driver is extending, contains a bunch of methods for importing and exporting:
protected virtual void Importing(TContent part, ImportContentContext context) {}
protected virtual void Imported(TContent part, ImportContentContext context) {}
protected virtual void Exporting(TContent part, ExportContentContext context) {}
protected virtual void Exported(TContent part, ExportContentContext context) {}
All you have to do is override the methods Importing and Exporting in your driver:
protected override void Importing(MyCustomQueryPart part, ImportContentContext context)
{
}
protected override void Exporting(MyCustomQueryPart part, ExportContentContext context)
{
}
Exporting will be called when the content part is exported. You have to map the properties from the content part to XML attributes.
Importing will be called when importing a content part. You have to map back the XML attributes to content part properties.
For example:
protected override void Importing(MyCustomQueryPart part, ImportContentContext context)
{
string name = context.Attribute(part.PartDefinition.Name, "Name");
if (name != null)
{
part.Name = name;
}
}
protected override void Exporting(MyCustomQueryPart part, ExportContentContext context)
{
context.Element(part.PartDefinition.Name).SetAttributeValue("Name", part.Name);
}
Note that if your properties are not strings you’ll have to convert them in the importing using e.g. bool.Parse or int.Parse e.g.:
protected override void Importing(MyCustomQueryPart part, ImportContentContext context)
{
part.IsOk= bool.Parse(context.Attribute(part.PartDefinition.Name, "IsOk"));
}
In the exporter, the conversion to strings is done automatically using ToString().
That’s it. Now your content part can be imported and exported and it can be cloned as well !
Now if one of the properties is actually a reference to a different content part, you’ll have to map in the importer the id to a content part e.g.:
int referenceId = int.Parse(context.Attribute(part.PartDefinition.Name, "Reference"));
part.Reference = _otherContentService.GetOtherContentPart(referenceId);
And you of course need to inject the service in the constructor of your driver:
private readonly ILinqFileService _fileService;
public MyCustomDriver(IOtherContentService otherContentService)
{
_otherContentService = otherContentService;
}
And define GetOtherContentPart in IOtherContentService and implement it in the service itself e.g.:
public ContentItem GetOtherContentPart(int referenceId)
{
return _contentManager.Get(referenceId, VersionOptions.AllVersions);
}
_contentManager is also injected in the Service by defining it in the constructor:
private readonly IContentManager _contentManager;
public OtherContentService(IContentManager contentManager)
{
_contentManager = contentManager;
}
It’s a little bit more complex but I guess you’ll need the GetOtherContentPart method for other purposes anyway (I already had it in there).
So the conclusion is that Orchard provides pretty much everything you need in a nicely designed way but unfortunately the lack of documentation makes it very difficult to figure out how to use it. In such cases (as always with Orchard) your best starting point is the source code of existing modules and search engines (although Orchard being relatively new, you will not find everything in search engines).
One thought on “Orchard CMS: Cloning custom content items”