Partager via


Personnaliser l’expérience utilisateur à l’aide de compléments hébergés par un fournisseur SharePoint

Cet article décrit des exemples qui montrent les meilleures pratiques pour la personnalisation des composants UX SharePoint, notamment les scénarios suivants :

  • Manipulation de page (ajout et modification d’une page wiki)

  • Affichage des compléments et des données dans les boîtes de dialogue modales

  • Création d’éléments d’interface utilisateur personnalisés

  • Rendu côté client (déploiement de fichiers JSLink qui personnalisent le rendu des champs dans les listes SharePoint)

  • Manipulation du composant WebPart et du composant de complément (provisionner et exécuter à distance un composant script de complément dans un complément hébergé par un fournisseur)

  • Agrégation et mise en cache des données (utilisation du stockage local HTML5 et des cookies HTTP pour réduire le nombre d’appels de service à SharePoint)

Remarque

Le code dans cet article est fourni tel quel, sans garantie d’aucune sorte, expresse ou implicite, y compris mais sans s’y limiter, aucune garantie implicite d’adéquation à un usage particulier, à une qualité marchande ou une absence de contrefaçon.

Manipulation de pages

L’exemple Core.ModifyPages comprend deux scénarios de manipulation de page :

  • Créez une page wiki.
  • Modifier la disposition d’une page wiki.

Cet exemple utilise la bibliothèque de pages de site par défaut et les dispositions prêtes à l’emploi existantes. Vous pouvez également le mettre à jour pour utiliser une bibliothèque de pages Wiki personnalisée et des mises en page personnalisées. L’interface utilisateur du complément comprend deux boutons qui créent les deux pages wiki et deux liens pour afficher les pages wiki que vous créez.

Page de démarrage de l’exemple de manipulation de page

Page de lancement pour l’exemple de manipulation de page


L’exemple de code du premier scénario détermine si vous avez déjà créé la page wiki. Si ce n’est pas le cas, il ajoute le fichier à la bibliothèque de pages de site et retourne son URL.

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

Dans les deux scénarios, l’exemple ajoute le code HTML entré via la zone de texte de la page de démarrage à l’aide de la méthode AddHtmlToWikiPage dans une classe d’assistance nommée LabHelper. Cette méthode insère le code HTML du formulaire dans le champ WikiField de la page 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();
        }

L’exemple de code du deuxième scénario crée une nouvelle instance WebPartEntity. Il utilise ensuite des méthodes dans une classe d’assistance pour remplir le composant WebPart avec du code XML. Le code XML affiche une liste de liens promus, y compris à la fois http://www.bing.com et la page d’accueil du dépôt GitHub modèles et pratiques des développeurs Office 365.

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);

Le code d’assistance affiche les liens promus avec une table à l’intérieur d’un composant WebPart XsltListViewWebPart .

Deuxième page wiki avec XsltListViewWebPart et table de liens promus

Deuxième page wiki avec composant XsltListViewWeb et tableau de liens promus


L’objet WpPromotedLinks sur le instance LabHelper contient du code XML qui définit l’apparence du composant WebPart qui sera incorporé dans la page wiki. La méthode AddWebPartToWikiPage insère ensuite le composant WebPart nouvellement défini dans une nouvelle div balise sur la page 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();

Affichage des compléments et des données dans les boîtes de dialogue modales

L’exemple Core.Dialog montre deux méthodes pour incorporer des liens de boîte de dialogue modal. Ces liens affichent une page de complément hébergé par un fournisseur dans un site hôte SharePoint. Le complément utilise le modèle objet client (CSOM) pour créer l’action personnalisée et JavaScript pour démarrer et afficher des informations dans la boîte de dialogue. Étant donné que certaines de ces informations proviennent du site hôte, elles utilisent également le modèle objet JavaScript (JSOM) pour récupérer des informations à partir du site hôte. Étant donné que le complément s’exécute dans un domaine différent de celui du site hôte SharePoint, il utilise également la bibliothèque inter-domaines SharePoint pour effectuer les appels au site hôte.

Remarque

Pour plus d’informations sur l’utilisation de la bibliothèque inter-domaines dans ce scénario, voir Accéder aux données SharePoint à partir de compléments à l’aide de la bibliothèque inter-domaines.

La page de démarrage est la page qui apparaît dans la boîte de dialogue. Pour gérer les différences d’affichage en fonction du contexte d’affichage (boîte de dialogue et pleine page), le complément détermine s’il est affiché dans une boîte de dialogue. Pour ce faire, il utilise un paramètre de chaîne de requête qui est transmis avec les liens qui démarrent les boîtes de dialogue.

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());
        }

Vous pouvez, par exemple, choisir d’afficher certains éléments d’interface utilisateur (tels que des boutons) ou même différentes mises en page, selon que le contenu est affiché ou non dans une boîte de dialogue.

L’interface utilisateur de la page de démarrage présente deux options pour créer des liens vers la boîte de dialogue, ainsi qu’une liste de toutes les listes sur le site web hôte. Il présente également les boutons OK et Annuler que vous pouvez utiliser dans le contexte de la boîte de dialogue pour fermer la boîte de dialogue et/ou demander des actions supplémentaires dans le complément.

Lorsque vous choisissez le bouton Ajouter un élément de menu , le complément crée un objet CustomActionEntity qui démarre la boîte de dialogue. Il utilise ensuite la méthode d’extension OfficeDevPnP Core nommée AddCustomAction pour ajouter la nouvelle action personnalisée au menu Paramètres du 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);

La méthode AddCustomAction ajoute l’action personnalisée à la collection UserCustomActions associée au site 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();

Lorsque vous choisissez le bouton Ajouter une page avec le composant WebPart Éditeur de script, le complément utilise les méthodes AddWikiPage et AddWebPartToWikiPage pour créer une page wiki à l’intérieur de la bibliothèque de pages de site et ajouter un composant WebPart Éditeur de script configuré à la page.

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);

La méthode AddWikiPage charge la bibliothèque de pages de site. Si la page wiki spécifiée par le complément OfficeDevPnP Core n’existe pas déjà, elle crée la page.

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;
        }

La méthode AddWebPartToWikiPage insère le composant WebPart nouvellement défini à l’intérieur d’une nouvelle <div> balise sur la page wiki. Il utilise ensuite JSOM et la bibliothèque inter-domaines pour récupérer les informations qui remplissent la liste des listes SharePoint sur le site web hôte.

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

Éléments d’interface utilisateur personnalisés

L’exemple Branding.UIElementPersonalization montre comment utiliser javascript incorporé et les valeurs stockées dans les profils utilisateur et les listes SharePoint pour personnaliser les éléments d’interface utilisateur sur le site web hôte. Il utilise également le stockage local HTML5 pour réduire les appels au site hôte.

La page de démarrage de l’exemple vous invite à ajouter l’une des trois chaînes (XX, YY ou ZZ) à la section À propos de moi de votre page de profil utilisateur.

Le complément a déjà déployé trois images et une liste SharePoint qui contient des titres et des URL pour chaque image, ainsi qu’un champ supplémentaire qui lie chaque image à l’une des trois chaînes. Une fois que vous avez choisi le bouton Injecter la personnalisation , le complément incorpore le fichier personalize.js dans la collection d’actions personnalisées de l’utilisateur.

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();
        }

Étant donné que les sites d’équipe SharePoint utilisent par défaut la stratégie de téléchargement minimal (MDS), le code du fichier personalize.js tente d’abord de s’inscrire auprès de MDS. De cette façon, lorsque vous chargez la page qui contient le Code JavaScript, le moteur MDS démarre la fonction main (RemoteManager_Inject). Si MDS est désactivé, cette fonction démarre immédiatement.

// 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();

    });
}

La fonction personalizeIt() vérifie le stockage local HTML5 avant de rechercher les informations de profil utilisateur. S’il accède aux informations de profil utilisateur, il stocke les informations qu’il récupère dans le stockage 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');
}

Le fichier personalize.js contient également du code qui vérifie si la clé de stockage locale a expiré. Ce n’est pas intégré au stockage 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;
    }
}

Affichage côté client

L’exemple Branding.ClientSideRendering montre comment utiliser un complément hébergé par un fournisseur pour approvisionner à distance des artefacts SharePoint et des fichiers JSLink qui utilisent le rendu côté client pour personnaliser l’apparence et le comportement des champs de liste SharePoint. Les fichiers JSLink et le rendu côté client vous permettent de contrôler le rendu des contrôles d’une page SharePoint (affichages de liste, champs de liste et formulaires d’ajout et de modification). Ce contrôle peut réduire ou éliminer le besoin de types de champs personnalisés. Le rendu côté client permet de contrôler à distance l’apparence des champs de liste à distance.

L’exemple combine les exemples JSLink des exemples de code de rendu côté client (JSLink) dans un complément hébergé par un fournisseur unique pour SharePoint qui provisionne les fichiers JSLink. Le rendu côté client vous permet d’utiliser des technologies web standard, telles que HTML et JavaScript, pour définir la logique de rendu des types de champs personnalisés et prédéfinis.

Lorsque vous démarrez l’exemple, la page de démarrage vous invite à provisionner tous les exemples.

Page de démarrage de l’exemple de rendu côté client

Page de lancement de l’échantillon de rendu côté client

Lorsque vous choisissez Approvisionner des exemples, le complément déploie une image ainsi que l’ensemble des listes SharePoint, des affichages de liste, des éléments de liste, des formulaires et des fichiers JavaScript utilisés dans chaque exemple. Le complément crée un dossier nommé JSLink-Samples dans la bibliothèque de styles, puis charge les fichiers JavaScript dans ce dossier. La méthode UploadFileToFolder effectue le travail de chargement et d’archivage de chaque fichier 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();
            }
        }

Exemple 1 : Appliquer la mise en forme à une colonne de liste

L’exemple 1 montre comment appliquer une mise en forme à une colonne de liste en fonction de la valeur du champ. Les valeurs de champ de priorité 1 (Élevé), 2 (Normal) et 3 (Faible) sont affichées en rouge, orange et jaune.

Affichage des champs de liste personnalisée

Affichage des champs de liste personnalisée

Le code JavaScript suivant remplace l’affichage des champs par défaut et crée un modèle d’affichage pour le champ De liste priorité . La technique de la fonction anonyme qui charge les informations contextuelles sur le champ dont vous souhaitez remplacer l’affichage est utilisée dans tous les exemples.

(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>";
    }
}

Exemple 2 : raccourcir le texte long

L’exemple 2 montre comment tronquer du texte long stocké dans le champ Corps d’une liste Annonces . Le texte intégral s’affiche sous la forme d’une fenêtre contextuelle qui s’affiche chaque fois que vous pointez sur un élément de liste, comme illustré dans la figure suivante.

Affichage d’un champ de liste raccourci affichant une fenêtre contextuelle

Affichage des champs de liste tronqués avec le menu contextuel

Le code JavaScript suivant raccourcit le texte du champ Corps et fait apparaître le texte intégral sous forme de fenêtre contextuelle via l’attribut title sur la balise d’étendue .

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>";

}

Exemple 3 : Afficher une image avec un nom de document

L’exemple 3 montre comment afficher une image à côté d’un nom de document dans une bibliothèque de documents. Un badge rouge s’affiche chaque fois que la valeur du champ Confidentiel est définie sur Oui.

Image affichée en regard du nom du document

Image affichée en regard du nom du document

Le code JavaScript suivant vérifie la valeur du champ Confidentiel , puis personnalise l’affichage du champ Nom en fonction de la valeur d’un autre champ. L’exemple utilise l’image qui est chargée lorsque vous choisissez Approvisionner des exemples.

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;
    }
}

Exemple 4 : Afficher un graphique à barres

L’exemple 4 montre comment afficher un graphique à barres dans le champ % terminé d’une liste de tâches. L’apparence du graphique à barres dépend de la valeur du champ % terminé , comme illustré dans la figure suivante. Notez qu’un graphique à barres apparaît également dans les formulaires pour créer et modifier des éléments de liste de tâches.

Diagramme à barres affiché dans le champ « % effectué »

Diagramme à barres affiché dans le champ « % effectué »

Le code suivant crée l’affichage du graphique à barres et l’associe aux formulaires d’affichage et d’affichage (percentCompleteViewFiledTemplate), puis aux formulaires new et edit (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>%";
}

Exemple 5 : Modifier le modèle de rendu

L’exemple 5 montre comment modifier le modèle de rendu pour un affichage de liste. Cet affichage affiche les titres des éléments de liste qui se développent comme des accordéons lorsque vous les sélectionnez. La vue développée affiche des champs d’élément de liste supplémentaires.

Vues des éléments dans les listes développées et réduites

Vues des éléments dans les listes développées et réduites

Le code suivant configure le modèle et l’inscrit auprès du modèle de liste. Il configure la disposition globale, puis utilise le gestionnaire d’événements OnPostRender pour inscrire la fonction JavaScript qui s’exécute lorsque la liste est affichée. Cette fonction associe l’événement au CSS et à la gestion des événements qui implémente la fonctionnalité d’accordéon.

(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');
}

Exemple 6 : Valider les valeurs des champs

L’exemple 6 montre comment utiliser des expressions régulières pour valider les valeurs de champ fournies par l’utilisateur. Un message d’erreur rouge s’affiche lorsque l’utilisateur tape une adresse e-mail non valide dans la zone de texte du champ Email. Cela se produit lorsque l’utilisateur crée ou modifie un élément de liste.

Message d’erreur affiché en cas de saisie d’un texte non valide dans un champ

Message d’erreur affiché en cas de saisie d’un texte non valide dans un champ

Le code suivant configure le modèle avec un espace réservé pour afficher le message d’erreur et inscrit les fonctions de rappel qui se déclenchent chaque fois que l’utilisateur tente d’envoyer les formulaires. Le premier rappel retourne la valeur de la colonne Email, et le deuxième rappel utilise des expressions régulières pour valider la valeur de chaîne.

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>";
}

Exemple 7 : Rendre les champs de modification d’élément de liste en lecture seule

L’exemple sept montre comment modifier des champs de formulaire d’élément de liste en lecture seule. Les champs en lecture seule s’affichent sans contrôle d’édition.

Champ en lecture seule dans un formulaire de modification de liste personnalisé

Champs en lecture seule dans un formulaire de modification de liste personnalisée

L’exemple de code suivant modifie les champs Title, AssignedTo et Priority dans le formulaire de modification de l’élément de liste afin qu’ils affichent les valeurs des champs uniquement sans contrôle de modification. Le code montre comment gérer les exigences d’analyse pour différents types de champs.

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;
  }
}

Exemple 8 : Masquer les champs

L’exemple 8 montre comment masquer les champs dans les formulaires nouveaux et de modification d’élément de liste. L’exemple masque le champ Prédécesseurs lorsqu’un utilisateur crée ou modifie un élément de liste de tâches.

Cet exemple est déployé en tant que formulaire de modification et de nouveau pour une liste appelée liste CSR-Hide-Controls. Pour plus d’informations sur la façon d’afficher le formulaire après avoir déployé l’exemple, consultez Branding.ClientSideRendering.

Le code suivant recherche le champ Prédécesseurs dans le code HTML du formulaire et le masque. Le champ reste présent dans le code HTML, mais l’utilisateur ne peut pas le voir dans le navigateur.

(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();
}

Manipulation du composant WebPart et du composant de complément

L’exemple Core.AppScriptPart montre comment utiliser des composants de script de complément pour incorporer des scripts s’exécutant dans un complément hébergé par un fournisseur sur une page SharePoint. Cet exemple montre comment modifier l’interface utilisateur d’une page sur le site hôte en déployant un composant script de complément et en l’ajoutant à une page SharePoint à partir de la galerie de composants WebPart.

Un composant de script de complément est semblable à un composant WebPart, car vous pouvez l’ajouter à une page SharePoint à partir de la galerie de composants WebPart, mais dans ce cas, le fichier .webpart incorpore un fichier JavaScript qui s’exécute à distance dans un complément hébergé par un fournisseur. Le composant script de complément s’exécute à l’intérieur d’une <div> balise de la page SharePoint et fournit donc une conception et une expérience plus réactives qu’avec les composants de complément qui s’exécutent dans des IFrames.

La page de démarrage comprend un bouton Exécuter le scénario qui déploie le composant script de complément dans la galerie de composants WebPart. L’exemple de code suivant construit une instance FileCreationInformationObject qui contient le contenu du fichier .webpart, puis charge le nouveau fichier dans la galerie de composants WebPart. Notez que vous pouvez également exécuter ce code automatiquement lorsque le composant de complément est installé ou dans le cadre du processus d’approvisionnement de la collection 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());
}

Une fois cette étape terminée, vous pouvez localiser le composant script du complément Informations de profil utilisateur dans une nouvelle catégorie composant de script de complément dans la galerie de composants WebPart. Une fois que vous avez ajouté le composant script de complément à la page, l’exécution à distance de JavaScript contrôle l’affichage des informations sur la page.

Lorsque vous affichez le composant script de complément en mode édition, vous voyez qu’il incorpore le fichier JavaScript qui s’exécute à distance. Le script userprofileinformation.js utilise le JSON pour obtenir des informations de profil utilisateur à partir du site hôte.

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');
}

Provisionnement des fonctionnalités de publication

L’exemple Provisioning.PublishingFeatures montre comment effectuer des tâches courantes avec des sites de publication hébergés sur Office 365; par exemple, l’approvisionnement et l’utilisation de mises en page, master pages et thèmes, ou l’incorporation de JavaScript dans des mises en page. Il montre également comment appliquer des filtres qui contrôlent les modèles de site disponibles pour les sous-sites et les mises en page disponibles sur le site web hôte.

Le complément hébergé par un fournisseur utilise CSOM pour approvisionner les éléments d’interface utilisateur couramment utilisés sur les sites de publication, et il utilise JavaScript pour créer des expériences plus dynamiques dans les mises en page que vous pouvez déployer sur des sites de publication. Il montre également les différences entre l’utilisation de pages et de thèmes master dans les sites de publication.

Importante

Pour que les fonctionnalités de cet exemple fonctionnent, vous devez activer les fonctionnalités de publication sur votre site. Pour plus d’informations, consultez Activer les fonctionnalités de publication.

L’exemple de page de démarrage présente trois scénarios de personnalisation de l’interface utilisateur des sites de publication :

  • Déployer des mises en page.
  • Déployez master pages et thèmes.
  • Filtrez les mises en page et les modèles de site disponibles sur le site hôte.

Scénario 1 : Déployer des pages

Le scénario 1 vous montre comment déployer une mise en page personnalisée. Le bouton Déployer des mises en page illustré dans la figure suivante crée une mise en page et une page qui utilise cette mise en page.

Bouton pour déployer des mises en page

Bouton qui déploie les mises en page

Vous pouvez afficher la nouvelle page en accédant à la page de démonstration nouvellement créée sur votre site hôte, qui contient du code JavaScript incorporé et affiche les informations de profil utilisateur.

L’exemple affiche les informations de cet utilisateur sur la page. Il ajoute également une page, bien que dans ce cas, il utilise un objet PublishingPageInformation pour créer la nouvelle page.

L’exemple ajoute une nouvelle mise en page en charge un fichier dans la galerie de pages maîtres et en lui affectant le type de contenu de mise en page. Le code suivant prend le chemin d’accès à un fichier *.aspx (que vous pouvez déployer en tant que ressource dans votre projet Visual Studio) et l’ajoute en tant que mise en page dans la galerie de pages maîtres.

// 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();

Vous pouvez vérifier que votre nouvelle page utilise la nouvelle mise en page en accédant à la bibliothèque Pages du site hôte.

Scénario 2 : Déployer master pages et thèmes

Le scénario 2 vous montre comment déployer et définir master pages et thèmes pour le site hôte à partir d’un complément hébergé par un fournisseur. Lorsque vous choisissez Déployer master et que vous l’utilisez dans l’exemple de page de démarrage, l’exemple déploie et applique une page master personnalisée au site hôte. Vous pouvez voir la nouvelle page master en accédant à la page d’accueil du site.

L’exemple ajoute une nouvelle page master en chargeant un fichier *.master dans la galerie de pages maîtres et en lui attribuant le type de contenu master page. Le code suivant prend le chemin d’un fichier *.master (que vous pouvez déployer en tant que ressource dans votre projet Visual Studio) et l’ajoute en tant que page master dans la galerie de pages maîtres.

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();

L’étape suivante consiste à définir l’URL de la nouvelle page master comme valeur pour les propriétés MasterUrl et CustomMasterUrl de l’objet Web qui représente le site. L’exemple gère cela avec une seule méthode qui extrait l’URL de la nouvelle page master dans la galerie de pages maîtres, puis affecte cette valeur aux propriétés Web.MasterUrl et Web.CustomMasterUrl.

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

Lorsque vous choisissez Déployer le thème et l’utilisez, l’exemple déploie et applique un thème personnalisé au site hôte. L’exemple définit la palette de couleurs, l’image d’arrière-plan et le jeu de polices du thème en ajoutant un nouveau thème avec ces valeurs (que vous pouvez déployer en tant que ressources à l’intérieur de votre projet Visual Studio) à la galerie de thèmes. Le code suivant crée le nouveau thème.

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();

L’étape suivante consiste à définir ce nouveau thème comme thème pour le site. Pour ce faire, le code suivant extrait le thème de la galerie de thèmes, puis applique ses valeurs au site hôte.

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();

Scénario 3 : Filtrer les mises en page disponibles et les modèles de site

Le scénario 3 vous montre comment limiter les options dont disposent les utilisateurs lorsqu’ils appliquent des modèles à de nouveaux sites et des mises en page à de nouvelles pages. Lorsque vous choisissez Appliquer des filtres au site web hôte sur l’exemple de page de démarrage, l’exemple définit une mise en page personnalisée comme mise en page par défaut et une mise en page supplémentaire comme seule autre option pour les nouvelles pages créées par un utilisateur. L’exemple réduit également le nombre d’options disponibles pour les utilisateurs lorsqu’ils créent des sous-sites. La figure suivante montre à quoi ressemble la zone de sélection du modèle de site avant et après l’application des filtres.

Sélection du modèle de site avant et après l’application des filtres d’échantillon

Sélection du modèle de site avant et après l’application des filtres d’échantillon

L’exemple définit les mises en page par défaut et disponibles en passant les fichiers *.aspx associés aux méthodes aux méthodes d’extension, comme indiqué dans le code.

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");

L’exemple définit les modèles de site disponibles en effectuant une opération similaire. Dans ce cas, il transmet les instances WebTemplateEntity qui définissent chaque modèle de site à une méthode d’extension appelée 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);

Les trois méthodes d’extension SetAvailablePageLayouts, SetDefaultPageLayoutForSite et SetAvailableWebTemplates fonctionnent de la même façon. Ils créent des documents XML qui contiennent des paires clé/valeur qui définissent les dispositions disponibles et par défaut, ainsi que les modèles disponibles. Ils passent ensuite ces documents à une méthode d’extension supplémentaire appelée SetPropertyBagValue. Cette méthode est implémentée dans l’extension OfficeDevPnP Core. Après avoir configuré les sacs de propriétés appropriés, ces sacs de propriétés sont ensuite utilisés pour filtrer les options dans l’interface.

Sur les trois méthodes, SetAvailableWebTemplates affiche le modèle complet.

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");
}

Le conteneur de propriétés InheritWebTemplates garantit que tous les modèles normalement hérités du site parent sont également ignorés lorsque vous créez des sous-sites.

Voir aussi