Skip to content

Commit 659ac3e

Browse files
committed
Updated docs for page transformation April drop
1 parent cf5b892 commit 659ac3e

File tree

3 files changed

+264
-2
lines changed

3 files changed

+264
-2
lines changed

docs/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,6 +1387,8 @@
13871387
href: transform/modernize-userinterface-site-pages-security.md
13881388
- name: Page transformation model
13891389
href: transform/modernize-userinterface-site-pages-model.md
1390+
- name: Publishing Page transformation model
1391+
href: transform/modernize-userinterface-site-pages-model-publishing.md
13901392
- name: Page transformation Functions and Selectors
13911393
href: transform/modernize-userinterface-site-pages-api.md
13921394
- name: Page layout mapping

docs/transform/modernize-userinterface-site-pages-dotnet.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,10 @@ using (var cc = am.GetSharePointOnlineAuthenticatedContextTenant(siteUrl, userNa
127127
using (var ccTarget = cc.Clone(targetSiteUrl))
128128
{
129129
var pageTransformator = new PublishingPageTransformator(cc, ccTarget, "C:\\temp\\custompagelayoutmapping.xml");
130-
var pages = cc.Web.GetPagesFromList("Pages");
130+
var pages = cc.Web.GetPagesFromList("Pages", "a");
131131
foreach (var page in pages)
132132
{
133-
PublishingPageTransformationInformation pti = new PublishingPageTransformationInformation(page, "a")
133+
PublishingPageTransformationInformation pti = new PublishingPageTransformationInformation(page)
134134
{
135135
// If target page exists, then overwrite it
136136
Overwrite = true,
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
---
2+
title: Understanding and configuring the publishing page transformation model
3+
description: Provides detailed guidance on how to configure and use the publishing page transformation model
4+
ms.date: 04/04/2019
5+
ms.prod: sharepoint
6+
localization_priority: Normal
7+
---
8+
9+
# Understanding and configuring the publishing page transformation model (as of April 2019 version)
10+
11+
Publishing pages are always based upon a page layout and a master page. Those two pages combined with fields containing data make up the page a user sees in the browser. When transforming publishing pages it's therefor mandatory to map the used page layouts into a publishing page transformation model. The publishing page transformation component will have a 'default' page layout mapping for all the out of the box page layouts, so if your portal is using those out of the box page layouts you're covered. Reality is that most portals use custom page layouts (and custom master pages) and therefor there's a need for page layout mappings for those custom page layouts. Custom page layouts can be handled in two ways:
12+
13+
- The **preferred** option is that you provide a custom page layout mapping file yourselves, this gives you full control on what fields are translated to web parts and where they reside on the modern page, what fields become metadata and more.
14+
- If there's no page layout mapping found for the page you're transforming then we'll generate a mapping on the fly and use that. Downside of this approach is that all content will end up in the same section and column of the modern page.
15+
16+
## Generating a page layout mapping file
17+
18+
If you're using custom page layouts then it's recommended to use a custom page layout mapping file so that you'll be able to get more accurate modern pages. Luckily you don't have to handcraft these custom layout files, there's a .Net API and [PnP PowerShell](https://aka.ms/sppnp-powershell) support for generating one.
19+
20+
### Using PowerShell
21+
22+
Using the `Export-PnPClientSidePageMapping` cmdlet you can:
23+
24+
- Export the built in mapping file (`-BuiltInPageLayoutMapping` parameter): this file will be used for the out of the box page layouts. **If you specify a custom mapping for an out of the box page layout than that mapping will take preference**
25+
- Analyze the page layouts in the connected portal and export those as a mapping file (`-CustomPageLayoutMapping` parameter): **all** the found page layouts (so out of the box and custom) are analyzed and exported
26+
27+
```PowerShell
28+
# Connect to your "classic" portal
29+
Connect-PnPOnline -Url https://contoso.sharepoint.com/sites/classicportal
30+
31+
# Analyze and export the page layout mapping files
32+
Export-PnPClientSidePageMapping -BuiltInPageLayoutMapping -CustomPageLayoutMapping -Folder c:\temp
33+
```
34+
35+
### Using .Net
36+
37+
In .Net you need to use the `PageLayoutAnalyser` class to analyze page layouts. Below two snippets show how to analyze either all page layouts or the page layouts used by certain publishing pages.
38+
39+
```csharp
40+
string siteUrl = "https://contoso.sharepoint.com/sites/classicportal";
41+
string userName = "[email protected]";
42+
AuthenticationManager am = new AuthenticationManager();
43+
using (var cc = am.GetSharePointOnlineAuthenticatedContextTenant(siteUrl, userName, GetSecureString("Password")))
44+
{
45+
var analyzer = new PageLayoutAnalyser(cc);
46+
// Analyze all found page layouts
47+
analyzer.AnalyseAll();
48+
analyzer.GenerateMappingFile("c:\\temp", "custompagelayoutmapping.xml");
49+
}
50+
```
51+
52+
```csharp
53+
string siteUrl = "https://contoso.sharepoint.com/sites/classicportal";
54+
string userName = "[email protected]";
55+
AuthenticationManager am = new AuthenticationManager();
56+
using (var cc = am.GetSharePointOnlineAuthenticatedContextTenant(siteUrl, userName, GetSecureString("Password")))
57+
{
58+
var analyzer = new PageLayoutAnalyser(cc);
59+
// Analyze all the unique page layouts for publishing pages starting with an "a"
60+
var pages = cc.Web.GetPagesFromList("Pages", "a");
61+
foreach (var page in pages)
62+
{
63+
analyzer.AnalysePageLayoutFromPublishingPage(page);
64+
}
65+
66+
analyzer.GenerateMappingFile("c:\\temp", "custompagelayoutmapping.xml");
67+
}
68+
```
69+
70+
## The page layout mapping model explained
71+
72+
When you open a page layout mapping file the following top level elements are present:
73+
74+
- **AddOns**: as user of page transformation you might have the needed to apply custom logic to realize your needs e.g. you need to transform a given property in a way that it can work with your custom client side web part. The framework supports this by allowing you to add your assemblies with functions...simply defining them in the AddOn section and then referencing your custom functions later on by prefixing them with the given name will make the publishing page transformation use your custom code.
75+
76+
- **PageLayouts**: this element contains information for each page layout that you'll plan to use. For each page layout you'll find a definition of the header, web part, metadata, web part zones and fixed web parts.
77+
78+
Upcoming chapters will provide more details.
79+
80+
## PageLayout definition in the page layout model
81+
82+
Let's analyze how a page layout mapping is configured in the page layout mapping model, which is best done based upon a sample definition:
83+
84+
```Xml
85+
<PageLayout Name="MyPageLayout" AssociatedContentType="CustomPage1" PageLayoutTemplate="AutoDetect" PageHeader="Custom">
86+
<Header Type="FullWidthImage" Alignment="Left" ShowPublishedDate="true">
87+
<Field Name="PublishingRollupImage" HeaderProperty="ImageServerRelativeUrl" Functions="ToImageUrl({PublishingRollupImage})" />
88+
<Field Name="ArticleByLine" HeaderProperty="TopicHeader" Functions=""/>
89+
<Field Name="PublishingContact" HeaderProperty="Authors" Functions="ToAuthors({PublishingContact})"/>
90+
</Header>
91+
<MetaData>
92+
<Field Name="PublishingContactEmail" TargetFieldName="MyPageContact" Functions="" />
93+
<Field Name="MyCategory" TargetFieldName="Category" Functions="" />
94+
</MetaData>
95+
<WebParts>
96+
<Field Name="PublishingPageImage" TargetWebPart="SharePointPnP.Modernization.WikiImagePart" Row="1" Column="1">
97+
<Property Name="ImageUrl" Type="string" Functions="ToImageUrl({PublishingPageImage})"/>
98+
<Property Name="AlternativeText" Type="string" Functions="ToImageAltText({PublishingPageImage})" />
99+
</Field>
100+
<Field Name="PublishingPageContent" TargetWebPart="SharePointPnP.Modernization.WikiTextPart" Row="1" Column="2">
101+
<Property Name="Text" Type="string" Functions="" />
102+
</Field>
103+
</WebParts>
104+
<WebPartZones>
105+
<WebPartZone Row="2" Column="1" ZoneId="g_0C7F16935FAC4709915E2D77092A90DE" ZoneIndex="0"/>
106+
</WebPartZones>
107+
<FixedWebParts>
108+
<WebPart Row="1" Column="2" Order="1" Type="Microsoft.SharePoint.WebPartPages.ContentEditorWebPart, Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">
109+
<Property Name="TITLE" Type="string" Value="Example Embedded Web Content Editor"/>
110+
<Property Name="CONTENT" Type="string" Value="PnP Rocks!"/>
111+
</WebPart>
112+
</FixedWebParts>
113+
</PageLayout>
114+
```
115+
116+
> [!Note]
117+
> It's recommended that you use the [pagelayoutmapping.xsd schema](https://github.com/SharePoint/sp-dev-modernization/blob/dev/Tools/SharePoint.Modernization/SharePointPnP.Modernization.Framework/Publishing/pagelayoutmapping.xsd) while editing these files. To files you provide to page transformation will be validated against that schema and transformation will fail when the provided page layout mapping file is invalid.
118+
119+
### PageLayout element
120+
121+
The following properties are used on the PageLayout element:
122+
123+
- **Name**: contains the name of your page layout.
124+
- **AssociatedContentType**: the name of the modern site page content type you want use. Leave this blank if you want to use the default site page content type.
125+
- **PageLayoutTemplate**: the layout system to use...defaults to `AutoDetect` which should work fine in all cases, but optionally you can pick a specific wiki layout as well.
126+
- **PageHeader**: controls the type of page header that will be used. Defaults to `Custom` as that allows for a nice header, but you can switch it to `None` for no header or `Default` for the default modern page header.
127+
128+
### Header element
129+
130+
The following properties are used on the Header element:
131+
132+
- **Type**: defaults to `FullWidthImage` allowing the header to show the header image in full width. Alternative options are `ColorBlock`, `CutInShape` and `NoImage`. Note that this attribute only is relevant when the PageHeader attribute was set to `Custom`.
133+
- **Alignment**: controls how the page header content is aligned. Default is `Left`, alternative option is `Center`.
134+
- **ShowPublishedDate**: defines whether the page publication date is shown. Defaults to `true`.
135+
136+
When constructing a modern page header there are 4 page header fields that you can populate with data coming from the publishing page:
137+
138+
- **ImageServerRelativeUrl**: If your header needs to show an image this field will need to defined a server relative image path for an image living in the same site collection as the page. By default the classic publishing page `PublishingRollupImage` field is used, but since that contains an Img tag the field contents is cleaned via the `ToImageUrl` function.
139+
- **TopicHeader**: By default the classic publishing page `ArticleByLine` field is used as topic header for the modern page header
140+
- **Authors**: Setting the authors allows you to show the main contact for this page in the page header. By default the classic publishing page `PublishingContact` field is used, but since that's a user field the field contents is transformed via the `ToAuthors` function.
141+
- **AlternativeText**: Not mapped by default, but you can add a custom mapping to fill the modern header alternative text property if desired
142+
143+
If you for example don't want to set a topic header you can simply remove or comment the respective Field element.
144+
145+
### MetaData element
146+
147+
The metadata element defines which of the classic publishing page fields need to be taken over as metadata for the modern page. For each field that you want to take over you'll need to add a Field element specifying:
148+
149+
- **Name**: the name of the field in the classic publishing page
150+
- **TargetFieldName**: the name of the field in the target modern page
151+
152+
> [!Note]
153+
> Functions support is not yet available for metadata field elements.
154+
155+
### WebParts element
156+
157+
Each field in the classic publishing page that needs to become a visual element on the target page (like a web part or text) must defined in the web parts section:
158+
159+
- **Name**: the name of the field in the classic publishing page.
160+
- **TargetWebPart**: the type of the target web part that will visualize this field on the modern page. Supported target web parts are `SharePointPnP.Modernization.WikiTextPart`, `SharePointPnP.Modernization.WikiImagePart` and `Microsoft.SharePoint.Publishing.WebControls.SummaryLinkWebPart, Microsoft.SharePoint.Publishing, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c`.
161+
- **Row**: the row you want to put the target web part in. Needs to be 1 or greater.
162+
- **Column**: the column you want to put the target web part in. Needs to be 1, 2 or 3.
163+
164+
Depending the chosen TargetWebPart you'll need to provide the web part properties holding the data needed during transformation. Each property has the following properties:
165+
166+
- **Name**: Name of the property. These property names need to match with properties used in the [page transformation model](modernize-userinterface-site-pages-model.md).
167+
- **Type**: Type of the property. These property types need to match with properties used in the [page transformation model](modernize-userinterface-site-pages-model.md).
168+
- **Functions**: If set to empty then the property value will be the value of the field in the source classic publishing page. You can use a function to first transform the field contents or to use data from another field of the classic publishing page
169+
170+
### WebPartZones element
171+
172+
If the page layout contains web part zones then these must be defined here. This is required in order to correctly position to transformed web parts on the modern page. Following properties are used:
173+
174+
- **ZoneId**: the name of the zone
175+
- **ZoneIndex**: the index of the zone (integer)
176+
- **Row**: the row you want to put the web parts hosted in this zone in. Needs to be 1 or greater.
177+
- **Column**: the column you want to put the web parts hosted in this zone in. Needs to be 1, 2 or 3.
178+
179+
### FixedWebParts element
180+
181+
Sometimes page layouts contain "fixed" web parts, this are web parts which are hard coded inside the page layout and therefore these are present on all pages that use the page layout. As there's no API's to detect these "fixed" web parts they need to be defined as part of the page layout mapping model.
182+
183+
Following properties can be set on a "fixed" web part:
184+
185+
- **Type**: type of the fixed web part. See [here](https://github.com/SharePoint/sp-dev-modernization/blob/dev/Tools/SharePoint.Modernization/SharePointPnP.Modernization.Framework/WebParts.cs) for a list of possible types.
186+
- **Row**: the row you want to put the "fixed" web part in. Needs to be 1 or greater.
187+
- **Column**: the column you want to put the "fixed" web part in. Needs to be 1, 2 or 3.
188+
- **Order**: the order, relevant if there's multiple web parts that you put in the same row and column.
189+
190+
For each "fixed" web part you need to define the relevant properties. Typically you would take over these properties as they're defined in your classic page layout. For each property the following attributes can be set:
191+
192+
- **Property**: Name of the property. These property names need to match with properties used in the [page transformation model](modernize-userinterface-site-pages-model.md).
193+
- **Type**: Type of the property. These property types need to match with properties used in the [page transformation model](modernize-userinterface-site-pages-model.md).
194+
- **Value**: the value this property has.
195+
196+
## AddOns definition in the page layout model
197+
198+
Add-ons allow you to insert your custom logic into the page layout mapping model by following these 2 steps:
199+
200+
- Create a custom assembly hosting your custom functions
201+
- Declare this custom assembly in the AddOns elements
202+
203+
### Create your custom functions/selectors assembly
204+
205+
To create your own functions you'll need to reference the SharePoint.Modernization.Framework assembly in your project and then create a class inheriting the `SharePointPnP.Modernization.Framework.Functions.FunctionsBase` class:
206+
207+
```csharp
208+
using Microsoft.SharePoint.Client;
209+
using SharePointPnP.Modernization.Framework.Functions;
210+
using System;
211+
212+
namespace Contoso.Modernization
213+
{
214+
public class MyCustomFunctions: FunctionsBase
215+
{
216+
public MyCustomFunctions(ClientContext clientContext) : base(clientContext)
217+
{
218+
}
219+
220+
public string MyListAddServerRelativeUrl(Guid listId)
221+
{
222+
if (listId == Guid.Empty)
223+
{
224+
return "";
225+
}
226+
else
227+
{
228+
var list = this.clientContext.Web.GetListById(listId);
229+
list.EnsureProperties(p => p.RootFolder.ServerRelativeUrl);
230+
return list.RootFolder.ServerRelativeUrl;
231+
}
232+
}
233+
234+
}
235+
}
236+
```
237+
238+
### Declare your custom assembly
239+
240+
Before custom functions can be used they need to be declared in the model by adding one reference per library/class into the AddOns element:
241+
242+
```Xml
243+
<AddOn Name="Custom" Type="Contoso.Modernization.MyCustomFunctions" Assembly="Contoso.Modernization.dll" />
244+
```
245+
246+
or (note the fully qualified path)
247+
248+
```Xml
249+
<AddOn Name="Custom" Type="Contoso.Modernization.MyCustomFunctions" Assembly="c:\transform\Contoso.Modernization.dll" />
250+
```
251+
252+
Note that each declaration has a name, "Custom" in above sample.
253+
254+
### Use your custom functions/selectors
255+
256+
Now that the assembly has been defined you can use your functions and selectors be referencing them by the name like you see the "Custom" prefix in below sample:
257+
258+
```Xml
259+
<Property Name="ListId" Type="guid" Functions="{ListServerRelativeUrl} = Custom.MyListAddServerRelativeUrl({ListId})"/>
260+
```

0 commit comments

Comments
 (0)