Cloning in one form or another has been in existence with the starter sites for a long time. It’s used for a variety of purposes, initially to support the Amendment process then later to support things like Project templates. I’ve even seen it used as a means to generate large volumes of test data. In order for cloning to work, it must respect a certain set of rules. These rules allow the result of the clone (i.e. the copy) to adhere to all the structural and relational rules that define a project. Some examples, include:
- The copy must have a unique ID
- All Data Entry CDTs must be uniquely owned by the project
- Persons, Organizations, and Selection CDTs must not be duplicated as they are considered to be shared resources, referenceable by multiple projects (among other things).
So what does this have to do with the Hybrid CDT approach? Keep in mind that the Hybrid CDT approach uses the default behavior for Data Entry and Selection CDTs in a unique way to take advantage of the native user interface controls, thus avoiding the need to craft a custom UI. To accomplish this, we use two CDTs: A Data Entry CDT that “wraps” a Selection CDT. From a UI perspective this gives us the flexibility that we need. However, from a data persistence perspective we’re actually deviating from the standard definition of a Selection CDT. Rather than the selection CDT entity being a shared resource that can be referenced by multiple entities, we change the rules a bit and only allow the selection CDT to be referenced by the data entry CDT that wraps it. In turn, being a data entry CDT, it can only be “owned” by one project. The combination of the two really represents data that should be managed as a data entry CDT entity.
The clone method in EntityCloner, however, has no idea that we changed the rules in the case of the Hybrid CDT, so it still clones Data Entry CDT entities but does not clone Selection CDT entities. This means that, following a clone the same entity is referenced by both the original and copy. This should not be allowed. To overcome this problem, we need to enhance EntityCloner to be aware of this new special case. The good news is that the enhancement is very simple to implement. The bad news is that it requires a base enhancement. Here’s what you need to do…
Via Entity Manager, edit the EType named EntityCloner. On that type, there is a method named isClonable. This method is called by the clone process to determine if an entity should be cloned. It returns a Boolean where true means clone and false means don’t. In that method, you’ll find the logic that makes this determination. The part we’re interested in looks like this:
ifYou will want to enhance it by adding the highlighted code as shown here
(
entitytype == Document ||
entitytype == WebPage ||
(entitytype.inheritsFrom(CustomDataType) && entitytype._usage == "dataEntry") ||
("isClonable") && entitytype.isClonable()) ||
entitytype.inheritsFrom(CustomAttributesManager) ||
entitytype == HistoricalDocument ||
entitytype == ResourceHistory
)
{
clonable = true;
}
ifThis will cause the method to determine if the Selection cDT entity is to be cloned by asking the entities type rather than just blindly saying “don’t clone”. The next step is to add the isClonable() method as a per-type method to each of the Selection CDTs that exists as one half of a Hybrid CDT. The method you will add looks like this:
(
entitytype == Document ||
entitytype == WebPage ||
(entitytype.inheritsFrom(CustomDataType) && entitytype._usage == "dataEntry") ||
(entitytype.inheritsFrom(CustomDataType) && entitytype._usage == "selection") &&
(entitytype.hasTypeMethodNamed("isClonable") && entitytype.isClonable()) ||
entitytype.inheritsFrom(CustomAttributesManager) ||
entitytype == HistoricalDocument ||
entitytype == ResourceHistory
)
{
clonable = true;
}
function isClonable()As EntityCloner is a base Extranet type, you will need to track this change in case you need to reapply it after a base Extranet upgrade but, once implemented, you can safely clone projects which leverage the Hybrid CDT technique.
{
try {
// this selection CDT is used as part of a Hybrid CDT pair so entities of this type need to be cloned.
return true;
}
catch (e) {
wom.log("EXCEPTION _YourSelectionCDT.isClonable: " + e.description);
throw(e);
}
}
Cheers!
Hi Tom - when I look at that method on my store (running 5.7.1). I don't see one of the lines you're referencing. Is it still safe to replace that block with your new code? Any chance that this will be incorporated into base in the future?
ReplyDeleteMy method looks like this:
if
(
entitytype == Document ||
entitytype == WebPage ||
(entitytype.inheritsFrom(CustomDataType) && entitytype._usage == "dataEntry") ||
entitytype.inheritsFrom(CustomAttributesManager) ||
entitytype == HistoricalDocument ||
entitytype == ResourceHistory
)
{
clonable = true;
}
Scott,
ReplyDeleteThis looks fine. You should be able to apply the suggested changes without problem.
As for including this in base, I've got some good news. Extranet 5.8 includes several performance improvements to clone functionality. These changes involve a number of changes to the provided methods. This includes a complete replacement to the code modified above. The new code supports the ability to implement a method on the Selection CDT that will, if it exists, automatically be called by the standard clone code. This means that as of Extranet 5.8 a custom base change will no longer be required. I intend to post a revised implementation as we get closer to the release of 5.8.