I know that this is an old post, but I was looking through and just wanted to add some information to help anyone who is actually still hoping to implement similar behavior to this.
At a high-level, @MarkCassidy is correct that there is no OOTB mechanism for what you are describing. However, you could achieve a limited version of this functionality by leveraging eventing or the item provider pipelines, combined with a little extra logic.
Lingo
- Branch = Branch
- Implementor = subtree created from branch
- Corresponding Item = item in implementor that corresponds to item in branch
- Corresponding Field = field of corresponding item in implementor corresponding to field of item in branch
Functionality to Implement
Before we start coding, let's consider the functionality that we can and plan to implement:
- Item in branch has field or presentation change -> change applied to corresponding item in implementors of the branch
- If corresponding item doesn't exist in implementor then no action is taken
- If a field is changed and the original value of the field does not match the value of the corresponding field in the implementor then no action is taken (do not overwrite changes)
- Item renames should not be applied to implementors
The above logic includes both the Renderings
and Final Renderings
fields (Shared Layout and Final Layout), so long as no changes have been made to either.
For simplicity, we will skip the following enhancements, but they could be added with limited additional effort:
- Field comparison supports token resolution (e.g.
$name
)
- Deleted item support (would require an additional event-handler; see below for more)
- Rendering property change detected by reviewing content of deltas
- Comparison of deltas in corresponding item to allow applying changes even after corresponding item's presentation details have been changed (this one would likely take more than a little effort, but it is still doable, with limitations)
Calling our Logic
Before we get into specifics, let's figure out how we will be calling this logic. For starters, we know that we want the logic to run when a branch template is "changed." We can detect that a branch template has "changed" by listening for when the branch template item or one of its descendants has been saved. However, note that it is possible that the branch template or one of its descendants will be saved without any actual changes. This extra call to our logic should not result in any side-effects.
Listening for Changes
Now that we know that we want to listen for the branch items or one of its descendants to be saved, we can pick the "listening" mechanism. This is a crucial step, as the information that we have about the item being changed will vary based on the mechanism that we choose. For example, the item:saved
event will show us the changed item, but we won't be able to compare it with its previous state (i.e. before it was changed).
There is really only one good candidate for listening mechanisms in this case: the item:saving
event.
The item:saving
Event
The item:saving
event has been around for a long time and its use is fairly well-documented online. The item:saving
event is viable for our use-case because while one of its parameters is an updated "copy" of the item being saved (i.e. an item that looks like the unmodified item, except that it has all of the changes that were made and are being saved), yet the changes haven't actually been saved. This means that we have access to both of the following:
- The "updated item", and thus the changes, by looking at the item retrieved from the event's parameters
- The unmodified item, and thus a point of comparison, by retrieving the item from the database using the same ID as the "updated item" (remember that the updated item hasn't been saved yet)
While this is all well and good, there are two major drawbacks of using the item:saving
handler for our use-case:
- In order to get the unmodified item for comparison, we need to retrieve the item from the database by performing an extra item-get (yuck, but not the end of the world)
- In order to actually see the changes that were made, all of the fields of the updated item will need to be compared with the unmodified item and any that differ can be considered "modified" (BIG yuck!)
The good news, is that - as previously mentioned - using this event has been pretty well-documented and should be pretty straightforward.
The only change that this handler will not support is if an item is deleted/archived. In this case, we would need to add an additional handler to support accounting for those changes. Once our initial logic is written, that extra handler will be pretty straightforward, so I will skip that functionality for the purpose of this post.
The Code/Logic
Obviously, this is a pretty complex piece of functionality, so I'm not actually going to code it here (being lazy :P), but I will walk you through the high-level of what you will want to do:
- Set up your event handler to run the rest of your logic and configure it to fire on
item:saving
.
- When hit, handler first checks to see if the item is or is part of a branch template and returns if it is not
- Retrieve the unmodified version of the item from the database and compare the fields to create a
Dictionary<KeyValuePair<string, string>, string>
with the KeyValuePair<FieldName, OriginalValue>
as a key and ModifiedValue
as the value. This will be the changesDictionary
If the populated changesDictionary
is empty, then return.
- Find all of the items created from that item's branch (links database, content search, etc)
- For each of the returned items, select the corresponding item to the item in the branch that was changed. If the corresponding item is missing, return null
- Filter out null items from the list (i.e. filter out implementors that are missing the corresponding item)
- For each
Key
in the changesDictionary
iterate through the remaining items and if the value of the Key.Key
field (corresponding field) is the same as the Key.Value
(unmodified branch field value) then update the field to the changesDictionary[Key]
(modified value) inside of an EventDisabler
(for performance reasons; not to avoid side-effects)
While this will work, there is a fairly large risk of performance degradation when saving branch items. Fortunately, this isn't something that happens often after initial development.