Entender e configurar o modelo de transformação de página
O coração da solução de transformação de página é o modelo que alimenta a transformação: o modelo informa ao mecanismo quais propriedades da Web Part são importantes, permite manipular essas propriedades e escolher dinamicamente um mapeamento para sua Web Part. O modelo de transformação de página é expresso no XML e vem com um esquema usado para validar a correção do modelo.
Importante
A modernização do SharePoint PnP faz parte da Estrutura PnP e está em constante evolução, verifique as notas de versão para se manter atualizado sobre as alterações mais recentes. Se você tiver problemas, registre-o na lista de problemas do GitHub sobre Estrutura PnP.
Arquitetura alto nível de transformação de página
Veja a seguir a imagem explicativa da transformação de página em 4 etapas:
- No início, é preciso informar ao mecanismo de transformação de que forma você deseja transformar as páginas. Isso é feito fornecendo um modelo de transformação de página. Este modelo é um arquivo XML que descreve como cada web part clássica precisa ser mapeada para uma equivalente moderna. O modelo contém uma lista de propriedades relevantes e informações de mapeamento por web part clássica. Confira o artigo sobre como entender e configurar o modelo de transformação de página para saber mais. Se você quiser entender a comparação entre web parts clássicas e web parts modernas é recomendável verificar o arquivoExperiências de web part clássica e moderna.
- A próxima etapa será analisar a página que você quer transformar: o mecanismo de transformação irá quebrar a página em uma coleção de web parts (o texto de wiki é quebrado em uma ou mais web parts de texto de wiki) e ele tentará detectar o layout usado.
- As informações recuperadas da análise na etapa 2 geralmente não são suficientes para mapear a web part para uma equivalente moderna. Dessa forma, na etapa 3, a informação será aprimorada chamando funções: essas funções obtêm as propriedades recuperadas na etapa 2 e geram novas propriedades com base naquelas inseridas na etapa 2. Após a etapa 3, temos todas as informações necessárias para mapear a Web Part... exceto que, opcionalmente, precisamos chamar o seletor definido para entender qual mapeamento precisaremos caso uma Web Part clássica possa ser mapeada para várias configurações modernas.
- A etapa final é criar e configurar a página moderna seguida pela adição das web parts modernas mapeadas a ela.
Estrutura do modelo da transformação de página
Ao abrir o modelo de transformação de página, os seguintes elementos de nível superior estarão presentes:
BaseWebPart: este elemento contém a configuração que se aplica a todas as web parts, por exemplo, ele descreve que a propriedade “Title” será obtida para todas as web parts. É também o local que define o mapeamento padrão da Web Part: se uma Web Part não tiver um mapeamento definido, o mecanismo retornará a esse mapeamento para representar a Web Part na página de destino.
AddOns: como usuário da transformação de página, talvez você precise aplicar a lógica personalizada para perceber suas necessidades, por exemplo, você precisa transformar uma determinada propriedade de uma maneira que possa funcionar com sua Web Part SPFX personalizada. O mecanismo oferece suporte a isso permitindo que você adicione seus assemblies com funções e seletores. Simplesmente defina-os na seção AddOn (suplementos) e, então, faça referência às suas funções e seletores personalizados posteriormente colocando um prefixo neles junto do referido nome. Isso fará com que a transformação de página use seu código personalizado.
Web Parts: esse elemento contém informações de cada web part que você deseja transformar. Para cada web part, você encontrará uma definição das propriedades a serem usadas, das funções a serem executadas nessas propriedades, dos mapeamentos possíveis que definem o destino da transformação combinada com um seletor que você seleciona dinamicamente o mapeamento necessário.
Os próximos capítulos oferecerão mais detalhes.
Definição de Web Part no modelo de transformação de página
Vamos analisar como uma web part é configurada no modelo de transformação de página, o qual é mais bem feito com base em um exemplo simplificado da definição XsltListViewWebPart:
<!-- XsltListView web part -->
<WebPart Type="Microsoft.SharePoint.WebPartPages.XsltListViewWebPart, Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">
<Properties>
<Property Name="XmlDefinitionLink" Type="string" />
<Property Name="ListUrl" Type="string" />
<Property Name="ListId" Type="guid" Functions="{ListWebRelativeUrl} = ListAddWebRelativeUrl({ListId}); {ListServerRelativeUrl} = ListAddServerRelativeUrl({ListId})"/>
<Property Name="Direction" Type="string"/>
<Property Name="GhostedXslLink" Type="string" />
<Property Name="DisableViewSelectorMenu" Type="bool"/>
<Property Name="XmlDefinition" Type="string" Functions="{ListViewId} = ListDetectUsedView({ListId},{XmlDefinition})"/>
<Property Name="SelectParameters" Type="string"/>
</Properties>
<!-- This selector outputs: Library, List -->
<Mappings Selector="ListSelectorListLibrary({ListId})">
<Mapping Name="List" Default="true">
<ClientSideText Text="You can map a source web part ({Title}) to a combination of modern web parts :-)" Order="10" />
<ClientSideWebPart Type="List" Order="20" JsonControlData="{"serverProcessedContent":{"htmlStrings":{},"searchablePlainTexts":{},"imageSources":{},"links":{}},"dataVersion":"1.0","properties":{"isDocumentLibrary":false,"selectedListId":"{ListId}","listTitle":"{Title}","selectedListUrl":"{ListServerRelativeUrl}","webRelativeListUrl":"{ListWebRelativeUrl}","webpartHeightKey":4,"selectedViewId":"{ListViewId}"}}" />
</Mapping>
<Mapping Name="Library" Default="false">
<ClientSideWebPart Type="List" Order="10" JsonControlData="{"serverProcessedContent":{"htmlStrings":{},"searchablePlainTexts":{},"imageSources":{},"links":{}},"dataVersion":"1.0","properties":{"isDocumentLibrary":true,"selectedListId":"{ListId}","listTitle":"{Title}","selectedListUrl":"{ListServerRelativeUrl}","webRelativeListUrl":"{ListWebRelativeUrl}","webpartHeightKey":4,"selectedViewId":"{ListViewId}"}}" />
</Mapping>
</Mappings>
</WebPart>
Elemento Properties
Para cada Web Part, o modelo define as propriedades que podem ser úteis para configurar as web part(s) modernas de destino. Se você não vir certas propriedades, é possível simplesmente estendê-las no modelo. Em algumas das propriedades, você verá um atributo Functions: esse atributo contém uma ou mais funções (funções separadas por meio de um ;) que são executados quando a Web Part de origem é mapeada para uma Web Part moderna de destino. A anatomia de uma função é a seguinte:
{Output} = FunctionName({Input1}, {Input2})
Uma função pode ter um ou mais valores de entrada que podem ser:
- Propriedades definidas nessa web part (por exemplo {ListId})
- Propriedades definidas na web part básica (por exemplo {Title})
- Propriedades que foram a saída de execuções de função anteriores (por exemplo {ListWebRelativeUrl})
- Propriedades com escopo do site padrão: {Host}, {Web}, {Site}, {WebId}, {SiteId}
Quando uma função executa sua saída será:
- Um valor de uma única cadeia de caracteres: esse valor ({Output} no modelo apresentado) será adicionado à lista de propriedades da web part com o nome "Output" e terá o valor que retornou da execução de
FunctionName
. - Uma lista de pares de chave/valor (cadeia de caracteres do dicionário<, cadeia de caracteres>): nesse caso, cada par de chave/valor retornado é adicionado à lista de propriedades da Web Part
Se a função não definir o parâmetro de saída, o valor da propriedade de web part que define a função será substituído pelo resultado da função.
Vamos esclarecer com um exemplo:
<Property Name="ListId" Type="guid" Functions="FormatGuid({ListId})"/>
Vamos supor que a propriedade da web part contenha originalmente um guid formatado como {AAFAD7D0-D57A-4BB1-8706-969A608C686B}. Após o FormatGuid
ser executado, o valor será definido como a saída de FormatGuid
(por exemplo, um guid sem as chaves AAFAD7D0-D57A-4BB1-8706-969A608C686B).
Se uma função retornar diversos valores, cada par chave/valor retornado que ainda exista como propriedade de web part substituirá esse valor de propriedades.
Observação
Todas as funções prontas para uso são descritas em Funções e Seletores de Transformação de Páginas
Elemento Mappings
Esse elemento define uma ou mais configurações de destino possíveis para a referida web part de origem. Como você pode definir vários destinos é necessário haver um mecanismo para determinar qual mapeamento usar:
- Se o elemento de mapeamento contiver um atributo Selector preenchido, a saída da execução do seletor será usada para encontrar o mapeamento correto pelo nome (por exemplo, função seletor
ListSelectorListLibrary
retorna a cadeia de caracteres "Library", que resulta no mapeamento com nome "Library" sendo usado). Os seletores são idênticos a funções que retornam um único valor, então, você poderá especificar qualquer atributo de web part como entrada para a função de seletor - Se houver apenas um único mapeamento, então, é considerado como se não houvesse nenhum resultado do seletor.
- Se não houver nenhum resultado do seletor e houver vários mapeamentos definidos, será considerado, então, o mapeamento marcado como Padrão
Observação
Todas as funções prontas para uso são descritas em Funções e Seletores de Transformação de Páginas
A seguir, vamos explicar o elemento Mapping propriamente dito.
Elemento Mapping
Dentro de um elemento Mapeamento, você pode ter um ou mais elementos ClientSideText ou ClientSideWebPart, conforme mostrado no snippet abaixo. Observe que você pode executar funções em um Mapeamento, o que é útil caso você queira fazer o processamento somente se um mapeamento específico for selecionado.
<Mapping Name="List" Default="true" Functions="{SampleVariable} = SampleFunction({ListId})>
<ClientSideText Text="You can map a source web part ({Title}) to a combination of modern web parts :-)" Order="10" />
<ClientSideWebPart Type="List" Order="20" JsonControlData="{"serverProcessedContent":{"htmlStrings":{},"searchablePlainTexts":{},"imageSources":{},"links":{}},"dataVersion":"1.0","properties":{"isDocumentLibrary":false,"selectedListId":"{ListId}","listTitle":"{Title}","selectedListUrl":"{ListServerRelativeUrl}","webRelativeListUrl":"{ListWebRelativeUrl}","webpartHeightKey":4,"selectedViewId":"{ListViewId}"}}" />
</Mapping>
No exemplo acima, a web part do XSLTListView de origem é mapeada para uma parte de texto moderna (ClientSideText
) e uma web part moderna (ClientSideWebPart
):
- Use o atributo
Order
para determinar qual será a primeira - Para uma
ClientSideWebPart
, é preciso especificar oType
da web part, caso escolhaCustom
como tipo. Também será preciso especificar a propriedadeControlId
para identificar a web part personalizada necessária. - As propriedades
Text
,JsonControlData
eControlId
podem conter tokens na forma de {Token} que são substituídos por valores reais na hora da execução. Cada token definido precisa ser como uma propriedade de web part ou resultado de função, conforme explicado anteriormente
Definição de AddOns (complementos) no modelo de transformação de página
Os complementos permitem que você insira sua lógica personalizada no modelo de mapeamento seguindo estas duas etapas:
- Crie um assembly personalizado que hospede seus seletores/funções personalizados
- Declare este assembly personalizado nos elementos AddOns
Criar seu assembly de funções/seletores personalizado
Para criar suas próprias funções, você precisará fazer referência ao assembly SharePoint.Modernization.Framework no projeto e criar uma classe que herde a classe SharePointPnP.Modernization.Framework.Functions.FunctionsBase
:
using Microsoft.SharePoint.Client;
using SharePointPnP.Modernization.Framework.Functions;
using System;
namespace Contoso.Modernization
{
public class MyCustomFunctions: FunctionsBase
{
public MyCustomFunctions(ClientContext clientContext) : base(clientContext)
{
}
public string MyListAddServerRelativeUrl(Guid listId)
{
if (listId == Guid.Empty)
{
return "";
}
else
{
var list = this.clientContext.Web.GetListById(listId);
list.EnsureProperties(p => p.RootFolder.ServerRelativeUrl);
return list.RootFolder.ServerRelativeUrl;
}
}
}
}
Declarar seu assembly personalizado
Antes de as funções personalizadas poderem ser usadas, elas precisam ser declaradas no modelo, incluindo uma referência por biblioteca/classe no elemento AddOns:
<AddOn Name="Custom" Type="Contoso.Modernization.MyCustomFunctions" Assembly="Contoso.Modernization.dll" />
ou (observe o caminho qualificado completo)
<AddOn Name="Custom" Type="Contoso.Modernization.MyCustomFunctions" Assembly="c:\transform\Contoso.Modernization.dll" />
Observe que cada declaração tem um nome "Custom" (personalizado) no exemplo acima.
Usar seus seletores/funções personalizados
Agora que o assembly foi definido, você poderá usar as funções e seletores fazendo referência a eles pelo nome, como você pode ver o prefixo "Custom" no exemplo abaixo:
<Property Name="ListId" Type="guid" Functions="{ListServerRelativeUrl} = Custom.MyListAddServerRelativeUrl({ListId})"/>