Partilhar via


Personalizar o UX usando suplementos hospedados pelo provedor do SharePoint

Este artigo descreve exemplos que mostram as melhores práticas para personalizar componentes do SharePoint UX, incluindo os seguintes cenários:

  • Manipulação de página (adicionar e modificar uma página wiki)

  • Mostrando suplementos e dados em caixas de diálogo modais

  • Criando elementos personalizados da interface do usuário

  • Renderização do lado do cliente (implantando arquivos JSLink que personalizam a renderização de campos em listas do SharePoint)

  • Manipulação de parte de web part e suplemento (provisionar remotamente e executar uma parte de script de suplemento em um suplemento hospedado pelo provedor)

  • Agregação e cache de dados (usando o armazenamento local HTML5 e cookies HTTP para reduzir o número de chamadas de serviço para o SharePoint)

Observação

The code in this article is provided as-is, without warranty of any kind, either express or implied, including any implied warranties of fitness for a particular purpose, merchantability, or non-infringement.

Manipulação de página:

O exemplo Core.ModifiPages inclui dois cenários de manipulação de página:

  • Criar uma página wiki.
  • Modifique o layout de uma página wiki.

Este exemplo usa a biblioteca de páginas de site padrão e layouts existentes fora da caixa. Você também pode atualizá-lo para usar uma biblioteca de páginas wiki personalizada e layouts personalizados. A interface do usuário de suplemento inclui dois botões que criam páginas wiki e dois links para exibir as páginas wiki que você cria.

Página inicial para o exemplo de manipulação de página

Página de inicialização para o modelo de manipulação de página


O código de exemplo para o primeiro cenário determina se você já criou a página wiki. Caso contrário, ele adiciona o arquivo à biblioteca de páginas do site e retorna sua URL.

var newpage = pageLibrary.RootFolder.Files.AddTemplateFile(newWikiPageUrl, TemplateFileType.WikiPage);
ctx.Load(newpage);
ctx.ExecuteQuery();
wikiPageUrl = String.Format("sitepages/{0}", wikiPageName);

Em ambos os cenários, o exemplo adiciona o HTML inserido por meio da caixa de texto na página inicial usando o método AddHtmlToWikiPage em uma classe auxiliar chamada LabHelper. Esse método insere o HTML do formulário dentro do campo WikiField da página wiki.

public void AddHtmlToWikiPage(ClientContext ctx, Web web, string folder, string html, string page)
        {
            Microsoft.SharePoint.Client.Folder pagesLib = web.GetFolderByServerRelativeUrl(folder);
            ctx.Load(pagesLib.Files);
            ctx.ExecuteQuery();

            Microsoft.SharePoint.Client.File wikiPage = null;

            foreach (Microsoft.SharePoint.Client.File aspxFile in pagesLib.Files)
            {
                if (aspxFile.Name.Equals(page, StringComparison.InvariantCultureIgnoreCase))
                {
                    wikiPage = aspxFile;
                    break;
                }
            }

            if (wikiPage == null)
            {
                return;
            }

            ctx.Load(wikiPage);
            ctx.Load(wikiPage.ListItemAllFields);
            ctx.ExecuteQuery();

            string wikiField = (string)wikiPage.ListItemAllFields["WikiField"];

            Microsoft.SharePoint.Client.ListItem listItem = wikiPage.ListItemAllFields;
            listItem["WikiField"] = html;
            listItem.Update();
            ctx.ExecuteQuery();
        }

O código de exemplo para o segundo cenário cria uma nova instância WebPartEntity . Em seguida, ele usa métodos em uma classe auxiliar para preencher a Web Part com XML. O XML exibe uma lista de links promovidos, incluindo ambos http://www.bing.com e a página inicial do repositório GitHub Office 365 Padrões e Práticas do Desenvolvedor.

WebPartEntity wp2 = new WebPartEntity();
wp2.WebPartXml = new LabHelper().WpPromotedLinks(linksID, string.Format("{0}/Lists/{1}",
                                                                Request.QueryString["SPHostUrl"], "Links"),
                                                                string.Format("{0}/{1}", Request.QueryString["SPHostUrl"],
                                                                scenario2PageUrl), "$Resources:core,linksList");
wp2.WebPartIndex = 1;
wp2.WebPartTitle = "Links";

new LabHelper().AddWebPartToWikiPage(ctx, ctx.Web, "SitePages", wp2, scenario2Page, 2, 1, false);
new LabHelper().AddHtmlToWikiPage(ctx, ctx.Web, "SitePages", htmlEntry.Text, scenario2Page, 2, 2);

this.hplPage2.NavigateUrl = string.Format("{0}/{1}", Request.QueryString["SPHostUrl"], scenario2PageUrl);

O código auxiliar exibe os links promovidos com uma tabela dentro de uma Web Part XsltListViewWebPart .

Segunda página wiki com XsltListViewWebPart e tabela de links promovidos

Segunda página Wiki com uma tabela de Web Part XSLT ListView e links promovidos


O objeto WpPromotedLinks na instância do LabHelper contém XML que define a aparência da Web Part que será inserida na página wiki. O método AddWebPartToWikiPage insere a Web Part recém-definida dentro de uma nova div marca na página wiki.

XmlDocument xd = new XmlDocument();
            xd.PreserveWhitespace = true;
            xd.LoadXml(wikiField);

            // Sometimes the wikifield content seems to be surrounded by an additional div.
            XmlElement layoutsTable = xd.SelectSingleNode("div/div/table") as XmlElement;
            if (layoutsTable == null)
            {
                layoutsTable = xd.SelectSingleNode("div/table") as XmlElement;
            }

            XmlElement layoutsZoneInner = layoutsTable.SelectSingleNode(string.Format("tbody/tr[{0}]/td[{1}]/div/div", row, col)) as XmlElement;
            // - space element
            XmlElement space = xd.CreateElement("p");
            XmlText text = xd.CreateTextNode(" ");
            space.AppendChild(text);

            // - wpBoxDiv
            XmlElement wpBoxDiv = xd.CreateElement("div");
            layoutsZoneInner.AppendChild(wpBoxDiv);

            if (addSpace)
            {
                layoutsZoneInner.AppendChild(space);
            }

            XmlAttribute attribute = xd.CreateAttribute("class");
            wpBoxDiv.Attributes.Append(attribute);
            attribute.Value = "ms-rtestate-read ms-rte-wpbox";
            attribute = xd.CreateAttribute("contentEditable");
            wpBoxDiv.Attributes.Append(attribute);
            attribute.Value = "false";
            // - div1
            XmlElement div1 = xd.CreateElement("div");
            wpBoxDiv.AppendChild(div1);
            div1.IsEmpty = false;
            attribute = xd.CreateAttribute("class");
            div1.Attributes.Append(attribute);
            attribute.Value = "ms-rtestate-read " + wpdNew.Id.ToString("D");
            attribute = xd.CreateAttribute("id");
            div1.Attributes.Append(attribute);
            attribute.Value = "div_" + wpdNew.Id.ToString("D");
            // - div2
            XmlElement div2 = xd.CreateElement("div");
            wpBoxDiv.AppendChild(div2);
            div2.IsEmpty = false;
            attribute = xd.CreateAttribute("style");
            div2.Attributes.Append(attribute);
            attribute.Value = "display:none";
            attribute = xd.CreateAttribute("id");
            div2.Attributes.Append(attribute);
            attribute.Value = "vid_" + wpdNew.Id.ToString("D");

            ListItem listItem = webPartPage.ListItemAllFields;
            listItem["WikiField"] = xd.OuterXml;
            listItem.Update();
            ctx.ExecuteQuery();

Mostrando suplementos e dados em caixas de diálogo modais

O exemplo Core.Dialog mostra dois métodos para inserir links de caixa de diálogo modal. Esses links exibem uma página de suplemento hospedada pelo provedor em um site de host do SharePoint. O suplemento usa o CSOM (modelo de objeto cliente) para criar a ação personalizada e o JavaScript para iniciar e exibir informações dentro da caixa de diálogo. Como algumas dessas informações vêm do site de host, ela também usa o modelo de objeto JavaScript (JSOM) para recuperar informações do site do host. E como o suplemento está sendo executado em um domínio diferente do site de host do SharePoint, ele também usa a biblioteca entre domínios do SharePoint para fazer as chamadas para o site de host.

Observação

Para obter mais informações sobre como usar a biblioteca entre domínios nesse cenário, confira Acessar dados do SharePoint de suplementos usando a biblioteca entre domínios.

A página inicial é a página que aparece na caixa de diálogo. Para lidar com quaisquer diferenças na exibição, dado o contexto de exibição (caixa de diálogo versus página inteira), o suplemento determina se ele está sendo exibido em uma caixa de diálogo. Ele faz isso usando um parâmetro de cadeia de caracteres de consulta que é passado junto com os links que iniciam as caixas de diálogo.

private string SetIsDlg(string isDlgValue)
        {
            var urlParams = HttpUtility.ParseQueryString(Request.QueryString.ToString());
            urlParams.Set("IsDlg", isDlgValue);
            return string.Format("{0}://{1}:{2}{3}?{4}", Request.Url.Scheme, Request.Url.Host, Request.Url.Port, Request.Url.AbsolutePath, urlParams.ToString());
        }

Você pode, por exemplo, optar por exibir determinados elementos da interface do usuário (como botões) ou até mesmo layouts de página diferentes, dependendo se o conteúdo está sendo exibido ou não em uma caixa de diálogo.

A interface do usuário da página inicial apresenta duas opções para criar links para a caixa de diálogo, juntamente com uma lista de todas as listas na Web do host. Ele também apresenta botões OK e Cancel que você pode usar no contexto da caixa de diálogo para fechar a caixa de diálogo e/ou solicitar ações adicionais no suplemento.

Quando você escolhe o botão Adicionar item de menu , o suplemento cria um objeto CustomActionEntity que inicia a caixa de diálogo. Em seguida, ele usa o método de extensão Do OfficeDevPnP Core chamado AddCustomAction para adicionar a nova ação personalizada ao menu Configurações do Site .

StringBuilder modelDialogScript = new StringBuilder(10);
modelDialogScript.Append("javascript:var dlg=SP.UI.ModalDialog.showModalDialog({url: '");
modelDialogScript.Append(String.Format("{0}", SetIsDlg("1")));
modelDialogScript.Append("', dialogReturnValueCallback:function(res, val) {} });");

// Create a custom action.
CustomActionEntity customAction = new CustomActionEntity()
{
  Title = "Office AMS Dialog sample",
  Description = "Shows how to start an add-in inside a dialog box.",
  Location = "Microsoft.SharePoint.StandardMenu",
  Group = "SiteActions",
  Sequence = 10000,
  Url = modelDialogScript.ToString(),
};

// Add the custom action to the site.
cc.Web.AddCustomAction(customAction);

O método AddCustomAction adiciona a ação personalizada à coleção UserCustomActions associada ao site do SharePoint.

var newAction = existingActions.Add();
            newAction.Description = customAction.Description;
            newAction.Location = customAction.Location;
            if (customAction.Location == JavaScriptExtensions.SCRIPT_LOCATION)
            {
                newAction.ScriptBlock = customAction.ScriptBlock;
            }
            else
            {
                newAction.Sequence = customAction.Sequence;
                newAction.Url = customAction.Url;
                newAction.Group = customAction.Group;
                newAction.Title = customAction.Title;
                newAction.ImageUrl = customAction.ImageUrl;
                if (customAction.Rights != null)
                {
                    newAction.Rights = customAction.Rights;
                }
            }
            newAction.Update();
            web.Context.Load(web, w => w.UserCustomActions);
            web.Context.ExecuteQuery();

Quando você escolhe a página Adicionar com o botão Web Part do Editor de Scripts , o suplemento usa os métodos AddWikiPage e AddWebPartToWikiPage para criar uma página wiki dentro da biblioteca de páginas do site e adicionar uma web part configurada do Editor de Scripts à página.

string scenario1Page = String.Format("scenario1-{0}.aspx", DateTime.Now.Ticks);
string scenario1PageUrl = cc.Web.AddWikiPage("Site Pages", scenario1Page);
cc.Web.AddLayoutToWikiPage("SitePages", WikiPageLayout.OneColumn, scenario1Page);
WebPartEntity scriptEditorWp = new WebPartEntity();
scriptEditorWp.WebPartXml = ScriptEditorWebPart();
scriptEditorWp.WebPartIndex = 1;
scriptEditorWp.WebPartTitle = "Script editor test";
cc.Web.AddWebPartToWikiPage("SitePages", scriptEditorWp, scenario1Page, 1, 1, false);

O método AddWikiPage carrega a biblioteca de páginas do site. Se a página wiki especificada pelo suplemento OfficeDevPnP Core ainda não existir, ela criará a página.

public static string AddWikiPage(this Web web, string wikiPageLibraryName, string wikiPageName)
        {
            string wikiPageUrl = "";


            var pageLibrary = web.Lists.GetByTitle(wikiPageLibraryName);

            web.Context.Load(pageLibrary.RootFolder, f => f.ServerRelativeUrl);
            web.Context.ExecuteQuery();

            var pageLibraryUrl = pageLibrary.RootFolder.ServerRelativeUrl;
            var newWikiPageUrl = pageLibraryUrl + "/" + wikiPageName;

            var currentPageFile = web.GetFileByServerRelativeUrl(newWikiPageUrl);

            web.Context.Load(currentPageFile, f => f.Exists);
            web.Context.ExecuteQuery();

            if (!currentPageFile.Exists)
            {
                var newpage = pageLibrary.RootFolder.Files.AddTemplateFile(newWikiPageUrl, TemplateFileType.WikiPage);

                web.Context.Load(newpage);
                web.Context.ExecuteQuery();

                wikiPageUrl = UrlUtility.Combine("sitepages", wikiPageName);
            }

            return wikiPageUrl;
        }

O método AddWebPartToWikiPage insere a Web Part recém-definida dentro de uma nova <div> marca na página wiki. Em seguida, ele usa o JSOM e a biblioteca entre domínios para recuperar as informações que preenchem a lista de listas do SharePoint na Web do host.

function printAllListNamesFromHostWeb() {
        var context;
        var factory;
        var appContextSite;
        var collList;

        context = new SP.ClientContext(appWebUrl);
        factory = new SP.ProxyWebRequestExecutorFactory(appWebUrl);
        context.set_webRequestExecutorFactory(factory);
        appContextSite = new SP.AppContextSite(context, spHostUrl);

        this.web = appContextSite.get_web();
        collList = this.web.get_lists();
        context.load(collList);

        context.executeQueryAsync(
            Function.createDelegate(this, successHandler),
            Function.createDelegate(this, errorHandler)
        );

https://github.com/SharePoint/PnP/tree/dev/Samples/Core.AppScriptPart

Elementos personalizados da interface do usuário

O exemplo Branding.UIElementPersonalization mostra como usar JavaScript inserido e valores armazenados em perfis de usuário e listas do SharePoint para personalizar elementos da interface do usuário na Web do host. Ele também usa o armazenamento local HTML5 para minimizar chamadas para o site do host.

A página inicial do exemplo solicita que você adicione uma das três cadeias de caracteres (XX, YY ou ZZ) à seção About Me de sua página de perfil de usuário.

O suplemento já implantou três imagens e uma lista do SharePoint que contém títulos e URLs para cada imagem, juntamente com um campo adicional que vincula cada imagem a uma das três cadeias de caracteres. Depois de escolher o botão Injetar personalização , o suplemento insere o arquivo personalize.js na coleção de ações personalizadas do usuário.

public void AddPersonalizeJsLink(ClientContext ctx, Web web)
        {
            string scenarioUrl = String.Format("{0}://{1}:{2}/Scripts", this.Request.Url.Scheme,
                                                this.Request.Url.DnsSafeHost, this.Request.Url.Port);
            string revision = Guid.NewGuid().ToString().Replace("-", "");
            string personalizeJsLink = string.Format("{0}/{1}?rev={2}", scenarioUrl, "personalize.js", revision);

            StringBuilder scripts = new StringBuilder(@"
                var headID = document.getElementsByTagName('head')[0];
                var");

            scripts.AppendFormat(@"
                newScript = document.createElement('script');
                newScript.type = 'text/javascript';
                newScript.src = '{0}';
                headID.appendChild(newScript);", personalizeJsLink);
            string scriptBlock = scripts.ToString();

            var existingActions = web.UserCustomActions;
            ctx.Load(existingActions);
            ctx.ExecuteQuery();

            var actions = existingActions.ToArray();
            foreach (var action in actions)
            {
                if (action.Description == "personalize" &amp;&amp;
                    action.Location == "ScriptLink")
                {
                    action.DeleteObject();
                    ctx.ExecuteQuery();
                }
            }

            var newAction = existingActions.Add();
            newAction.Description = "personalize";
            newAction.Location = "ScriptLink";

            newAction.ScriptBlock = scriptBlock;
            newAction.Update();
            ctx.Load(web, s => s.UserCustomActions);
            ctx.ExecuteQuery();
        }

Como os sites de equipe do SharePoint por padrão usam a MDS (Estratégia Mínima de Download), o código no arquivo personalize.js primeiro tenta se registrar no MDS. Dessa forma, quando você carrega a página que contém o JavaScript, o mecanismo MDS inicia a função main (RemoteManager_Inject). Se o MDS estiver desabilitado, essa função será iniciada imediatamente.

// Register script for MDS, if possible.
RegisterModuleInit("personalize.js", RemoteManager_Inject); //MDS registration
RemoteManager_Inject(); //non-MDS run

if (typeof (Sys) != "undefined" &amp;&amp; Boolean(Sys) &amp;&amp; Boolean(Sys.Application)) {
    Sys.Application.notifyScriptLoaded();h
}

if (typeof (NotifyScriptLoadedAndExecuteWaitingJobs) == "function") {
    NotifyScriptLoadedAndExecuteWaitingJobs("scenario1.js");
}
// The RemoteManager_Inject function is the entry point for loading the other scripts that perform the customizations. When a given script depends on another script, be sure to load the dependent script after the one on which it depends. This sample loads the JQuery library before the personalizeIt function that uses JQuery.
function RemoteManager_Inject() {

    var jQuery = "https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.2.min.js";

    // Load jQuery.
    loadScript(jQuery, function () {

        personalizeIt();

    });
}

A função personalizeIt() verifica o armazenamento local HTML5 antes de examinar as informações do perfil do usuário. Se ele for para informações de perfil de usuário, ele armazenará as informações que recupera no armazenamento local HTML5.

function personalizeIt() {
    clientContext = SP.ClientContext.get_current();

    var fileref = document.createElement('script');
    fileref.setAttribute("type", "text/javascript");
    fileref.setAttribute("src", "/_layouts/15/SP.UserProfiles.js");
    document.getElementsByTagName("head")[0].appendChild(fileref);

    SP.SOD.executeOrDelayUntilScriptLoaded(function () {

        // Get localstorage values if they exist.
        buCode = localStorage.getItem("bucode");
        buCodeTimeStamp = localStorage.getItem("buCodeTimeStamp");

        // Determine whether the page already has embedded personalized image.
        var pageTitle = $('#pageTitle')[0].innerHTML;
        if (pageTitle.indexOf("img") > -1) {
            personalized = true;
        }
        else {
            personalized = false;
        }

        // If nothing is in localstorage, get profile data, which will also populate localstorage.
        if (buCode == "" || buCode == null) {
            getProfileData(clientContext);
            personalized = false;
        }
        else {
            // Check for expiration.
            if (isKeyExpired("buCodeTimeStamp")) {
                getProfileData(clientContext);

                if (buCode != "" || buCode != null) {
                    // Set timestamp for expiration.
                    currentTime = Math.floor((new Date().getTime()) / 1000);
                    localStorage.setItem("buCodeTimeStamp", currentTime);

                    // Set personalized to false so that the code can check for a new image in case buCode was updated.
                    personalized = false;
                }
            }
        }

        // Load image or make sure it is current based on the value in AboutMe.
        if (!personalized) {
            loadPersonalizedImage(buCode);
        }


    }, 'SP.UserProfiles.js');
}

O arquivo personalize.js também contém código que verifica se a chave de armazenamento local expirou. Isso não é integrado ao armazenamento local HTML5.

// Check to see if the key has expired
function isKeyExpired(TimeStampKey) {

    // Retrieve the example setting for expiration in seconds.
    var expiryStamp = localStorage.getItem(TimeStampKey);

    if (expiryStamp != null &amp;&amp; cacheTimeout != null) {

        // Retrieve the timestamp and compare against specified cache timeout settings to see if it is expired.
        var currentTime = Math.floor((new Date().getTime()) / 1000);

        if (currentTime - parseInt(expiryStamp) > parseInt(cacheTimeout)) {
            return true; //Expired
        }
        else {
            return false;
        }
    }
    else {
        //default
        return true;
    }
}

Client-side rendering

O exemplo Branding.ClientSideRendering mostra como usar um suplemento hospedado pelo provedor para provisionar remotamente artefatos do SharePoint e arquivos JSLink que usam a renderização do lado do cliente para personalizar a aparência e o comportamento dos campos de lista do SharePoint. Os arquivos JSLink e a renderização do lado do cliente fornecem controle sobre como os controles em uma página do SharePoint (exibições de lista, campos de lista e adicionar e editar formulários) são renderizados. Esse controle pode reduzir ou eliminar a necessidade de tipos de campo personalizados. A renderização do lado do cliente torna possível controlar remotamente a aparência do campo de lista remotamente.

O exemplo combina os exemplos JSLink dos exemplos de código JSLink (renderização do lado do cliente) em um único suplemento hospedado pelo provedor para SharePoint que provisiona os arquivos JSLink. A renderização do lado do cliente permite que você use tecnologias web padrão, como HTML e JavaScript, para definir a lógica de renderização dos tipos de campo personalizados e predefinidos.

Quando você inicia o exemplo, a página inicial solicita que você provisione todos os exemplos.

Página inicial do exemplo de renderização do lado do cliente

Página de inicial do modelo de renderização do cliente

Quando você escolhe Exemplos de Provisionamento, o suplemento implanta uma imagem, bem como todas as listas do SharePoint, exibições de lista, itens de lista, formulários e arquivos JavaScript que são usados em cada exemplo. O suplemento cria uma pasta chamada JSLink-Samples na Biblioteca de Estilos e, em seguida, carrega os arquivos JavaScript nessa pasta. O método UploadFileToFolder faz o trabalho de carregar e verificar em cada arquivo JavaScript.

public static void UploadFileToFolder(Web web, string filePath, Folder folder)
        {
            using (FileStream fs = new FileStream(filePath, FileMode.Open))
            {
                FileCreationInformation flciNewFile = new FileCreationInformation();

                flciNewFile.ContentStream = fs;
                flciNewFile.Url = System.IO.Path.GetFileName(filePath);
                flciNewFile.Overwrite = true;

                Microsoft.SharePoint.Client.File uploadFile = folder.Files.Add(flciNewFile);
                uploadFile.CheckIn("CSR sample js file", CheckinType.MajorCheckIn);

                folder.Context.Load(uploadFile);
                folder.Context.ExecuteQuery();
            }
        }

Exemplo 1: Aplicar formatação a uma coluna de lista

O exemplo 1 mostra como aplicar a formatação a uma coluna de lista com base no valor do campo. Os valores de campo de prioridade de 1 (Alto), 2 (Normal) e 3 (Baixo) são exibidos em vermelho, laranja e amarelo.

Exibição de campo de lista personalizado

Exibição de campo de lista personalizado

O JavaScript a seguir substitui a exibição de campo padrão e cria um novo modelo de exibição para o campo Lista de prioridades . A técnica na função anônima que carrega as informações contextuais sobre o campo cuja exibição você deseja substituir é usada em todos os exemplos.

(function () {

    // Create object that has the context information about the field that you want to render differently.
    var priorityFiledContext = {};
    priorityFiledContext.Templates = {};
    priorityFiledContext.Templates.Fields = {
        // Apply the new rendering for Priority field in List View.
        "Priority": { "View": priorityFiledTemplate }
    };

    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(priorityFiledContext);

})();

// This function provides the rendering logic for list view.
function priorityFiledTemplate(ctx) {

    var priority = ctx.CurrentItem[ctx.CurrentFieldSchema.Name];

    // Return HTML element with appropriate color based on the Priority column's value.
    switch (priority) {
        case "(1) High":
            return "<span style='color :#f00'>" + priority + "</span>";
            break;
        case "(2) Normal":
            return "<span style='color :#ff6a00'>" + priority + "</span>";
            break;
        case "(3) Low":
            return "<span style='color :#cab023'>" + priority + "</span>";
    }
}

Exemplo 2: Encurtar texto longo

O exemplo 2 mostra como truncar texto longo armazenado no campo Corpo de uma lista de anúncios . O texto completo é exibido como um pop-up que aparece sempre que você passa o mouse sobre um item de lista, conforme mostrado na figura a seguir.

Exibição de campo de lista abreviada mostrando pop-up

Exibição de um campo de lista truncada mostrando uma janela pop-up

O JavaScript a seguir encurta o texto do campo Corpo e faz com que o texto completo apareça como um pop-up por meio do atributo de título na marca de intervalo .

function bodyFiledTemplate(ctx) {

    var bodyValue = ctx.CurrentItem[ctx.CurrentFieldSchema.Name];

    // This regex expression is used to delete HTML tags from the Body field.
    var regex = /(<([^>]+)>)/ig;

    bodyValue = bodyValue.replace(regex, "");

    var newBodyValue = bodyValue;

    if (bodyValue &amp;&amp; bodyValue.length >= 100)
    {
        newBodyValue = bodyValue.substring(0, 100) + " ...";
    }

    return "<span title='" + bodyValue + "'>" + newBodyValue + "</span>";

}

Exemplo 3: exibir uma imagem com um nome de documento

O exemplo 3 mostra como exibir uma imagem ao lado de um nome de documento dentro de uma biblioteca de documentos. Um selo vermelho é exibido sempre que o valor do campo Confidencial é definido como Sim.

Exibição de imagem ao lado do nome do documento

Exibição de imagem ao lado do nome do documento

O JavaScript a seguir verifica o valor do campo Confidencial e personaliza a exibição do campo Nome com base no valor de outro campo. O exemplo usa a imagem carregada quando você escolhe Provisionar Exemplos.

function linkFilenameFiledTemplate(ctx) {

    var confidential = ctx.CurrentItem["Confidential"];
    var title = ctx.CurrentItem["FileLeafRef"];

    // This Regex expression is used to delete the extension (.docx, .pdf ...) from the file name.
    title = title.replace(/\.[^/.]+$/, "")

    // Check confidential field value.
    if (confidential &amp;&amp; confidential.toLowerCase() == 'yes') {
        // Render HTML that contains the file name and the confidential icon.
        return title + "&amp;nbsp;<img src= '" + _spPageContextInfo.siteServerRelativeUrl + "/Style%20Library/JSLink-Samples/imgs/Confidential.png' alt='Confidential Document' title='Confidential Document'/>";
    }
    else
    {
        return title;
    }
}

Exemplo 4: exibir um gráfico de barras

O exemplo 4 mostra como exibir um gráfico de barras no campo % Completo de uma lista de tarefas. A aparência do gráfico de barras depende do valor do campo % Completo , conforme mostrado na figura a seguir. Observe que um gráfico de barras também aparece nos formulários para criar e editar itens da lista de tarefas.

Gráfico de barras exibido no campo % concluído

Gráfico de barras exibido no campo % concluído

O código a seguir cria a exibição do gráfico de barras e o associa à exibição e aos formulários de exibição (percentCompleteViewFiledTemplate) e, em seguida, aos formulários novos e de edição (percentCompleteEditFiledTemplate).

// This function provides the rendering logic for View and Display forms.
function percentCompleteViewFiledTemplate(ctx) {

    var percentComplete = ctx.CurrentItem[ctx.CurrentFieldSchema.Name];
    return "<div style='background-color: #e5e5e5; width: 100px;  display:inline-block;'> \
            <div style='width: " + percentComplete.replace(/\s+/g, '') + "; background-color: #0094ff;'> \
            &amp;nbsp;</div></div>&amp;nbsp;" + percentComplete;

}

// This function provides the rendering logic for New and Edit forms.
function percentCompleteEditFiledTemplate(ctx) {

    var formCtx = SPClientTemplates.Utility.GetFormContextForCurrentField(ctx);

    // Register a callback just before submit.
    formCtx.registerGetValueCallback(formCtx.fieldName, function () {
        return document.getElementById('inpPercentComplete').value;
    });

    return "<input type='range' id='inpPercentComplete' name='inpPercentComplete' min='0' max='100' \
            oninput='outPercentComplete.value=inpPercentComplete.value' value='" + formCtx.fieldValue + "' /> \
            <output name='outPercentComplete' for='inpPercentComplete' >" + formCtx.fieldValue + "</output>%";
}

Exemplo 5: alterar o modelo de renderização

O exemplo 5 mostra como alterar o modelo de renderização para uma exibição de lista. Essa exibição exibe títulos de item de lista que se expandem como acordeões quando você os seleciona. A exibição expandida mostra campos de item de lista adicionais.

Exibições de itens de lista recolhidos e expandidos

Exibições de itens de lista recolhidos e expandidos

O código a seguir configura o modelo e o registra com o modelo de lista. Ele configura o layout geral e usa o manipulador de eventos OnPostRender para registrar a função JavaScript que é executada quando a lista é renderizada. Essa função associa o evento ao tratamento de eventos e CSS que implementa a funcionalidade de acordeon.

(function () {

    // jQuery library is required in this sample.
    // Fallback to loading jQuery from a CDN path if the local is unavailable.
    (window.jQuery || document.write('<script src="//ajax.aspnetcdn.com/ajax/jquery/jquery-1.10.0.min.js"><\/script>'));

    // Create object that has the context information about the field that you want to render differently.
    var accordionContext = {};
    accordionContext.Templates = {};

    // Be careful when adding the header for the template, because it will break the default list view render.
    accordionContext.Templates.Header = "<div class='accordion'>";
    accordionContext.Templates.Footer = "</div>";

    // Add OnPostRender event handler to add accordion click events and style.
    accordionContext.OnPostRender = accordionOnPostRender;

    // This line of code tells the TemplateManager that you want to change all the HTML for item row rendering.
    accordionContext.Templates.Item = accordionTemplate;

    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(accordionContext);

})();

// This function provides the rendering logic.
function accordionTemplate(ctx) {
    var title = ctx.CurrentItem["Title"];
    var description = ctx.CurrentItem["Description"];

    // Return whole item html.
    return "<h2>" + title + "</h2><p>" + description + "</p><br/>";
}

function accordionOnPostRender() {

    // Register event to collapse and expand when selecting accordion header.
    $('.accordion h2').click(function () {
        $(this).next().slideToggle();
    }).next().hide();

    $('.accordion h2').css('cursor', 'pointer');
}

Exemplo 6: Validar valores de campo

O exemplo 6 mostra como usar expressões regulares para validar os valores de campo fornecidos pelo usuário. Uma mensagem de erro vermelha é exibida quando o usuário digita um endereço de email inválido na caixa de texto do campo Email. Isso acontece quando o usuário cria ou edita um item de lista.

Mensagem de erro para entrada de texto de campo inválido

Mensagem de erro para entrada de texto de campo inválido

O código a seguir configura o modelo com um espaço reservado para exibir a mensagem de erro e registra as funções de retorno de chamada que são disparadas sempre que o usuário tenta enviar os formulários. O primeiro retorno de chamada retorna o valor da coluna Email e o segundo retorno de chamada usa expressões regulares para validar o valor da cadeia de caracteres.

function emailFiledTemplate(ctx) {
  var formCtx = SPClientTemplates.Utility.GetFormContextForCurrentField(ctx);

  // Register a callback just before submit.
  formCtx.registerGetValueCallback(formCtx.fieldName, function () {
      return document.getElementById('inpEmail').value;
  });

  // Create container for various validations.
  var validators = new SPClientForms.ClientValidation.ValidatorSet();
  validators.RegisterValidator(new emailValidator());

  // Validation failure handler.
  formCtx.registerValidationErrorCallback(formCtx.fieldName, emailOnError);

  formCtx.registerClientValidator(formCtx.fieldName, validators);

  return "<span dir='none'><input type='text' value='" + formCtx.fieldValue + "'  maxlength='255' id='inpEmail' class='ms-long'> \
          <br><span id='spnError' class='ms-formvalidation ms-csrformvalidation'></span></span>";
}

// Custom validation object to validate email format.
emailValidator = function () {
  emailValidator.prototype.Validate = function (value) {
    var isError = false;
    var errorMessage = "";

    //Email format Regex expression
    var emailRejex = /\S+@\S+\.\S+/;

    if (!emailRejex.test(value) &amp;&amp; value.trim()) {
      isError = true;
      errorMessage = "Invalid email address";
    }

    // Send error message to error callback function (emailOnError).
    return new SPClientForms.ClientValidation.ValidationResult(isError, errorMessage);
  };
};

// Add error message to spnError element under the input field element.
function emailOnError(error) {
  document.getElementById("spnError").innerHTML = "<span role='alert'>" + error.errorMessage + "</span>";
}

Exemplo 7: tornar os campos de edição de item de lista somente leitura

O exemplo sete mostra como fazer com que os campos de formulário de edição de item de lista sejam lidos somente. Os campos somente leitura são exibidos sem controles de edição.

Campo somente leitura em um formulário de edição de lista personalizado

Campos somente leitura em um formulário de edição de lista personalizado

O exemplo de código a seguir modifica os campos Título, AssignedTo e Prioridade no formulário de edição do item de lista para que eles mostrem os valores de campo apenas sem controles de edição. O código mostra como lidar com os requisitos de análise para diferentes tipos de campo.

function readonlyFieldTemplate(ctx) {
  // Reuse SharePoint JavaScript libraries.
  switch (ctx.CurrentFieldSchema.FieldType) {
    case "Text":
    case "Number":
    case "Integer":
    case "Currency":
    case "Choice":
    case "Computed":
      return SPField_FormDisplay_Default(ctx);

    case "MultiChoice":
      prepareMultiChoiceFieldValue(ctx);
      return SPField_FormDisplay_Default(ctx);

    case "Boolean":
      return SPField_FormDisplay_DefaultNoEncode(ctx);

    case "Note":
      prepareNoteFieldValue(ctx);
      return SPFieldNote_Display(ctx);

    case "File":
      return SPFieldFile_Display(ctx);

    case "Lookup":
    case "LookupMulti":
      return SPFieldLookup_Display(ctx);

    case "URL":
      return RenderFieldValueDefault(ctx);

    case "User":
      prepareUserFieldValue(ctx);
      return SPFieldUser_Display(ctx);

    case "UserMulti":
      prepareUserFieldValue(ctx);
      return SPFieldUserMulti_Display(ctx);

    case "DateTime":
      return SPFieldDateTime_Display(ctx);

    case "Attachments":
      return SPFieldAttachments_Default(ctx);

    case "TaxonomyFieldType":
      //Re-use JavaScript from the sp.ui.taxonomy.js SharePoint JavaScript library.
      return SP.UI.Taxonomy.TaxonomyFieldTemplate.renderDisplayControl(ctx);
  }
}

// User control needs specific formatted value to render content correctly.
function prepareUserFieldValue(ctx) {
  var item = ctx['CurrentItem'];
  var userField = item[ctx.CurrentFieldSchema.Name];
  var fieldValue = "";

  for (var i = 0; i < userField.length; i++) {
    fieldValue += userField[i].EntityData.SPUserID + SPClientTemplates.Utility.UserLookupDelimitString + userField[i].DisplayText;

    if ((i + 1) != userField.length) {
      fieldValue += SPClientTemplates.Utility.UserLookupDelimitString
    }
  }

  ctx["CurrentFieldValue"] = fieldValue;
}

// Choice control needs specific formatted value to render content correctly.
function prepareMultiChoiceFieldValue(ctx) {
  if (ctx["CurrentFieldValue"]) {
    var fieldValue = ctx["CurrentFieldValue"];

    var find = ';#';
    var regExpObj = new RegExp(find, 'g');

    fieldValue = fieldValue.replace(regExpObj, '; ');
    fieldValue = fieldValue.replace(/^; /g, '');
    fieldValue = fieldValue.replace(/; $/g, '');

    ctx["CurrentFieldValue"] = fieldValue;
  }
}

// Note control needs specific formatted value to render content correctly.
function prepareNoteFieldValue(ctx) {
  if (ctx["CurrentFieldValue"]) {
    var fieldValue = ctx["CurrentFieldValue"];
    fieldValue = "<div>" + fieldValue.replace(/\n/g, '<br />'); + "</div>";

    ctx["CurrentFieldValue"] = fieldValue;
  }
}

Exemplo 8: Ocultar campos

O exemplo 8 mostra como ocultar campos no item de lista novos e editar formulários. O exemplo oculta o campo Predecessores quando um usuário cria ou edita um item de lista de tarefas.

Este exemplo é implantado como a edição e o novo formulário para uma lista chamada lista CSR-Hide-Controls. Para obter informações sobre como exibir o formulário depois de implantar o exemplo, consulte Branding.ClientSideRendering.

O código a seguir localiza o campo Predecessores no HTML do formulário e o oculta. O campo permanece presente no HTML, mas o usuário não pode vê-lo no navegador.

(function () {
  // jQuery library is required in this sample.
  // Fallback to loading jQuery from a CDN path if the local is unavailable.
  (window.jQuery || document.write('<script src="//ajax.aspnetcdn.com/ajax/jquery/jquery-1.10.0.min.js"><\/script>'));

  // Create object that has the context information about the field that we want to render differently.
  var hiddenFiledContext = {};
  hiddenFiledContext.Templates = {};
  hiddenFiledContext.Templates.OnPostRender = hiddenFiledOnPreRender;
  hiddenFiledContext.Templates.Fields = {
    // Apply the new rendering for Predecessors field in New and Edit forms.
    "Predecessors": {
      "NewForm": hiddenFiledTemplate,
      "EditForm": hiddenFiledTemplate
    }
  };

  SPClientTemplates.TemplateManager.RegisterTemplateOverrides(hiddenFiledContext);
})();


// This function provides the rendering logic.
function hiddenFiledTemplate() {
  return "<span class='csrHiddenField'></span>";
}

// This function provides the rendering logic.
function hiddenFiledOnPreRender(ctx) {
  jQuery(".csrHiddenField").closest("tr").hide();
}

Manipulação de parte da Web part e suplemento

O exemplo Core.AppScriptPart mostra como usar partes de script de suplemento para inserir scripts em execução em um suplemento hospedado pelo provedor em uma página do SharePoint. Este exemplo mostra como modificar a interface do usuário de uma página no site do host implantando uma parte de script de suplemento e adicionando-a a uma página do SharePoint da Galeria de Web Part.

Uma parte de script de suplemento é como uma Web Part na qual você pode adicioná-la a uma página do SharePoint da Galeria de Web Part, mas, nesse caso, o arquivo .webpart insere um arquivo JavaScript que é executado remotamente em um suplemento hospedado pelo provedor. A parte de script de suplemento é executada dentro de uma <div> marca na página do SharePoint e, portanto, fornece um design e uma experiência mais responsivos do que você obtém com partes de suplemento que são executadas em IFrames.

A página inicial inclui um botão Executar Cenário que implanta a parte do script de suplemento na Galeria de Web Part. O exemplo de código a seguir constrói uma instância FileCreationInformationObject que contém o conteúdo do arquivo .webpart e, em seguida, carrega o novo arquivo na Galeria de Web Part. Observe que você também pode executar esse código automaticamente quando a parte do suplemento for instalada ou como parte do processo de provisionamento da coleção de sites.

var spContext = SharePointContextProvider.Current.GetSharePointContext(Context);
using (var clientContext = spContext.CreateUserClientContextForSPHost())
{
  var folder = clientContext.Web.Lists.GetByTitle("Web Part Gallery").RootFolder;
  clientContext.Load(folder);
  clientContext.ExecuteQuery();

  // Upload the OneDrive for Business Usage Guidelines.docx.
  using (var stream = System.IO.File.OpenRead(Server.MapPath("~/userprofileinformation.webpart")))
  {
    FileCreationInformation fileInfo = new FileCreationInformation();
    fileInfo.ContentStream = stream;
    fileInfo.Overwrite = true;
    fileInfo.Url = "userprofileinformation.webpart";
    File file = folder.Files.Add(fileInfo);
    clientContext.ExecuteQuery();
  }

  // Update the group for uploaded web part.
  var list = clientContext.Web.Lists.GetByTitle("Web Part Gallery");
  CamlQuery camlQuery = CamlQuery.CreateAllItemsQuery(100);
  Microsoft.SharePoint.Client.ListItemCollection items = list.GetItems(camlQuery);
  clientContext.Load(items);
  clientContext.ExecuteQuery();
  foreach (var item in items)
  {
    // Random group name to differentiate it from the rest.
    if (item["FileLeafRef"].ToString().ToLowerInvariant() == "userprofileinformation.webpart")
    {
      item["Group"] = "add-in Script Part";
      item.Update();
      clientContext.ExecuteQuery();
    }
  }

  lblStatus.Text = string.Format("add-in script part has been added to Web Part Gallery. You can find 'User Profile Information' script part under 'Add-in Script Part' group in the <a href='{0}'>host web</a>.", spContext.SPHostUrl.ToString());
}

Depois de concluir esta etapa, você pode localizar a parte de script de suplemento de informações de perfil de usuário dentro de uma nova categoria de Parte de Script de Suplemento na Galeria de Web Part. Depois de adicionar a parte de script de suplemento à página, o JavaScript em execução remota controla a exibição das informações na página.

Ao exibir a parte do script de suplemento no modo de edição, você verá que ele insira o arquivo JavaScript que está sendo executado remotamente. O scriptuserprofileinformation.js usa o JSON para obter informações do perfil do usuário do site do host.

function sharePointReady() {
  clientContext = SP.ClientContext.get_current();

  var fileref = document.createElement('script');
  fileref.setAttribute("type", "text/javascript");
  fileref.setAttribute("src", "/_layouts/15/SP.UserProfiles.js");
  document.getElementsByTagName("head")[0].appendChild(fileref);

  SP.SOD.executeOrDelayUntilScriptLoaded(function () {

    //Get Instance of People Manager Class.
    var peopleManager = new SP.UserProfiles.PeopleManager(clientContext);

    //Get properties of the current user.
    userProfileProperties = peopleManager.getMyProperties();
    clientContext.load(userProfileProperties);
    clientContext.executeQueryAsync(Function.createDelegate(this, function (sender, args) {
      var firstname = userProfileProperties.get_userProfileProperties()['FirstName'];
      var name = userProfileProperties.get_userProfileProperties()['PreferredName'];
      var title = userProfileProperties.get_userProfileProperties()['Title'];
      var aboutMe = userProfileProperties.get_userProfileProperties()['AboutMe'];
      var picture = userProfileProperties.get_userProfileProperties()['PictureURL'];

      var html = "<div><h2>Welcome " + firstname + "</h2></div><div><div style='float: left; margin-left:10px'><img style='float:left;margin-right:10px' src='" + picture + "' /><b>Name</b>: " + name + "<br /><b>Title</b>: " + title + "<br />" + aboutMe + "</div></div>";

      document.getElementById('UserProfileAboutMe').innerHTML = html;
    }), Function.createDelegate(this, function (sender, args) {
      console.log('The following error has occurred while loading user profile property: ' + args.get_message());
    }));
  }, 'SP.UserProfiles.js');
}

Provisionamento de recursos de publicação

O exemplo Provisioning.PublishingFeatures mostra como fazer tarefas comuns com sites de publicação hospedados em Office 365; por exemplo, provisionar e usar layouts de página, master páginas e temas ou inserir JavaScript em layouts de página. Ele também mostra como aplicar filtros que controlam quais modelos de site estão disponíveis para subsites e quais layouts de página estão disponíveis na Web do host.

O suplemento hospedado pelo provedor usa o CSOM para provisionar elementos de interface do usuário comumente usados em sites de publicação e usa JavaScript para criar experiências mais dinâmicas em layouts de página que você pode implantar em sites de publicação. Ele também mostra as diferenças entre o uso de master páginas e temas em sites de publicação.

Importante

Para que a funcionalidade neste exemplo funcione, você precisa ativar os recursos de publicação em seu site. Para obter informações, consulte Habilitar recursos de publicação.

A página inicial de exemplo apresenta três cenários para personalizar a interface do usuário de sites de publicação:

  • Implantar layouts de página.
  • Implante master páginas e temas.
  • Filtre os layouts de página disponíveis e os modelos de site no site do host.

Cenário 1: Implantar páginas

O cenário 1 mostra como implantar um layout de página personalizado. O botão Implantar layouts de página mostrados na figura a seguir cria um novo layout de página e uma página que usa esse layout.

Botão para implantar layouts de página

Botão que implanta layouts de páginas

Você pode exibir a nova página acessando a página de demonstração recém-criada em seu site de host, que contém JavaScript inserido e exibe informações do perfil do usuário.

O exemplo exibe as informações desse usuário na página. Ele também adiciona uma página, embora, nesse caso, use um objeto PublishingPageInformation para criar a nova página.

O exemplo adiciona um novo layout de página carregando um arquivo para a Galeria de Páginas Mestras e atribuindo-lhe o tipo de conteúdo de layout de página. O código a seguir usa o caminho para um arquivo *.aspx (que você pode implantar como um recurso em seu projeto do Visual Studio) e o adiciona como um layout de página na Galeria de Páginas Mestras.

// Get the path to the file that you are about to deploy.
List masterPageGallery = web.GetCatalog((int)ListTemplateType.MasterPageCatalog);
Folder rootFolder = masterPageGallery.RootFolder;
web.Context.Load(masterPageGallery);
web.Context.Load(rootFolder);
web.Context.ExecuteQuery();

var fileBytes = System.IO.File.ReadAllBytes(sourceFilePath);

// Use CSOM to upload the file.
FileCreationInformation newFile = new FileCreationInformation();
newFile.Content = fileBytes;
newFile.Url = UrlUtility.Combine(rootFolder.ServerRelativeUrl, fileName);
newFile.Overwrite = true;

Microsoft.SharePoint.Client.File uploadFile = rootFolder.Files.Add(newFile);
web.Context.Load(uploadFile);
web.Context.ExecuteQuery();

// Check out the file if needed.
if (masterPageGallery.ForceCheckout || masterPageGallery.EnableVersioning)
{
  if (uploadFile.CheckOutType == CheckOutType.None)
  {
    uploadFile.CheckOut();
  }
}

// Get content type for ID to assign associated content type information.
ContentType associatedCt = web.GetContentTypeById(associatedContentTypeID);

var listItem = uploadFile.ListItemAllFields;
listItem["Title"] = title;
listItem["MasterPageDescription"] = description;
// Set the item as page layout.
listItem["ContentTypeId"] = Constants.PAGE_LAYOUT_CONTENT_TYPE;
// Set the associated content type ID property
listItem["PublishingAssociatedContentType"] = string.Format(";#{0};#{1};#", associatedCt.Name, associatedCt.Id);
listItem["UIVersion"] = Convert.ToString(15);
listItem.Update();

// Check in the page layout if needed.
if (masterPageGallery.ForceCheckout || masterPageGallery.EnableVersioning)
{
  uploadFile.CheckIn(string.Empty, CheckinType.MajorCheckIn);
  listItem.File.Publish(string.Empty);
}
web.Context.ExecuteQuery();

Você pode verificar se sua nova página está usando o novo layout de página acessando a biblioteca Páginas do site do host.

Cenário 2: Implantar master páginas e temas

O Cenário 2 mostra como implantar e definir master páginas e temas para o site de host de um suplemento hospedado pelo provedor. Quando você escolhe Implantar master e usá-la na página inicial do exemplo, o exemplo implanta e aplica uma página de master personalizada ao site do host. Você pode ver a nova página master acessando a página inicial do site.

O exemplo adiciona uma nova página master carregando um arquivo *.master para a Galeria de Páginas Mestras e atribuindo-lhe o tipo de conteúdo da página master. O código a seguir leva o caminho para um arquivo *.master (que você pode implantar como um recurso em seu projeto do Visual Studio) e o adiciona como uma página master na Galeria de Página Mestra.

string fileName = Path.GetFileName(sourceFilePath);
// Get the path to the file that you are about to deploy.
List masterPageGallery = web.GetCatalog((int)ListTemplateType.MasterPageCatalog);
Folder rootFolder = masterPageGallery.RootFolder;
web.Context.Load(masterPageGallery);
web.Context.Load(rootFolder);
web.Context.ExecuteQuery();

// Get the file name from the provided path.
var fileBytes = System.IO.File.ReadAllBytes(sourceFilePath);

// Use CSOM to upload the file.
FileCreationInformation newFile = new FileCreationInformation();
newFile.Content = fileBytes;
newFile.Url = UrlUtility.Combine(rootFolder.ServerRelativeUrl, fileName);
newFile.Overwrite = true;

Microsoft.SharePoint.Client.File uploadFile = rootFolder.Files.Add(newFile);
web.Context.Load(uploadFile);
web.Context.ExecuteQuery();


var listItem = uploadFile.ListItemAllFields;
if (masterPageGallery.ForceCheckout || masterPageGallery.EnableVersioning)
{
  if (uploadFile.CheckOutType == CheckOutType.None)
  {
    uploadFile.CheckOut();
  }
}

listItem["Title"] = title;
listItem["MasterPageDescription"] = description;
// Set content type as master page.
listItem["ContentTypeId"] = Constants.MASTERPAGE_CONTENT_TYPE;
listItem["UIVersion"] = uiVersion;
listItem.Update();
if (masterPageGallery.ForceCheckout || masterPageGallery.EnableVersioning)
{
  uploadFile.CheckIn(string.Empty, CheckinType.MajorCheckIn);
  listItem.File.Publish(string.Empty);
}
web.Context.Load(listItem);
web.Context.ExecuteQuery();

A próxima etapa é definir a URL da nova página master como o valor para as propriedades MasterUrl e CustomMasterUrl do objeto Web que representa o site. O exemplo lida com isso com um único método que busca a URL da nova página master na Galeria de Página Mestra e atribui esse valor às propriedades Web.MasterUrl e Web.CustomMasterUrl.

// Assign master page to the host web.
clientContext.Web.SetMasterPagesForSiteByName("contoso.master", "contoso.master");

Quando você escolhe Implantar tema e usá-lo, o exemplo implanta e aplica um tema personalizado ao site do host. O exemplo define a paleta de cores, a imagem em segundo plano e o esquema de fonte do tema adicionando um novo tema com esses valores (que você pode implantar como recursos dentro do projeto do Visual Studio) à Galeria de Temas. O código a seguir cria o novo tema.

List themesOverviewList = web.GetCatalog((int)ListTemplateType.DesignCatalog);
web.Context.Load(themesOverviewList);
web.Context.ExecuteQuery();
ListItemCreationInformation itemInfo = new ListItemCreationInformation();
Microsoft.SharePoint.Client.ListItem item = themesOverviewList.AddItem(itemInfo);
item["Name"] = themeName;
item["Title"] = themeName;
if (!string.IsNullOrEmpty(colorFileName))
{
  item["ThemeUrl"] = UrlUtility.Combine(rootWeb.ServerRelativeUrl, string.Format(Constants.THEMES_DIRECTORY, Path.GetFileName(colorFileName)));
}
if (!string.IsNullOrEmpty(fontFileName))
{
  item["FontSchemeUrl"] = UrlUtility.Combine(rootWeb.ServerRelativeUrl, string.Format(Constants.THEMES_DIRECTORY, Path.GetFileName(fontFileName)));
}
if (!string.IsNullOrEmpty(backgroundName))
{
  item["ImageUrl"] = UrlUtility.Combine(rootWeb.ServerRelativeUrl, string.Format(Constants.THEMES_DIRECTORY, Path.GetFileName(backgroundName)));
}
item["DisplayOrder"] = 11;
item.Update();
web.Context.ExecuteQuery();

A próxima etapa é definir esse novo tema como o tema do site. O código a seguir faz isso buscando o tema na Galeria de Temas e, em seguida, aplicando seus valores ao site do host.

CamlQuery query = new CamlQuery();
// Find the theme by themeName.
string camlString = string.Format(CAML_QUERY_FIND_BY_FILENAME, themeName);
query.ViewXml = camlString;
var found = themeList.GetItems(query);
rootWeb.Context.Load(found);
LoggingUtility.Internal.TraceVerbose("Getting theme: {0}", themeName);
rootWeb.Context.ExecuteQuery();
if (found.Count > 0)
{
  ListItem themeEntry = found[0];

  // set the properties for applying the custom theme that was just uploaded.
  string spColorURL = null;
  if (themeEntry["ThemeUrl"] != null &amp;&amp; themeEntry["ThemeUrl"].ToString().Length > 0)
  {
    spColorURL = UrlUtility.MakeRelativeUrl((themeEntry["ThemeUrl"] as FieldUrlValue).Url);
  }
  string spFontURL = null;
  if (themeEntry["FontSchemeUrl"] != null &amp;&amp; themeEntry["FontSchemeUrl"].ToString().Length > 0)
  {
    spFontURL = UrlUtility.MakeRelativeUrl((themeEntry["FontSchemeUrl"] as FieldUrlValue).Url);
  }
  string backGroundImage = null;
  if (themeEntry["ImageUrl"] != null &amp;&amp; themeEntry["ImageUrl"].ToString().Length > 0)
  {
    backGroundImage = UrlUtility.MakeRelativeUrl((themeEntry["ImageUrl"] as FieldUrlValue).Url);
  }

  // Set theme for demonstration.
  // TODO: Why is shareGenerated false? If deploying to root and inheriting, maybe use shareGenerated = true.
  web.ApplyTheme(spColorURL,
                      spFontURL,
                      backGroundImage,
                      false);
  web.Context.ExecuteQuery();

Cenário 3: Filtrar layouts de página disponíveis e modelos de site

O Cenário 3 mostra como limitar as opções que os usuários têm quando aplicam modelos a novos sites e layouts a novas páginas. Quando você escolhe Aplicar filtros para hospedar a Web na página inicial do exemplo, o exemplo define um layout de página personalizado como o layout de página padrão e um layout de página adicional como a única outra opção para quaisquer novas páginas que um usuário cria. O exemplo também reduz o número de opções disponíveis para os usuários quando eles criam novos subsites. A figura a seguir mostra como é a caixa de seleção do modelo de site antes e depois da aplicação dos filtros.

Seleção de modelo de site antes e após a aplicação dos filtros do modelo

Seleção de modelo de site antes e após a aplicação dos filtros do modelo

O exemplo define os layouts de página padrão e disponíveis passando os arquivos *.aspx associados para métodos para métodos de extensão, conforme mostrado no código.

List<string> pageLayouts = new List<string>();
pageLayouts.Add("ContosoLinksBelow.aspx");
pageLayouts.Add("ContosoLinksRight.aspx");
clientContext.Web.SetAvailablePageLayouts(clientContext.Web, pageLayouts);

// Set default page layout for the site.
clientContext.Web.SetDefaultPageLayoutForSite(clientContext.Web, "ContosoLinksBelow.aspx");

O exemplo define os modelos de site disponíveis fazendo algo semelhante. Nesse caso, ele passa as instâncias WebTemplateEntity que definem cada modelo de site para um método de extensão chamado SetAvailableWebTemplates.

List<WebTemplateEntity> templates = new List<WebTemplateEntity>();
templates.Add(new WebTemplateEntity() { LanguageCode = "1035", TemplateName = "STS#0" });
templates.Add(new WebTemplateEntity() { LanguageCode = "", TemplateName = "STS#0" });
templates.Add(new WebTemplateEntity() { LanguageCode = "", TemplateName = "BLOG#0" });
clientContext.Web.SetAvailableWebTemplates(templates);

Todos esses três métodos de extensão — SetAvailablePageLayouts, SetDefaultPageLayoutForSite e SetAvailableWebTemplates — funcionam da mesma maneira. Eles criam documentos XML que contêm pares de chave/valor que definem os layouts disponíveis e padrão e os modelos disponíveis. Em seguida, eles passam esses documentos para um método de extensão adicional chamado SetPropertyBagValue. Esse método é implementado na extensão Do OfficeDevPnP Core. Depois de configurar os sacos de propriedade apropriados, esses sacos de propriedade são usados para filtrar opções na interface.

Dos três métodos, SetAvailableWebTemplates mostra o padrão completo.

public static void SetAvailableWebTemplates(this Web web, List<WebTemplateEntity> availableTemplates)
{
  string propertyValue = string.Empty;

  LanguageTemplateHash languages = new LanguageTemplateHash();
  foreach (var item in availableTemplates)
  {
    AddTemplateToCollection(languages, item);
  }

  if (availableTemplates.Count > 0)
  {
    XmlDocument xd = new XmlDocument();
    XmlNode xmlNode = xd.CreateElement("webtemplates");
    xd.AppendChild(xmlNode);
    foreach (var language in languages)
    {
      XmlNode xmlLcidNode = xmlNode.AppendChild(xd.CreateElement("lcid"));
      XmlAttribute xmlAttribute = xd.CreateAttribute("id");
      xmlAttribute.Value = language.Key;
      xmlLcidNode.Attributes.SetNamedItem(xmlAttribute);

      foreach (string item in language.Value)
      {
        XmlNode xmlWTNode = xmlLcidNode.AppendChild(xd.CreateElement("webtemplate"));
        XmlAttribute xmlAttributeName = xd.CreateAttribute("name");
        xmlAttributeName.Value = item;
        xmlWTNode.Attributes.SetNamedItem(xmlAttributeName);
      }
    }
    propertyValue = xmlNode.OuterXml;
  }
  // Save the XML entry to property bag.
  web.SetPropertyBagValue(AvailableWebTemplates, propertyValue);
  // Set that templates are not inherited.
  web.SetPropertyBagValue(InheritWebTemplates, "False");
}

O saco de propriedades InheritWebTemplates garante que todos os modelos que normalmente são herdados do site pai também sejam ignorados quando você cria subsites.

Confira também