Modernization: Publishing Page Transformation Model : Mapping File : Allow multiple target field names / functions

Created on 10 May 2019  路  13Comments  路  Source: pnp/modernization

Not sure if this feature exists? It would be super useful to be able to define multiple target fields when mapping <Field> elements in either the <Header> or <MetaData> sections.

For example, when setting the Header "banner image" property:
<Field Name="PublishingRollupImage" HeaderProperty="ImageServerRelativeUrl" Functions="ToImageUrl({PublishingRollupImage})" />

It would be great if I could say:

_"Try to use 'PublishingRollupImage', but if that field is empty or invalid then use 'PublishingPageContent' instead"_

I think this could be tackled in a number of ways:


Approach 1 - Allow multiple field name attributes
_For simplicity it would be REALLY useful if I could refer to the element attributes in the Functions - here I have used "@Name" to represent the Name attribute. Just another suggestion?_
<Field Name="PublishingRollupImage,PublishingPageContent" HeaderProperty="ImageServerRelativeUrl" Functions="ToImageUrl({@Name})" />

Same could be done for <MetaData> field elements
<Field Name="Title" TargetFieldName="CustomTitleField,Title" Functions="" />

Approach 2 - A TryEach function?
This would be in the format:
TryEach([<value1>,<value2>,<valueN>], <function>)
again, the ability to use a token value to refer to the value being passed to the function - I have used $_ as this is standard PowerShell syntax - there may be something else more suitable?

<Field Name="PublishingRollupImage" HeaderProperty="ImageServerRelativeUrl" Functions="TryEach([{PublishingRollupImage},{PublishingPageContent},StaticString('/images/myCustomBanner.png')], ToImageUrl({$_})" />

_and yes, I snuck in a nested "use a static string if the value is empty" too - another thing I think would be useful_


I think Approach 1 is more elegant and cleaner code wise, but I appreciate that mileage my vary when it comes to implementing this in the back-end code.

Also "functions" can be used elsewhere and we might want to make use of it elsewhere in the mappings file?

enhancement PageTransformationEngine

Most helpful comment

Are you looking for contributors?
I'd be happy to get my hands dirty to help make this a reality.

All 13 comments

Hey @MartinHatch ,

Interesting ideas, I for sure see the value of this. Implementation wise this, however, is not an easy thing to do...will evaluate this for a future release.

Are you looking for contributors?
I'd be happy to get my hands dirty to help make this a reality.

Would be great if you could help! Before jumping in code let's first finalize a design for this...currently busy with prepping for upcoming conferences (SPC and ECS), but after there should be plenty of time. If any of you is either SPC or ECS we discuss this face to face as well.

brain dump: probably easiest to implement by using the existing model in web part mapping file where one can specify multiple functions split by a ; delimiter. We could make the second function conditional based up on the value of the field after the first function run...this would then allow to setup a "fall back" model

ok .. so provide multiple functions, and if the first one results in a blank or null value then execute the second one?

How about also having token based function arguments? (like @name) and then using comma-separated values for the Field "name" attribute value?

Thinking it makes copy-pasting a long function much much neater. No change required in the XSD either.

  • Use the first "field.name" value which is not null or empty
  • Update the function pre-processing to convert tokens before calling the function.

I admit this would be slightly more work, but I think it is the much more elegant and flexible solution for the target developer community.

The current model does allow for function arguments via curly brackets like this:

<Field Name="PreviewImage" TargetFieldName="BannerImageUrl" Functions="ToPreviewImageUrl({PreviewImage})" />

What I was thinking of is enabling the following scenario:

<Field Name="PreviewImage" TargetFieldName="BannerImageUrl" Functions="ToPreviewImageUrl({PreviewImage});ToPreviewImageUrl({OtherImageField})" />

So in the above snippet, the behavior would be this:

  • If the PreviewImage field is filled then we'll use the first function
  • If the field value is null then the OtherImageField value is used with the function around it

Does this align with your thinking @MartinHatch ?

My only thought on that approach is that it makes the Name attribute completely redundant, because the Functions expression overrides it (in fact .. that is the case anyway).

I think it would both be clearer, more flexible and neater to instead refer to a dynamic attribute in the Functions expression, and use multiple values for the Name attribute.

Basic field mapping:
<Field Name="ContactName,OtherNameField" TargetFieldName="Contact" Functions="" />

Mapping which includes a function:
<Field Name="PreviewImage,OtherImageField" TargetFieldName="BannerImageUrl" Functions="ToPreviewImageUrl({@Name})" />

This could in fact be adopted as the standard approach and works perfectly well even when you only have a single field to map to, as the current model risks mis-reading the XML file and thinking it is doing something different than what it actually is.

Take this example:

<Field Name="PublishingRollupImage" TargetFieldName="BannerImageUrl" Functions="ToPreviewImageUrl({PublishingPageContent})" />

At first glance you would think that it is using "PublishingRollupImage" as the mapping .. but it isn't, because the Function expression is completely overriding it - the actual "Name" attribute value is effectively being ignored.

Writing it instead like this make it clear, and removes any possible ambiguity:

<Field Name="PublishingPageContent" TargetFieldName="BannerImageUrl" Functions="ToPreviewImageUrl({@Name})" />

What do you think??

I know this is a bit more effort, but I totally think this is worth it in terms of general function usage?

Right now I was looking for this, I'm glad to find some information and I like that implementation @MartinHatch

I would say that field to field mapping in some cases is confusing in any way, for example in my scope I need to promote the pages to the site news and the only way I found was by using this field mapping:

<Field Name="ID" TargetFieldName="PromotedState" Functions="StaticString('2')" />
_I'm using the ID field because it always has values, but it smells like a trick._

Although it might be better to have a custom field mapping plug-in for anyone to write their own transformations. What do you think?

Right now I was looking for this, I'm glad to find some information and I like that implementation @MartinHatch

I would say that field to field mapping in some cases is confusing in any way, for example in my scope I need to promote the pages to the site news and the only way I found was by using this field mapping:

<Field Name="ID" TargetFieldName="PromotedState" Functions="StaticString('2')" />
_I'm using the ID field because it always has values, but it smells like a trick._

Although it might be better to have a custom field mapping plug-in for anyone to write their own transformations. What do you think?

@eduardpaul That's a fair point when using static strings. Maybe we could make the "Name" attribute optional and if BOTH the Name and Functions attributes are empty then it just gets ignored.

@jansenbe so . if you are happy I propose the following changes (please confirm and I will make all this happen! :))

  1. Name attribute (on Field element) is changed to allow blank values

  2. Change in logic to ignore Field mappings when both Name and Functions attributes are empty

  3. Change Field processing to allow multiple (comma separated) Name attribute values - First field with a value which is not null, empty or blank will be used.

  4. Change in general "Functions" processing to allow use of element attributes as part of the declaration using the {@AttributeName} syntax

As per the above .. example syntax for proposed logic:

<!-- Field with no "Name" or "Functions" attributes - and will be ignored -->
<Field Name="" TargetFieldName="PromotedState" Functions="" />

<!-- Field with no 'Name' attribute - for static string functions -->
<Field Name="" TargetFieldName="PromotedState" Functions="StaticString('2')" />

<!-- Field with multiple mappings --> 
<Field Name="CustomTitle,Title" TargetFieldName="Title" Functions="" />

<!-- Field with {@Attribute} syntax in the function -->
<Field Name="PublishingRollupImage" TargetFieldName="BannerImageUrl" Functions="ToPreviewImageUrl({@Name})" />

<!-- Field with multiple mappings AND {@Attribute} syntax in the function -->
<Field Name="PublishingRollupImage,PublishingPageImage,PublishingPageContent" TargetFieldName="BannerImageUrl" Functions="ToPreviewImageUrl({@Name})" />

Hi @MartinHatch ,

Sorry for the delayed answer (too many conferences :-) ....anyhow, I do like your proposal as it indeed is more logical and easier to read. At first sight, I also do not see any breaking changes for existing templates so we can safely introduce this going forward.

Looking forward to your PR for this!

Assigned to July release cycle for now...

@MartinHatch : July release will be released on July 1st / 2nd which implies that possible PR's you want to be included should get in by end of next week. If you feel you need more time than you simply wait for the August release window.

Was this page helpful?
0 / 5 - 0 ratings