Working copies - customize with XSLT
Handle the whole process of creating and releasing working copies using XSLT, according to the features and relations that you define. A sample XSLT walk-through.
Context
Provide and explain the XSLT for configuring the update process of already released content. There is always a pair of XSLT files needed:
XSLT for creating the working copy
XSLT for releasing the working copy
You can create configurations according to a specific asset type.
Prerequisites
None
XSLT: Create working copy
Use the following sample XSLT to handle the whole process of creating an asset type-specific working copy. You basically only need to specify the asset type, MIME type and the default, and type-specific features and relations that should be copied or excluded from copying. Other than that, you do not need to adapt the XSLT.
The XSLT checks for any existing working copy of the original asset ID, identifies the original parent asset and the working copy suffix set in the user preferences.
<!-- global variables -->
<xsl:variable name="check-for-existing-working-copy" select="true()"/>
<xsl:param name="original-asset"/>
<xsl:param name="asset-suffix"/>
Define the asset type such as "picture." and the type-specific features that should be copied during the creation of the working copy. The name of the working copy is created and concatenated with the given suffix.
<!-- configuration -->
<xsl:variable name="additional-attributes-mapping">
<mapping type="???">
<!-- type-specific attributes can be declared here(starts-with-match on mapping/@type) -->
</mapping>
<!-- default attributes are applied in addition to available type-specific attributes -->
<default-mapping>
<attributes name="=concat(@name, '', $asset-suffix)" />
</default-mapping>
</xsl:variable>
Define the relations that should be mapped to the working copy. By default, a parent_asset_rel of type user.working.copy is created between the working copy and the original asset. You can also define relations specific to certain asset types like above for features.
<!-- default and type-specific relations to add to duplicated asset -->
<xsl:variable name="additional-elements-mapping">
<mapping/>
<default-mapping>
<parent_asset_rel parent_asset="{$original-asset/@id}" key="user.working-copy." />
</default-mapping>
</xsl:variable>
Define relations to be excluded. Mappings works similarly as in "additional-attributes-mapping". Some are excluded by default.
<!-- default and type-specific relations to omit for duplicated asset -->
<xsl:variable name="exclude-elements-mapping">
<mapping/>
<default-mapping>
<asset_feature feature="censhare:asset.released"/>
<asset_feature feature="censhare:download-hash"/>
<storage_item/>
<message/>
<asset_info/>
<parent_asset_rel/>
<child_asset_rel/>
</default-mapping>
</xsl:variable>
Check whether the parent asset does have any other working copy children attached to it. If not, create the working copy by simply duplicating the original asset with the "additional-attributes-mapping","additional-elements-mapping" and "exclude-elements-mapping" that were set above. Depending on provided parameters, a different duplicate function is called.
<!-- main loop -->
<xsl:template match="/">
<xsl:if test="not(exists($original-asset/child_asset_rel[@key='user.working-copy.']) and $check-for-existing-working-copy)">
<result>
<xsl:attribute name="working-copy-id">
<xsl:copy-of select="csc:duplicate($original-asset, $additional-attributes-mapping, $additional-elements-mapping, $exclude-elements-mapping)" />
</xsl:attribute>
</result>
</xsl:if>
</xsl:template>
<!-- duplication function (call with asset to duplicate only) -->
<xsl:function name="csc:duplicate">
<xsl:param name="master-asset" />
<xsl:copy-of select="csc:duplicate($master-asset, (), (), ())"/>
<!-- duplication function (call with asset to duplicate and attributes to add) -->
<xsl:function name="csc:duplicate">
<xsl:param name="master-asset" />
<xsl:param name="additional-attributes" />
<xsl:copy-of select="csc:duplicate($master-asset, $additional-attributes, (), ())"/>
</xsl:function>
<!-- duplication function (call with asset to duplicate, attributes to add and elements to add or omit) -->
<xsl:function name="csc:duplicate">
<xsl:param name="master-asset" />
<xsl:param name="additional-attributes" />
<xsl:param name="additional-elements" />
<xsl:param name="exclude-elements" />
<xsl:variable name="original-type" select="$master-asset/@type" />
<xsl:variable name="cleaned-and-cloned-master-asset">
<cs:command name="com.censhare.api.assetmanagement.CloneAndCleanAssetXml">
<cs:param name="source" select="$master-asset"/>
</cs:command>
</xsl:variable>
<xsl:variable name="duplicate-asset">
<asset>
<xsl:copy-of select="$cleaned-and-cloned-master-asset/asset/@*"/>
<!--make sure this asset points to its original --->
<xsl:attribute name="id_org" select="$master-asset/@id"/>
<!--add/overwrite default attributes --->
<xsl:for-each select="$additional-attributes/default-mapping/attributes/@*">
<xsl:apply-templates select="." mode="create-attribute">
<xsl:with-param name="asset" select="$cleaned-and-cloned-master-asset"/>
</xsl:apply-templates>
</xsl:for-each>
<!--add other configured attributes for given types -->
<xsl:for-each select="$additional-attributes/mapping[starts-with($original-type,@type)]/attributes/@*">
<xsl:apply-templates select="." mode="coreate-attribute">
<xsl:with-param name="asset" select="$cleaned-and-cloned-master-asset"/>
</xsl:apply-templates>
</xsl:for-each>
<!--add default elements -->
<xsl:copy-of select="$additional-elements/default-mapping/node()"/>
<!-- add other configured elements for give types -->
<xsl:copy-of select="$additional-elements/mapping[starts-with($original-type,@type)]/node()"/>
<!-- only copy elements not configured for exclusion -->
<!-- first create merge elements to exclude -->
<xsl:variable name="this-exclude-elements-mapping">
<xsl:copy-of select="$exclude-elements-mapping/mapping[starts-with
($original-type,@type)]" />
<xsl:copy-of select="$exclude-elements-mapping/default-mapping" />
</xsl:variable>
<!--copy all nodes except asset elements, asset element relations and other elements as configured in $exclude-elements-mapping -->
<xsl:for-each select="$cleaned-and-cloned-master-asset/asset/node()[not(local-name()=('asset_element', 'child_asset_element_rel', 'parent_asset_element_rel', 'child_asset_rel'))]">
<xsl:variable name="omit-element" select="csc:match-exclude-elements(current(),$this-exclude-elements-mapping)" />
<xsl:if test="not($omit-element='true')">
<xsl:copy-of select="current()" />
</xsl:if>
</xsl:for-each>
<!--copy any child asset rel except placements and omitting those excluded by purpose in $exclude-elements-mapping -->
<xsl:for-each select="$cleaned-and-cloned-master-asset/asset/child_asset_rel[not(@key='actual.')]">
<xsl:variable name="omit-element" select="csc:match-exclude-elements(current(), $this-exclude-elements-mapping)" />
<xsl:if test="not($omit-element='true')">
<xsl:copy-of select="current()" />
</xsl:if>
</xsl:for-each>
<!-- handle storage items, asset elements, asset element relations and placements (actual child asset relations) -->
<xsl:copy-of select="$cleaned-and-cloned-master-asset/asset/storage_item"/>
<xsl:copy-of select="$cleaned-and-cloned-master-asset/asset/child_asset_rel[@key='actual.']"/>
<xsl:copy-of select="$cleaned-and-cloned-master-asset/asset/child_asset_element_rel[@key='actual.']"/>
<!-- make sure planning elements exist and synchronize with actual structure -->
<xsl:variable name="root-actual-asset-element" select="$cleaned-and-cloned-master-asset/asset/asset_element[@key='actual.' and not(@parent_idx)]"/>
<xsl:variable name="actual-page-asset-elements" select="$cleaned-and-cloned-master-asset/asset/asset_element[@key='actual.' and @parent_idx=$root-actual-asset-element/@idx]"/>
<xsl:variable name="new-page-asset-elements">
<!--- root elements - -->
<asset_element asset_id="{$cleaned-and-cloned-master-asset/asset/@id}" asset_currversion="0" idx="0" key="target." isversioned="0" version="0" deletion="0" />
<xsl:for-each select="$actual-page-asset-elements">
<xsl:sort select="number(@paging)"/>
<!--- planning element - -->
<asset_element asset_id="{$cleaned-and-cloned-master-asset/asset/@id}" asset_currversion="0" idx="{position()}" key="target." isversioned="0" parent_idx="0" version="0" deletion="0" width_mm="{@width_mm}" height_mm="{@height_mm}" />
<!--keep and extend the existing actual element -->
<asset_element target_idx="{position()}">
<xsl:copy-of select="./@*"/>
</asset_element>
</xsl:for-each>
</xsl:variable>
<!-- copy root and page asset elements -->
<xsl:copy-of select="$new-page-asset-elements"/>
<!-- copy other asset elements (non-pages) -->
<xsl:copy-of select="$cleaned-and-cloned-master-asset/asset/asset_element[@key='actual.' and not(@parent_idx=$root-actual-asset-element/@idx)]"/>
</asset>
</xsl:variable>
<!-- <xsl:copy-of select="$duplicate-asset" /> -->
<xsl:copy-of select="csc:checkin-new($duplicate-asset)" />
</xsl:function>
<xsl:function name="csc:match-exclude-elements">
<xsl:param name="element"/>
<xsl:param name="exclude-elements"/>
<xsl:variable name="result">
<xsl:for-each select="$exclude-elements/default-mapping/node()[local-name()=$element/local-name()]">
<xsl:choose>
<!-- globally excluded elements -->
<xsl:when test="current()/local-name()=$element/local-name() and count(current()/@*)=0">
<xsl:value-of select="true()"/>
</xsl:when>
<!-- elements with attributes gets checked in detail -->
<xsl:when test="current()/local-name()=$element/local-name()">
<xsl:variable name="attribute-checks">
<xsl:for-each select="current()/@*">
<xsl:value-of select="current()=$element/@*[local-name()=current()/local-name()]"/>
</xsl:for-each>
</xsl:variable>
<!-- all attributes need to match with 'true' to exclude this element -->
<xsl:value-of select="not(contains($attribute-checks,'false'))"/>
</xsl:when>
<!-- no matching exclude rule found -->
<xsl:otherwise>
<xsl:value-of select="false()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<!-- at least one test resulted with true, so omit this element -->
<xsl:value-of select="contains($result,'true')"/>
</xsl:function>
<!--resolve attributes -->
<xsl:template match="@*" mode="create-attribute">
<xsl:param name="asset"/>
<xsl:attribute name="{local-name()}">
<xsl:choose>
<xsl:when test=" starts-with(., '=')">
<xsl:apply-templates select="$asset" mode="create-attribute">
<xsl:with-param name="expression" select="substring-after(.,'=')" />
<xsl:with-param name="master-layout" select="asset"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:template>
<!-- evaluate expression in right context -->
<xsl:template match="asset" mode="create-attribute">
<xsl:param name="expression"/>
<xsl:param name="master-layout"/>
<xsl:value-of select="cs:evaluate($expression)"/>
</xsl:template>
<!-- common functions -->
<xsl:function name="csc:checkin-new">
<xsl:param name="asset-xml"/>
<xsl:variable name="created-asset">
<cs:command name="com.censhare.api.assetmanagement.checkInNew" returning="workingCopyAsset">
<cs:param name="source" select="$asset-xml"/>
</cs:command>
<temp-asset id="{$workingCopyAsset/@id}"/>
</xsl:variable>
<xsl:copy-of select="$created-asset/temp-asset/@id"/>
</xsl:function>
</xsl:stylesheet>
XSLT: Overwrite parent asset with working copy
Use the following sample XSLT to handle the whole process of merging the working copy with the released asset.
<!-- global variables -->
<xsl:variable name="log-prefix" select="'csc xsl-mapper: '" />
<xsl:variable name="detailed-debug" select="true()"/>
<xsl:param name="working-copy-asset"/>
<xsl:param name="original-asset"/>
<!-- - map for collecting data to synchronize from working copy to original - -->
<xsl:variable name="metadata-sync-map">
<mapping>
</mapping>
</xsl:variable>
<!-- - this variable may be used to inject context data into the mapping process - -->
<xsl:variable name="context"/>
The XSLT checks whether the working copy and the original asset exist. If both exist, it runs the "csc:resolve-working-copy" function. This function compares both assets and copies all relevant features and relations to the original asset and overwrites/updates the storage items. Finally, the working copy is deleted.
<!-- main loop -->
<xsl:template match="/">
<xsl:if test="exists($working-copy-asset and$original-asset)">
<xsl:copy-of select="csc:resolve-working-copy()" />
</xsl:if>
</xsl:template>
<!-- sync configured features to original, replace original's file with working copy and delete working copy asset physically -->
<xsl:function name="csc:resolve-working-copy">
<xsl:variable name="working-copy-feature-set" select="csc:normalize-values(csc:map-elements-set($working-copy-asset,(), $metadata-sync-map/mapping))" />
<xsl:variable name="original-feature-set" select="csc:normalize-values(csc:extract-defined-elements-of-asset($original-asset, $metadata-sync-map/mapping))" />
<xsl:variable name="element-sets-diff" select="csc:normalize-values(csc:diff-element-sets($working-copy-feature-set, $original-feature-set))" />
<!-- commit updates and removes -->
<xsl:variable name="updated-asset-xml" select="csc:apply-diffs($original-asset,$element-sets-diff)"/>
<xsl:if test="$detailed-debug">
<xsl:message> <xsl:value-of select="$log-prefix"/>master <xsl:copy-of select="$working-copy-feature-set" /></xsl:message>
<xsl:message><xsl:value-of select="$log-prefix"/>slave <xsl:copy-of select="$original-feature-set" />
</xsl:message>
<xsl:message><xsl:value-of select="$log-prefix"/>diffs <xsl:copy-of select="$element-sets-diff"/>
</xsl:message>
<xsl:message><xsl:value-of select="$log-prefix"/>updated <xsl:copy-of select="$updated-asset-xml"/>
</xsl:message>
</xsl:if>
<!-- add element structure and storage items of working copy and remove working copy relations -->
<xsl:variabl name="new-original-asset-xml"><asset>
<xsl:copy-of select="$updated-asset-xml/@*" />
<xsl:choose>
<xsl:when test="$original-asset/storage_item[@key='master']/@hashcode=$working-copy-asset/storage_item[@key='master']/@hashcode">
<!-- no change of storage items needed, only omit working copy relation -->
<xsl:copy-of select="$updated-asset-xml/node()[not(local-name()='parent_asset_rel'and @key='user.working-copy.')]"/>
</xsl:when>
<xsl:otherwise>
<!-- omit working copy relation and replace storage items -->
<xsl:copy-of select="$updated-asset-xml/node()[not(local-name()='parent_asset_rel' and @key='user.working-copy.') and not(local-name()=('storage_item'))]"/>
<xsl:for-each select="$working-copy-asset/storage_item">
<xsl:variable name="replaced-file" select="concat('censhare:///service/filesystem/', @filesys_name, '/', replace(@relpath, 'file:', "))" />
<storage_item asset_id="{$original-asset/@id}" asset_version="{$original-asset/@version}" corpus:asset-temp-file-url="{$replaced-file}">
<xsl:copy-of select="@*[local-name()=('element_idx', 'key', 'mimetype', 'asset_version')]" />
</storage_item>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</asset>
</xsl:variable>
<!--- update refined original asset - -->
<xsl:copy-of select="csc:update($new-original-asset-xml)"/>
<!--- physically delete asset - -->
<xsl:copy-of select="csc:delete($working-copy-asset, 'physical')"/>
</xsl:function>
<!-- this variable contains all features that wrap an asset attribute (e.g., a field of the asset table).
If the feature's attribute mapping field does not point to the fields name, an exception is defined,
e.g. for the language the mapping field contains an
XPATH: :(@language, asset_feature[@feature='censhare:additional-language']/@value_string) -->
<xsl:variable name="feature-wrapped-attributes">
<xsl:for-each select="cs:master-data('feature')[@type='censhare:attribute' and @storage=2 and @target_object_type='asset']">
<xsl:choose>
<xsl:when test="@key='censhare:asset.language'">
<feature key="censhare:asset.language" attribute_mapping="@language" value_type="{@value_type}"/>
</xsl:when>
<xsl:otherwise>
<feature key="{@key}" attribute_mapping="{@attribute_mapping}" value_type="{@value_type}"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<!-- create an element set of differences between master and slave data -->
<xsl:function name="csc:diff-element-sets">
<xsl:param name="master-element-set"/>
<xsl:param name="slave-element-set"/>
<xsl:variable name="temp-element-sets-diff">
<maintain>
<xsl:copy-of select="for $element in$master-element-set/node() return if ($slave-element-set[deep-equal(.,$element)]) then $element else ()"/>
</maintain>
<add>
<xsl:copy-of select="for $element in$master-element-set/node() return if ($slave-element-set[deep-equal(.,$element)]) then () else $element"/>
</add>
<remove>
<xsl:copy-of select="for $element in$slave-element-set return if ($master-element-set/node()[deep-equal(.,$element)]) then () else $element"/>
</remove>
</xsl:variable>
<!--- respect do-update: don't allow unwanted updates - -->
<xsl:variable name="overwrites" select="$temp-element-sets-diff/add/asset_feature[@do-update='false']" />
<xsl:variable name="changes-due-to-update-policy">
<changes>
<xsl:for-each select="$overwrites">
<!-- check if a suitable value exists in remove set -->
<xsl:variable name="attr-name" select="./@*[starts-with(local-name(),'value')][1]/local-name()" />
<xsl:variable name="feature-key" select="./@feature" />
<xsl:choose>
<!-- new value not set before -->
<xsl:when test="$temp-element-sets-diff/remove/asset_feature[@feature=$feature-key and @*[local-name()=$attr-name]=''] or not($temp-element-sets-diff/remove/asset_feature[@feature=$feature-key])">
<add-to-add>
<asset_feature>
<xsl:copy-of select="@*[not(local-name()='do-update')]" copy-namespace="no"/>
</asset_feature>
</add-to-add>
<remove-from-remove>
<xsl:copy-of select="$temp-element-sets-diff/remove/asset_feature[@feature=$feature-keyand @*[local-name()=$attr-name]='']" />;
</remove-from-remove>
</xsl:when>
<xsl:otherwise>
<!-- value has been set before, maintain existing one -->
<remove-from-add>
<xsl:copy-of select="." copy-namespace="no"/>
</remove-from-add>
<add-to-maintain>
<xsl:copy-of select="$temp-element-sets-diff/remove/asset_feature[@feature=$feature-key]" />
</add-to-maintain>
<remove-from-remove>
<xsl:copy-of select="$temp-element-sets-diff/remove/asset_feature[@feature=$feature-key]" />
</remove-from-remove>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</changes>
</xsl:variable>
<xsl:variable name="element-sets-diff">
<maintain>
<xsl:copy-of select="$temp-element-sets-diff/maintain/node()" copy-namespace="no"/>
<xsl:copy-of select="$changes-due-to-update-policy/changes/add-to-maintain/node()" copy-namespace="no"/>
</maintain>
<add>
<xsl:copy-of select="for $element in$temp-element-sets-diff/add/node() return if ($changes-due-to-update-policy/changes/remove-from-add/node()[deep-equal(.,$element)]) then () else $element"/>
</add>
<remove>
<xsl:copy-of select="for $element in$temp-element-sets-diff/remove/node()[not(@force-add='true')] return if($changes-due-to-update-policy/changes/remove-from-remove/node()[deep-equal(.,$element)]) then () else $element"/>
</remove>
</xsl:variable>
<xsl:copy-of select="$element-sets-diff"/>
</xsl:function>
<!-- construct new asset xml by applying changes of an element set of diffs -->
<xsl:function name="csc:apply-diffs" as="element(asset)">
<xsl:param name="asset-xml"/>
<xsl:param name="element-sets-diff"/>
<xsl:message>test:1233 <xsl:copy-of select="$asset-xml"/></xsl:message>
<xsl:variable name="updated-asset" as="element(asset)">
<asset>
<!--mark asset updated -->
<xsl:if test="$element-sets-diff/add/node() or $element-sets-diff/remove/node()">
<xsl:attribute name="updated" select="true()"/>
</xsl:if>
<!-- handle asset attributes -->
<!-- <xsl:variable name="added-asset-attributes" select="for $mapping in cs:master-data('feature')[($element-sets-diff/add/asset_feature[@feature=$feature-wrapped-attributes/feature/@key]/@feature)=@key]/@attribute_mapping return replace($mapping,'@',")"/>
<xsl:variable name="removed-asset-attributes" select="for $mapping in cs:master-data('feature')[($element-sets-diff/remove/asset_feature[@feature=$feature-wrapped-attributes/feature/@key]/@feature)=@key]/@attribute_mapping return replace($mapping,'@','")"/> -->
<xsl:variable name="added-asset-attributes" select="for $mapping in $feature-wrapped-attributes/feature[($element-sets-diff/add/asset_feature/@feature)=@key]/@attribute_mapping return replace($mapping, '@', ")"/>
<xsl:variable name="removed-asset-attributes" select="for $mapping in $feature-wrapped-attributes/feature[($element-sets-diff/remove/asset_feature/@feature)=@key]/@attribute_mapping return replace($mapping,'@',")"/>
<!--copy all existing asset attributes except added or removed -->
<xsl:for-each select="$asset-xml/@*" >
<xsl:if test="not(name()=$added-asset-attributes or name()=$removed-asset-attributes)">
<xsl:copy/>
</xsl:if>
</xsl:for-each>
<!--add new or updated asset attributes -->
<xsl:for-each select="$added-asset-attributes" >
<xsl:variable name="attribute-name" select="."/>
<xsl:variable name="attribute-wrapper-feature-key" select="$feature-wrapped-attributes/feature[@attribute_mapping=concat(@,$attribute-name)]/@key"/>
<xsl:attribute name="{$attribute-name}" select="$element-sets-diff/add/asset_feature[@feature=$attribute-wrapper-feature-key]/@*[starts-with(name(),value)][1]"/>
</xsl:for-each>
<!--omit all removed and updated elements -->
<xsl:for-each select="$asset-xml/node()">
<xsl:variable name="normalized-element" select="csc:normalize-values(csc:normalize-element(.))"/>
<xsl:if test="not($element-sets-diff/remove/node()[deep-equal(.,$normalized-element)]) and not($element-sets-diff/add/node()[deep-equal(., $normalized-element)])">
<xsl:choose>
<!--- special changes - -->
<xsl:when test="./name()=child_asset_rel and./@key=actual.">
<xsl:copy>
<xsl:copy-of select="@*|node()"/>
<!--- set cancellation flag at actual relation if product has been canceled --->
<xsl:if test="./@child_asset=$element-sets-diff/remove/child_asset_rel[@key=target.]/@child_asset">
<xsl:attribute name="iscancellation" select="1"/>
</xsl:if>
<!-- set content update flag actual relation if target relation has been changed (and reset a cancellation flag if necessary)-->
<xsl:if test="./@child_asset=$element-sets-diff/add/child_asset_rel[@key=target.]/@child_asset">
<xsl:attribute name="force_update_content" select="1"/>
<xsl:attribute name="has_update_content" select="1"/>
<xsl:attribute name="iscancellation" select="0"/>
</xsl:if>
<!--reset flags cancellation flag at actual relation if target relation is maintained -->
<xsl:if test="./@child_asset=$element-sets-diff/maintain/child_asset_rel[@key=target.]/@child_asset">
<xsl:attribute name="iscancellation" select="0"/>
</xsl:if>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:for-each>
<!--copy all new and updated elements except wrapped asset attributes -->
<xsl:copy-of select="$element-sets-diff/add/node()[not(@feature=$feature-wrapped-attributes/feature/@key)]" />
</asset>
</xsl:variable>
<xsl:copy-of select="$updated-asset"/>
</xsl:function>
<!--construct a comparable set of elements of master data-->
<xsl:function name="csc:map-elements-set">
<xsl:param name="sources" />
<xsl:param name="context" />
<xsl:param name="element-map" />
<xsl:for-each select="$sources">
<xsl:copy-of select="csc:map-element-set(.,$context, $element-map)"/>
</xsl:for-each>
</xsl:function>
<xsl:function name="csc:map-element-set">
<xsl:param name="source"/>
<xsl:param name="context" />
<xsl:param name="element-map"/>
<xsl:variable name="preliminary-element-set">
<xsl:apply-templates select="$element-map" mode="construct-element-set">
<xsl:with-param name="source" select="$source" />
<xsl:with-param name="context" select="$context" />
</xsl:apply-templates>
</xsl:variable>
<xsl:message>test:<xsl:copy-of select="$preliminary-element-set"/>
</xsl:message>
<!-- don't allow empty parent nodes in output -->
<xsl:for-each select="$preliminary-element-set/node()">
<xsl:choose>
<xsl:when test="@suppress-empty-parent='true'">
<xsl:if test="node()">
<xsl:element name="{node()/name()}">
<xsl:copy-of select="@*[name()!=suppress-empty-parent]|node()"/>
</xsl:element>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:function>
<!--- special loop handler - evaluate the path attribute and apply mapping in this new context - -->
<xsl:template match="loop" mode="construct-element-set">
<xsl:param name="source" />
<xsl:param name="context" />
<xsl:variable name="this-element-map" select="node()" />
<xsl:choose>
<xsl:when test="@path">
<xsl:for-each select="cs:evaluate(concat($source/,@path))">
<!--- add loop-sorting attribute - -->
<xsl:variable name="loop-element">
<xsl:copy>
<xsl:attribute name="loop-sorting" select="position()"/>
<xsl:copy-of select="@*|node()" />
</xsl:copy>
</xsl:variable>
<xsl:apply-templates select="$this-element-map" mode="construct-element-set">
<xsl:with-param name="source" select="$loop-element/node()" />
<xsl:with-param name="context" select="$context" />
</xsl:apply-templates>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:message><xsl:value-of select="$log-prefix" />Warning: loop element found without path attribute!</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--- special expand handler - evaluate the path attribute and apply mapping in this new context - -->
<xsl:template match="expand" mode="construct-element-set">
<xsl:param name="source"/>
<xsl:param name="context"/>
<xsl:variable name="map" select="node()" />
<xsl:choose>
<xsl:when test="@path">
<xsl:for-each select="cs:evaluate(@path)">
<xsl:variable name="loop-element">
<xsl:copy>
<xsl:attribute name="loop-sorting" select="position()"/>
<xsl:copy-of select="@*|node()" />
</xsl:copy>
</xsl:variable>
<xsl:variable name="new-context">
<context>
<xsl:copy-of select="$loop-element" />
<xsl:copy-of select="$context/context/node()" />
</context>
</xsl:variable>
<xsl:apply-templates select="$map" mode="construct-element-set">
<xsl:with-param name="source" select="$source" />
<xsl:with-param name="context" select="$new-context" />
</xsl:apply-templates>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:message><xsl:value-of select="$log-prefix" />Warning: expand element found without path attribute!</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--- handler for xml features - -->
<xsl:template match="xmldata[@data]" mode="construct-element-set">
<xsl:param name="source" />
<xsl:param name="context" />
<xmldata>
<xsl:copy-of select="cs:evaluate(substring-after(@data,=))" />
</xmldata>
</xsl:template>
<!-- default constructing handler for elements -->
<xsl:template match="node()" mode="construct-element-set">
<xsl:param name="source" />
<xsl:param name="context" />
<xsl:variable name="this-element" select="." />
<xsl:variable name="temp-element">
<!-- copy element node and start processing element attributes -->
<xsl:element name="{$this-element/name()}">
<xsl:apply-templates select="$this-element/@*" mode="construct-element-set">
<xsl:with-param name="source" select="$source"/>
<xsl:with-param name="context" select="$context"/>
</xsl:apply-templates>
<!-- resolve hierarchical structures recursively -->
<xsl:apply-templates select="$this-element/node()" mode="construct-element-set">
<xsl:with-param name="source" select="$source" />
<xsl:with-param name="context" select="$context"/>
</xsl:apply-templates>
</xsl:element>
</xsl:variable>
<!-- never copy elements with unresolved properties -->
<xsl:if test="not($temp-element/node()/@*=) and not($this-element/@suppress-without-children=true and count($temp-element//node()[not(@*=)])=1)">
<xsl:copy-of select="$temp-element"/>
</xsl:if>
</xsl:template>
<!-- default constructing handler for attributes -->
<xsl:template match="@*" mode="construct-element-set">
<xsl:param name="source" />
<xsl:param name="context" />
<xsl:choose>
<!--simple mappings -->
<xsl:when test="starts-with(., map:)">
<xsl:variable name="attribute-or-element-name" select="replace(substring-after(.,map:), @, )" />
<xsl:choose>
<!--- retrieve an attribute value - -->
<xsl:when test="$source/@*[name()=$attribute-or-element-name]">
<xsl:attribute name="{name()}" select="$source/@*[name()=$attribute-or-element-name]" />
</xsl:when>
<!--- retrieve an element value - -->
<xsl:when test="$source/node()[name()=$attribute-or-element-name]">
<xsl:attribute name="{name()}" select="$source/node()[name()=$attribute-or-element-name]" />
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="{name()}" select="" />
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<!--- evaluate an XPATH expression - -->
<xsl:when test="starts-with(., =) and not(.==)">
<xsl:variable name="expression" select="substring-after(.,=)" />
<xsl:attribute name="{name()}" select="cs:evaluate($expression)" />
</xsl:when>
<xsl:when test="./name()=suppress-without-children"/>
<!--use a separate template to resolve value -->
<xsl:when test="starts-with(., eval:)">
<xsl:apply-templates select="parent::node()" mode="eval-attribute-value">
<xsl:with-param name="source" select="$source"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<!--just copy literal attribute -->
<xsl:copy />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- normalize elements of an existing asset to grant comparability -->
<xsl:function name="csc:extract-defined-elements-of-asset">
<xsl:param name="asset-xml"/>
<xsl:param name="element-map"/>
<xsl:variable name="asset" select="$asset-xml"/>
<xsl:for-each select="$element-map/node()">
<xsl:variable name="mapping" select="." />
<xsl:choose>
<!-- processing multi-value wrappers -->
<xsl:when test="name()=loop">
<xsl:choose>
<xsl:when test="node()[name()=expand]">
<xsl:copy-of select="csc:extract-defined-elements-of-asset($asset-xml,node())"/>
</xsl:when>
<!--- have to ignore dynamic feature references - -->
<xsl:when test="node()[name()=asset_feature and not(starts-with(@feature, =))]">
<xsl:copy-of select="csc:extract-defined-elements-of-asset($asset-xml,.)"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="csc:extract-defined-elements-of-asset($asset-xml,.)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<!-- processing asset attribute wrapped by feature -->
<xsl:when test="name()=asset_feature and $mapping/@feature=$feature-wrapped-attributes/feature/@key">
<xsl:variable name="feature-definition" select="$feature-wrapped-attributes/feature[@key=$mapping/@feature]"/>
<xsl:variable name="value" select="$asset/@*[name()=replace($feature-definition/@attribute_mapping,@, )]"/>
<xsl:element name="asset_feature">
<xsl:attribute name="feature"select="$mapping/@feature"/>
<xsl:choose>
<xsl:when test="$feature-definition/@value_type=(1,2)">
<xsl:attribute name="value_key" select="$value"/>
</xsl:when>
<xsl:when test="$feature-definition/@value_type=(3,6)">
<xsl:attribute name="value_long" select="$value"/>
</xsl:when>
<xsl:when test="$feature-definition/@value_type=(4)">
<xsl:attribute name="value_string" select="$value"/>
</xsl:when>
<xsl:when test="$feature-definition/@value_type=(5)">
<xsl:attribute name="value_timestamp" select="$value"/>
</xsl:when>
<xsl:when test="$feature-definition/@value_type=(7)">
<xsl:attribute name="value_double" select="$value"/>
</xsl:when>
<xsl:otherwise>
<xsl:message><xsl:value-of select="$log-prefix" />can't map value type for feature <xsl:value-ofselect="$mapping/@feature"/></xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:when>
<!-- processing any other element -->
<xsl:otherwise>
<xsl:for-each select="$asset/node()[name()=$mapping/name()]">
<xsl:variable name="element" select="." />
<xsl:variable name="apos"></xsl:variable>
<xsl:variable name="test" select="for $x in$mapping/@*[not(contains(.,eval:)) and not(starts-with(.,=)) and not(starts-with(.,$source)) and not(contains(.,expr:)) and not(contains(.,map:)) and not(name()=suppress-without-children)] return if($x/name()!=suppress-without-children) then concat(@, $x/name(),=, $apos,$x, $apos) else "/>
<!-- evaluate all default attributes of element map against element -->
<xsl:if test="exists($test)">
<xsl:if test="cs:evaluate(string-join($test, and))">
<xsl:element name="{name()}">
<xsl:for-each select="$mapping/@*[not(name()=suppress-without-children)]">
<xsl:sort select="@*" />
<xsl:variable name="attribute" select="name()" />
<xsl:attribute name="{name()}" select="$element/@*[name()=$attribute]"/>
</xsl:for-each>
<xsl:copy-of select="$element/xmldata" copy-namespace="no"/>
<xsl:for-each select="$mapping">
<xsl:copy-of select="csc:extract-defined-elements-of-asset($element,.)"/>
</xsl:for-each>
</xsl:element>
</xsl:if>
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:function>
<xsl:function name="csc:normalize-element">
<xsl:param name="element"/>
<xsl:element name="{$element/name()}">
<xsl:choose>
<xsl:when test="$element/name()=(asset_feature,asset_rel_feature, asset_element_feature)">
<xsl:copy-of select="$element/@feature,$element/@language, $element/@sorting, $element/@*[starts-with(local-name(),value)]"/>
</xsl:when>
<xsl:when test="$element/name()=child_asset_rel">
<xsl:copy-of select="$element/@child_asset,$element/@domain, $element/@key, $element/@sorting"/>
</xsl:when>
<xsl:when test="$element/name()=parent_asset_rel">
<xsl:copy-of select="$element/@parent_asset,$element/@domain, $element/@key, $element/@sorting"/>
</xsl:when>
<xsl:when test="$element/name()=asset_element">
<xsl:copy-of select="$element/@bleed,$element/@key, $element/@idx, $element/@height_mm, $element/@width_mm,$element/@parent_idx"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$element/@*"/>
</xsl:otherwise>
</xsl:choose>
<xsl:copy-of select="$element/xmldata"copy-namespace="no"/>
<xsl:for-each select="$element/node()[not(local-name()=xmldata)]">
<xsl:copy-of select="csc:normalize-element(.)"/>
</xsl:for-each>
</xsl:element>
</xsl:function>
<!-- normalize values to grant comparability by equal sorting and refined values -->
<xsl:function name="csc:normalize-values">
<xsl:param name="feature-set"/>
<xsl:for-each select="$feature-set">
<xsl:sort select="name()"/>
<xsl:sort select="@feature"/>
<xsl:sort select="@child_asset"/>
<xsl:sorts elect="@parent_asset"/>
<xsl:sort select="@sorting"/>
<xsl:apply-templates select="." mode="normalize-values"/>
</xsl:for-each>
</xsl:function>
<xsl:template match="node()" mode="normalize-values" priority="10">
<xsl:copy>
<xsl:apply-templates select="@*" mode="normalize-values">
<xsl:sort select="name()"/>
</xsl:apply-templates>
<xsl:apply-templates select="node()" mode="normalize-values">
<xsl:sort select="name()"/>
<xsl:sort select="@feature"/>
<xsl:sort select="@child_asset"/>
<xsl:sort select="@parent_asset"/>
<xsl:sort select="@sorting"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="@*" mode="normalize-values" priority="4">
<xsl:choose>
<xsl:when test="name()=(value_timestamp,value_timestamp2)">
<!-- turn source 2015-01-01 12:00:00Z format into2013-11-20T22:28:18Z -->
<xsl:attribute name="{name()}" select="replace(., , T)"/>
</xsl:when>
<!--- suppress value_normalized - -->
<xsl:when test="name()=(value_normalized)"/>
<xsl:when test="name()=(xmlns:corpus)"/>
<xsl:when test="name()=(corpus:dto_flags)"/>
<xsl:otherwise>
<xsl:copy/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--- asset management functions - -->
<xsl:function name="csc:update">
<xsl:param name="asset-xml"/>
<cs:command name="com.censhare.api.assetmanagement.Update" returning="processed-asset-xml">
<cs:param name="source" select="$asset-xml" />
</cs:command>
<xsl:copy-of select="$processed-asset-xml"/>
</xsl:function>
<xsl:function name="csc:delete">
<xsl:param name="asset-xml" />
<xsl:param name="deletion-state" />
<!-- - Possible values are none, proposed, marked or physical - -->
<cs:command name="com.censhare.api.assetmanagement.Delete">
<cs:param name="source" select="$asset-xml"/>
<cs:param name="state" select="$deletion-state" />
</cs:command>
</xsl:function>
</xsl:stylesheet>
Results
You have walked through the XSLT to configure the update process for released assets.