How to map a new custom MediaAsset field to allowed object

Customer might want to have an additional field in the media asset area for some purpose, which is also meaningful for the assigned objects (Item, Product, Variant, Structure Group). And he might also want to manipulate such media asset field directly from the assigned object for some cases, e.g. assigning such media asset field value to object per import. A possible solution is to create an additional MediaAsset field and then map this new field to the corresponding object, since there are some reserved fields predefined under the entity type "MediaAssetType" which can be easily used for customer's use case.

The following steps show you how to create and map a custom MediaAsset field, and such field can not be used as a primary key which has more constraints.

Add new MediaAsset field

Let us assume that a text field is desired, then the reserved field type "MediaAssetType.Res_Text100_01" can be taken for it.

Active the desired MediaAsset field type

First, active the reserved MediaAssetType.Res_Text100_01 field type in the repository:

images/download/attachments/295035263/image2018-9-18_14-32-37.png

Add a new MediaAsset field for the activated field type

Then, create a new field MediaAsset.Res_Text100_01 which has the field type MediaAssetType.Res_Text100_01:

images/download/attachments/295035263/image2018-9-18_14-40-22.png

MediaAsset.Res_Text100f_01
<field identifier="MediaAsset.Res_Text100_01" category-ref="master-data" field-type-ref="MediaAssetType.Res_Text100_01">
<name>%field.MediaAsset.Res_Text100_01.name</name>
<description>%field.MediaAsset.Res_Text100_01.description</description>
<max-length>260</max-length>
<visible-from-top>true</visible-from-top>
<display-by-default>true</display-by-default>
<display-by-default-from-top>true</display-by-default-from-top>
<average-length>25</average-length>
<purpose>1</purpose>
<importPurpose>1</importPurpose>
<exportPurpose>51</exportPurpose>
</field>

Map MediaAsset field to the objects of the field type "ArticleType"

For the previous versions, this step is only necessary, if customer needs the similar case like importing Item/Product2G/Variant with the above defined MediaAsset.Res_Text100_01 field value. Otherwise this step can be ignored, since the user can edit this field value in Desktop UI and Web UI without any mapping.

But from the following versions on, all custom fields should be mapped to the objects, otherwise they can not be edited in UI since UI changes on MediaAsset fields trigger a direct update for the assigned object.

  • 8.1.1.04.12

  • 8.1.1.07

  • 10.0.0.01.05

  • 10.0.0.02

  • 10.1

Add mapped field type and field

Add a mapped ArticleMediaAssetMapType field type

Add a new field type ArticleMediaAssetMapType.Res_Text100_01 which will be mapped to MediaAssetType.Res_Text100_01:

images/download/attachments/295035263/image2018-9-18_14-53-2.png

ArticleMediaAssetMapType.Res_Text100_01
<field-type identifier="ArticleMediaAssetMapType.Res_Text100_01">
<object-name>res_Text100_01</object-name>
<class-name>java.lang.String</class-name>
<persistence-class-name>java.lang.String</persistence-class-name>
<fragment-column-access>MediaAsset.Res_Text100_01</fragment-column-access>
<max-length>2000</max-length>
</field-type>

The "Object Name" property of the mapped media asset field type should be same as the one of the media asset field type!

E.g. ArticleMediaAssetMapType.Res_Text100_01 has a value "res_Text100_01" for the "Object Name" property, that is the same property value for the field type MediaAssetType.Res_Text100_01.

Add a corresponding ArticleMediaAssetMap field

Add a new field ArticleMediaAssetMap.Res_Text100_01:

images/download/attachments/295035263/image2018-9-18_14-46-5.png

ArticleMediaAssetMap.Res_Text100_01
<field identifier="ArticleMediaAssetMap.Res_Text100_01" category-ref="assets" field-type-ref="ArticleMediaAssetMapType.Res_Text100_01" supports-serviceapi="true">
<name>%field.ArticleMediaAssetMap.Res_Text100_01.name</name>
<description>%field.ArticleMediaAssetMap.Res_Text100_01.description</description>
<max-length>260</max-length>
<editable>true</editable>
<visible>true</visible>
<visible-from-top>false</visible-from-top>
<display-by-default>false</display-by-default>
<display-by-default-from-top>false</display-by-default-from-top>
<average-length>25</average-length>
<mergeable>true</mergeable>
<purpose>1</purpose>
<importPurpose>1</importPurpose>
<exportPurpose>0</exportPurpose>
</field>

Add a corresponding field for other objects

Add such a field to Product2GMediaAssetMap and also to VariantMediaAssetMap if you are working with the three tier product paradigm.

It is similar to the ArticleMediaAssetMap.Res_Text100_01, e.g. a Product2GMediaAssetMap.Res_Text100_01 which has the same field type "ArticleMediaAssetMapType.Res_Text100_01".

Complete map logic with code

Set a NullPropertyMediator for the mapped field type

NullPropertyMediator for ArticleMediaAssetMapType.Res_Text100_01
<extension
point="com.heiler.ppm.persistence.server.persister">
...
<propertyMediator
class="com.heiler.ppm.persistence.server.mediator.impl.NullPropertyMediator"
description="Null Property Mediator for ArticleMediaAssetMapType.Res_Text100_01"
fieldTypeIdentifier="ArticleMediaAssetMapType.Res_Text100_01"
id="hpm.article.AMAM.Res_Text100_01">
</propertyMediator>
...
</extension>

Contribute own PropertyMediator to replace the standard one for the mapped MediaAssetProxy field

Property Mediator for ArticleMediaAssetMapType.MediaAssetProxy
<extension
point="com.heiler.ppm.persistence.server.persister">
...
<propertyMediator
class="custom.mediaasset.core.mediator.CustomAMAMMediaAssetProxyPropertyMediator"
description="Custom Property Mediator for ArticleMediaAssetMapType.MediaAssetProxy"
fieldTypeIdentifier="ArticleMediaAssetMapType.MediaAssetProxy"
id="hpm.custom.article.AMAM.MediaAssetProxy"
target-id="hpm.article.AMAM.MediaAssetProxy">
</propertyMediator>
...
</extension>

The contributed class CustomAMAMMediaAssetProxyPropertyMediator should implement the interface methods like following:

  • read( MediatorSession session, PropertyData propertyData ): extend the similar logic also for the reserved fields. As a quick solution you can copy the code from AMAMMediaAssetProxyPropertyMediator and its super class EntityMAMMediaAssetProxyPropertyMediator, then call an own JPQL (similar to the com.heiler.ppm.mediaasset.db.query.MediaAssetJPQL.QUERY_SEL_MEDIAASSET_FIELDS_BY_ID) to read the corresponding value for all standard fields and also for the reserved fields, and store them in the business object (see EntityMAMMediaAssetProxyPropertyMediator.setMediaAssetFields(...)).

  • write( MediatorSession context, PropertyData propertyData, AttributeChange attributeChange ): same as the one of the standard AMAMMediaAssetProxyPropertyMediator, just copy it.

Add a PropertyPostSetter to transfer the field change

The field change should be transferred from mapped field type ArticleMediaAssetMapType.Res_Text100_01 to the field type MediaAssetType.Res_Text100_01.

PropertyPostSetter for ArticleMediaAssetMapType.Res_Text100_01
<operator
class="custom.mediaasset.core.command.CustomArticleMediaAssetMapPostSetter"
description="Mirrors the custom ArticleMediaAssetMap fields in the MediaAssetProxy"
id="CustomAMAMPostSetter"
name="CustomAMAMPostSetter"
type="PropertyPostSetter">
...
<repository-identifier
identifier="ArticleMediaAssetMapType.Res_Text100_01"
type="FIELD_TYPE">
</repository-identifier>
<target-command type="PutCommand"/>
...
</operator>

The class CustomArticleMediaAssetMapPostSetter should be an implementation of the interface PropertyPostSetter and transfer the field change from the mapped field type ArticleMediaAssetMapType.Res_Text100_01 to the MediaAsset field type MediaAssetType.Res_Text100_01. As a concrete example please visit the standard source code com.heiler.ppm.mediaasset.core.internal.command.ArticleMediaAssetMapPostSetter.

Map field to the object of the field type "StructureGroupType" (optional)

It is only necessary, if customer needs the case like importing structure group directly with the above defined MediaAsset.Res_Text100_01 field value. Otherwise this step can be ignored, since the user can edit this field value in Desktop UI and Web UI.

But from the following versions on, all custom fields should be mapped to the objects, otherwise they can not be edited in UI since UI changes on MediaAsset fields trigger a direct update for the assigned object.

  • 8.1.1.04.12

  • 8.1.1.07

  • 10.0.0.01.05

  • 10.0.0.02

  • 10.1

Add mapped field type and field

Add a mapped StructureGroupMediaAssetMapType field type

It is similar to ArticleMediaAssetMapType.Res_Text100_01, e.g. a new field type "StructureGroupMediaAssetMapType.Res_Text100_01".

Add a corresponding StructureGroupMediaAssetMap field

It is similar to the ArticleMediaAssetMap.Res_Text100_01 field, e.g. StructureGroupMediaAssetMap.Res_Text100_01 which has the field type "StructureGroupMediaAssetMapType.Res_Text100_01".

Complete map logic with code

Set a NullPropertyMediator for the mapped field type

It is similar to the one for ArticleMediaAssetMapType.Res_Text100_01.

Contribute own PropertyMediator to replace the standard one for the mapped MediaAssetProxy field

It is similar to the one for ArticleMediaAssetMapType.MediaAssetProxy.

Add a PropertyPostSetter to transfer the field change

The field change should be transferred from mapped field type StructureGroupMediaAssetMapType.Res_Text100_01 to the field type MediaAssetType.Res_Text100_01.

It is similar to the above described CustomAMAMPostSetter.

Enable custom field in the Web

To show the new custom field in the P360 Web UI under the Media tab of the selected object (Item/Product/Variant/Structure Group), it should be added in the media.variants.xml like following:

media.variants.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<formDefinitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FormDefinitions.xsd">
<definition position="2147483647" rootEntity="MediaAsset">
<column>
<fieldGroup>
<field identifier="MediaAsset.Name"/>
<field identifier="MediaAsset.Type"/>
<field identifier="MediaAsset.Res_Text100_01"/>
</fieldGroup>
<fieldGroup displaySectionWidget="true" subEntityId="MediaAssetLang">
<field identifier="MediaAssetLang.Description"/>
<field identifier="MediaAssetLang.AlternateText"/>
<logicalKey identifier="MediaAssetLangType.LK.Language" value="English"/>
</fieldGroup>
</column>
</definition>
</formDefinitions>

Map a new custom MediaAssetLang field

The above described steps are for mapping a custom MediaAsset field, and it is also feasible to map a custom MediaAssetLang field by similar steps.

For example, if a custom MediaAssetLang field is desired to be mapped to item, then the following steps should be done:

  • Active a desired reserved MediaAssetLang field type (e.g. MediaAssetLangType.Res_Text100_01)

  • Add a new MediaAssetLang field for the activated field type (e.g. MediaAssetLang.Res_Text100_01)

  • Add a mapped ArticleMediaAssetLangType field type (e.g. ArticleMediaAssetLangType.Res_Text100_01)

  • Add a corresponding ArticleMediaAssetLang field (e.g. ArticleMediaAssetLang.Res_Text100_01)

  • It is unnecessary to set a NullPropertyMediator for the mapped MediaAssetLang field type (e.g. ArticleMediaAssetLangType.Res_Text100_01), since there is already a contributed NullSubMediator for the entity type ArticleMediaAssetLangType

  • Contribute (or adjust if it is created for a custom MediaAsset field) own PropertyMediator by loading the field value with enhanced JPQL

  • Add a PropertyPostSetter to transfer the field change from the mapped field type (e.g. ArticleMediaAssetLangType.Res_Text100_01) to the field type (e.g. MediaAssetLangType.Res_Text100_01)

  • Enable custom MediaAssetLang field in the Web