Freigeben über


Anpassen der Benutzeroberfläche mithilfe von vom SharePoint-Anbieter gehosteten Add-Ins

In diesem Artikel werden Beispiele beschrieben, die bewährte Methoden zum Anpassen von SharePoint-UX-Komponenten zeigen, einschließlich der folgenden Szenarien:

  • Seitenbearbeitung (Hinzufügen und Ändern einer Wiki-Seite)

  • Anzeigen von Add-Ins und Daten in modalen Dialogfeldern

  • Erstellen personalisierter Benutzeroberflächenelemente

  • Clientseitiges Rendering (Bereitstellen von JSLink-Dateien, die das Rendern von Feldern in SharePoint-Listen anpassen)

  • Webpart- und Add-In-Part-Bearbeitung (Remotebereitstellung und Ausführung eines Add-In-Skriptteils in einem vom Anbieter gehosteten Add-In)

  • Datenaggregation und Zwischenspeicherung (mithilfe von lokalem HTML5-Speicher und HTTP-Cookies, um die Anzahl von Dienstaufrufen an SharePoint zu reduzieren)

Hinweis

Der Code in diesem Artikel wird wie besehen und ohne jegliche Garantie zur Verfügung gestellt, gleich ob ausdrücklich oder konkludent, einschließlich jedweder stillschweigenden Gewährleistung der Eignung für einen bestimmten Zweck, Marktgängigkeit oder Nichtverletzung von Rechten.

Seitenbearbeitung

Das Core.ModifyPages-Beispiel umfasst zwei Seitenbearbeitungsszenarien :

  • Erstellen Sie eine Wiki-Seite.
  • Ändern sie das Layout einer Wiki-Seite.

In diesem Beispiel werden die Standardbibliothek für Websiteseiten und vorhandene sofort einsatzbereite Layouts verwendet. Sie können es auch aktualisieren, um eine benutzerdefinierte Wiki-Seitenbibliothek und benutzerdefinierte Layouts zu verwenden. Die Add-In-Benutzeroberfläche enthält zwei Schaltflächen, die beide Wiki-Seiten erstellen, und zwei Links zum Anzeigen der von Ihnen erstellten Wiki-Seiten.

Startseite für das Beispiel für die Seitenbearbeitung

Startseite für das Seitenbearbeitungsbeispiel


Der Beispielcode für das erste Szenario bestimmt, ob Sie die Wiki-Seite bereits erstellt haben. Andernfalls wird die Datei der Websiteseitenbibliothek hinzugefügt und ihre URL zurückgegeben.

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

In beiden Szenarien fügt das Beispiel den HTML-Code hinzu, der über das Textfeld auf der Startseite mithilfe der AddHtmlToWikiPage-Methode in einer Hilfsklasse namens LabHelper eingegeben wird. Diese Methode fügt den HTML-Code aus dem Formular innerhalb des WikiField-Felds der Wiki-Seite ein.

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

Der Beispielcode für das zweite Szenario erstellt eine neue WebPartEntity-instance. Anschließend werden Methoden in einer Hilfsklasse verwendet, um das Webpart mit XML aufzufüllen. Im XML-Code wird eine Liste höhergestufter Links angezeigt, einschließlich http://www.bing.com der Homepage des GitHub-Repositorys Office 365 Developer Patterns and Practices.

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

Der Hilfscode zeigt die heraufgestuften Links mit einer Tabelle in einem XsltListViewWebPart-Webpart an.

Zweite Wiki-Seite mit XsltListViewWebPart und höher gestufter Linktabelle

Zweite Wiki-Seite mit XsltListViewWeb-Teil und höher gestufter Linktabelle


Das WpPromotedLinks-Objekt im LabHelper-instance enthält XML, das die Darstellung des Webparts definiert, das in die Wiki-Seite eingebettet wird. Die AddWebPartToWikiPage-Methode fügt dann das neu definierte Webpart in ein neues div Tag auf der Wiki-Seite ein.

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

Anzeigen von Add-Ins und Daten in modalen Dialogfeldern

Das Core.Dialog-Beispiel zeigt zwei Methoden zum Einbetten modaler Dialogfeldlinks. Diese Links zeigen eine vom Anbieter gehostete Add-In-Seite auf einer SharePoint-Hostwebsite an. Das Add-In verwendet das Clientobjektmodell (CSOM) zum Erstellen der benutzerdefinierten Aktion und JavaScript zum Starten und Anzeigen von Informationen im Dialogfeld. Da einige dieser Informationen von der Hostwebsite stammen, wird auch das JavaScript-Objektmodell (JSOM) verwendet, um Informationen von der Hostwebsite abzurufen. Und da das Add-In in einer anderen Domäne als die SharePoint-Hostwebsite ausgeführt wird, verwendet es auch die domänenübergreifende SharePoint-Bibliothek, um die Aufrufe an die Hostwebsite zu tätigen.

Hinweis

Weitere Informationen zur Verwendung der domänenübergreifenden Bibliothek in diesem Szenario finden Sie unter Zugreifen auf SharePoint-Daten aus Add-Ins mithilfe der domänenübergreifenden Bibliothek.

Die Startseite ist die Seite, die im Dialogfeld angezeigt wird. Das Add-In bestimmt, ob es in einem Dialogfeld in einem Dialogfeld angezeigt wird, um unterschiede bei der Anzeige des Anzeigekontexts (Dialogfeld im Vergleich zur ganzseitigen Darstellung) zu behandeln. Dazu wird ein Abfragezeichenfolgenparameter verwendet, der zusammen mit den Links übergeben wird, die die Dialogfelder starten.

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

Sie können z. B. bestimmte Ui-Elemente (z. B. Schaltflächen) oder sogar unterschiedliche Seitenlayouts anzeigen, je nachdem, ob der Inhalt in einem Dialogfeld angezeigt wird oder nicht.

Die Startseiten-Benutzeroberfläche bietet zwei Optionen zum Erstellen von Links zum Dialogfeld sowie eine Liste aller Listen im Hostweb. Außerdem werden die Schaltflächen OK und Abbrechen angezeigt, die Sie im Dialogfeldkontext verwenden können, um das Dialogfeld zu schließen und/oder zusätzliche Aktionen im Add-In aufzufordern.

Wenn Sie auf die Schaltfläche Menüelement hinzufügen klicken, erstellt das Add-In ein CustomActionEntity-Objekt , das das Dialogfeld startet. Anschließend wird die OfficeDevPnP Core-ErweiterungsmethodeaddCustomAction verwendet, um die neue benutzerdefinierte Aktion dem Menü Websiteeinstellungen hinzuzufügen.

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

Die AddCustomAction-Methode fügt die benutzerdefinierte Aktion der UserCustomActions-Auflistung hinzu, die der SharePoint-Website zugeordnet ist.

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

Wenn Sie die Schaltfläche Seite mit Skript-Editor-Webpart hinzufügen auswählen, verwendet das Add-In die Methoden AddWikiPage und AddWebPartToWikiPage , um eine Wiki-Seite innerhalb der Websiteseitenbibliothek zu erstellen und der Seite ein konfiguriertes Skript-Editor-Webpart hinzuzufügen.

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

Die AddWikiPage-Methode lädt die Websiteseitenbibliothek. Wenn die vom Add-In OfficeDevPnP Core angegebene Wiki-Seite noch nicht vorhanden ist, wird die Seite erstellt.

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

Die AddWebPartToWikiPage-Methode fügt das neu definierte Webpart in ein neues <div> Tag auf der Wiki-Seite ein. Anschließend wird JSOM und die domänenübergreifende Bibliothek verwendet, um die Informationen abzurufen, die die Liste der SharePoint-Listen im Hostweb auffüllen.

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

Personalisierte Ui-Elemente

Das Branding.UIElementPersonalization-Beispiel zeigt, wie Eingebettetes JavaScript und in Benutzerprofilen und SharePoint-Listen gespeicherte Werte verwendet werden, um Ui-Elemente im Hostweb zu personalisieren. Außerdem wird lokaler HTML5-Speicher verwendet, um Aufrufe der Hostwebsite zu minimieren.

Auf der Startseite des Beispiels werden Sie aufgefordert, eine von drei Zeichenfolgen (XX, YY oder ZZ) zum Abschnitt "Über mich " Ihrer Benutzerprofilseite hinzuzufügen.

Das Add-In hat bereits drei Bilder und eine SharePoint-Liste bereitgestellt, die Titel und URLs für jedes Bild enthält, sowie ein zusätzliches Feld, das jedes Bild mit einer der drei Zeichenfolgen verknüpft. Nachdem Sie die Schaltfläche Anpassung einfügen ausgewählt haben, bettet das Add-In die personalize.js-Datei in die Sammlung benutzerdefinierter Aktionen ein.

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

Da SharePoint-Teamwebsites standardmäßig die Minimale Downloadstrategie (Minimal Download Strategy, MDS) verwenden, versucht der Code in der personalize.js-Datei zunächst, sich selbst bei MDS zu registrieren. Auf diese Weise startet die MDS-Engine beim Laden der Seite, die javaScript enthält, die Standard-Funktion (RemoteManager_Inject). Wenn MDS deaktiviert ist, wird diese Funktion sofort gestartet.

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

    });
}

Die funktion personalizeIt() überprüft den lokalen HTML5-Speicher, bevor benutzerprofilinformationen nachschlagen. Wenn sie zu Benutzerprofilinformationen wechselt, werden die abgerufenen Informationen im lokalen HTML5-Speicher gespeichert.

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

Die personalize.js-Datei enthält auch Code, mit dem überprüft wird, ob der lokale Speicherschlüssel abgelaufen ist. Dies ist nicht in den lokalen HTML5-Speicher integriert.

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

Clientseitiges Rendering

Das Branding.ClientSideRendering-Beispiel zeigt, wie Sie mithilfe eines vom Anbieter gehosteten Add-Ins SharePoint-Artefakte und JSLink-Dateien remote bereitstellen, die clientseitiges Rendering verwenden, um das Aussehen und Verhalten von SharePoint-Listenfeldern anzupassen. JSLink-Dateien und clientseitiges Rendering ermöglichen Ihnen die Kontrolle darüber, wie Steuerelemente auf einer SharePoint-Seite (Listenansichten, Listenfelder und Formulare hinzufügen und bearbeiten) gerendert werden. Dieses Steuerelement kann die Notwendigkeit benutzerdefinierter Feldtypen reduzieren oder beseitigen. Clientseitiges Rendering ermöglicht es, die Darstellung von Listenfeldern remote zu steuern.

Das Beispiel kombiniert die JSLink-Beispiele aus den Codebeispielen für clientseitiges Rendering (JSLink) in einem einzelnen vom Anbieter gehosteten Add-In für SharePoint, das die JSLink-Dateien bereitstellt. Mit dem clientseitigen Rendering können Sie Standardwebtechnologien wie HTML und JavaScript verwenden, um die Renderinglogik von benutzerdefinierten und vordefinierten Feldtypen zu definieren.

Wenn Sie das Beispiel starten, werden Sie auf der Startseite aufgefordert, alle Beispiele bereitzustellen.

Startseite des clientseitigen Renderingbeispiels

Startseite des Beispiels „Clientseitiges Rendering“

Wenn Sie Beispiele bereitstellen auswählen, stellt das Add-In ein Bild sowie alle SharePoint-Listen, Listenansichten, Listenelemente, Formulare und JavaScript-Dateien bereit, die in jedem Beispiel verwendet werden. Das Add-In erstellt einen Ordner namens JSLink-Samples in der Formatbibliothek und lädt dann die JavaScript-Dateien in diesen Ordner hoch. Die UploadFileToFolder-Methode führt das Hochladen und Einchecken jeder JavaScript-Datei durch.

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

Beispiel 1: Anwenden von Formatierungen auf eine Listenspalte

Beispiel 1 zeigt, wie Eine Formatierung auf eine Listenspalte basierend auf dem Feldwert angewendet wird. Prioritätsfeldwerte von 1 (Hoch), 2 (Normal) und 3 (Niedrig) werden rot, orange und gelb angezeigt.

Anzeige „Benutzerdefiniertes Listenfeld“

Anzeige „Benutzerdefiniertes Listenfeld“

Das folgende JavaScript überschreibt die Standardfeldanzeige und erstellt eine neue Anzeigevorlage für das Listenfeld Priorität . Die Technik in der anonymen Funktion, die die Kontextinformationen zu dem Feld lädt, dessen Anzeige Sie überschreiben möchten, wird in allen Beispielen verwendet.

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

Beispiel 2: Kürzen von langem Text

Beispiel 2 zeigt, wie Sie langen Text abschneiden, der im Feld Text einer Ankündigungsliste gespeichert ist. Der Volltext wird als Popup angezeigt, das immer dann angezeigt wird, wenn Sie mit der Maus auf ein Listenelement zeigen, wie in der folgenden Abbildung dargestellt.

Anzeige des verkürzten Listenfelds mit Popup

Abgeschnittene Listenfeldanzeige mit Popup

Der folgende JavaScript-Code verkürzt den Text des Textfelds und bewirkt, dass der vollständige Text über das title-Attribut im span-Tag als Popup angezeigt wird.

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

}

Beispiel 3: Anzeigen eines Bilds mit einem Dokumentnamen

Beispiel 3 zeigt, wie Sie ein Bild neben einem Dokumentnamen in einer Dokumentbibliothek anzeigen. Ein rotes Signal wird angezeigt, wenn der Wert des Felds Vertraulich auf Ja festgelegt ist.

Bildanzeige neben dem Dokumentnamen

Bildanzeige neben dem Dokumentnamen

Der folgende JavaScript-Code überprüft den Wert des Vertraulichen Felds und passt dann die Anzeige des Felds Name basierend auf dem Wert eines anderen Felds an. Im Beispiel wird das Bild verwendet, das hochgeladen wird, wenn Sie Beispiele bereitstellen auswählen.

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

Beispiel 4: Anzeigen eines Balkendiagramms

Beispiel 4 zeigt, wie Sie ein Balkendiagramm im Feld % abgeschlossen einer Aufgabenliste anzeigen. Die Darstellung des Balkendiagramms hängt vom Wert des Felds % Abgeschlossen ab, wie in der folgenden Abbildung dargestellt. Beachten Sie, dass ein Balkendiagramm auch in den Formularen zum Erstellen und Bearbeiten von Aufgabenlistenelementen angezeigt wird.

Im Feld „Abgeschlossen (%)“ angezeigtes Balkendiagramm

Im Feld „Abgeschlossen (%)“ angezeigtes Balkendiagramm

Der folgende Code erstellt die Balkendiagrammanzeige und ordnet sie den Ansichts- und Anzeigeformularen (percentCompleteViewFiledTemplate) und dann den neuen Formularen und Bearbeitungsformularen (percentCompleteEditFiledTemplate) zu.

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

Beispiel 5: Ändern der Renderingvorlage

Beispiel 5 zeigt, wie Sie die Renderingvorlage für eine Listenansicht ändern. In dieser Ansicht werden Listenelementtitel angezeigt, die sich wie Akkordeons erweitern, wenn Sie sie auswählen. In der erweiterten Ansicht werden zusätzliche Listenelementfelder angezeigt.

Ansichten mit eingeblendeten und ausgeblendeten Listenelementen

Ansichten mit eingeblendeten und ausgeblendeten Listenelementen

Der folgende Code richtet die Vorlage ein und registriert sie bei der Listenvorlage. Es richtet das Gesamtlayout ein und verwendet dann den OnPostRender-Ereignishandler , um die JavaScript-Funktion zu registrieren, die beim Rendern der Liste ausgeführt wird. Diese Funktion ordnet das -Ereignis dem CSS und der Ereignisbehandlung zu, die die Accordion-Funktionalität implementiert.

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

Beispiel 6: Überprüfen von Feldwerten

Beispiel 6 zeigt, wie Sie reguläre Ausdrücke verwenden, um vom Benutzer bereitgestellte Feldwerte zu überprüfen. Eine rote Fehlermeldung wird angezeigt, wenn der Benutzer eine ungültige E-Mail-Adresse in das Textfeld Email Feld eingibt. Dies geschieht, wenn der Benutzer ein Listenelement erstellt oder bearbeitet.

Fehlermeldung für die ungültige Feldtexteingabe

Fehlermeldung für die ungültige Feldtexteingabe

Der folgende Code richtet die Vorlage mit einem Platzhalter für die Anzeige der Fehlermeldung ein und registriert die Rückruffunktionen, die ausgelöst werden, wenn der Benutzer versucht, die Formulare zu übermitteln. Der erste Rückruf gibt den Wert der Email Spalte zurück, und der zweite Rückruf verwendet reguläre Ausdrücke, um den Zeichenfolgenwert zu überprüfen.

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

Beispiel 7: Listenelementbearbeitungsfelder schreibgeschützt festlegen

Beispiel 7 zeigt, wie Listenelementbearbeitungsformularfelder schreibgeschützt werden. Die schreibgeschützten Felder werden ohne Bearbeitungssteuerelemente angezeigt.

Schreibgeschütztes Feld in einem benutzerdefinierten Listenbearbeitungsformular

Schreibgeschützte Felder in einem benutzerdefinierten Listenformular „Bearbeiten“

Im folgenden Codebeispiel werden die Felder Title, AssignedTo und Priority im Bearbeitungsformular für Listenelemente geändert, sodass die Feldwerte nur ohne Bearbeitungssteuerelemente angezeigt werden. Der Code zeigt, wie die Analyseanforderungen für verschiedene Feldtypen behandelt werden.

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

Beispiel 8: Ausblenden von Feldern

Beispiel 8 zeigt, wie Felder in neuen Formularen des Listenelements ausgeblendet und bearbeitet werden. Das Beispiel blendet das Feld Vorgänger aus, wenn ein Benutzer ein Aufgabenlistenelement erstellt oder bearbeitet.

In diesem Beispiel wird als Bearbeitungs- und neues Formular für eine Liste namens CSR-Hide-Controls-Liste bereitgestellt. Informationen zum Anzeigen des Formulars nach der Bereitstellung des Beispiels finden Sie unter Branding.ClientSideRendering.

Der folgende Code sucht das Feld Vorgänger im HTML-Code des Formulars und blendet es aus. Das Feld bleibt im HTML-Code vorhanden, aber der Benutzer kann es im Browser nicht sehen.

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

Bearbeitung von Webparts und Add-In-Webparts

Das Core.AppScriptPart-Beispiel zeigt, wie Add-In-Skriptparts verwendet werden, um Skripts einzubetten, die in einem vom Anbieter gehosteten Add-In auf einer SharePoint-Seite ausgeführt werden. In diesem Beispiel wird gezeigt, wie Sie die Benutzeroberfläche einer Seite auf der Hostwebsite ändern, indem Sie ein Add-In-Skriptpart bereitstellen und aus dem Webpartkatalog zu einer SharePoint-Seite hinzufügen.

Ein Add-In-Skriptpart ähnelt einem Webpart, in dem Sie es einer SharePoint-Seite aus dem Webpartkatalog hinzufügen können, aber in diesem Fall bettet die WEBPART-Datei eine JavaScript-Datei ein, die remote in einem vom Anbieter gehosteten Add-In ausgeführt wird. Der Add-In-Skriptteil wird in einem <div> Tag auf der SharePoint-Seite ausgeführt und bietet daher ein reaktionsschnelleres Design und eine dynamischere Oberfläche als add-in-Parts, die in IFrames ausgeführt werden.

Die Startseite enthält eine Schaltfläche Szenario ausführen , mit der das Add-In-Skriptteil im Webpartkatalog bereitgestellt wird. Im folgenden Codebeispiel wird eine FileCreationInformationObject-instance erstellt, die den Inhalt der WEBPART-Datei enthält, und dann die neue Datei in den Webpartkatalog hochgeladen. Beachten Sie, dass Sie diesen Code auch automatisch ausführen können, wenn das Add-In-Teil oder als Teil des Bereitstellungsprozesses der Websitesammlung installiert wird.

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

Nachdem Sie diesen Schritt abgeschlossen haben, können Sie das Add-In-Skriptteil Benutzerprofilinformationen in einer neuen Add-In-Skriptpartkategorie im Webpartkatalog suchen. Nachdem Sie der Seite den Add-In-Skriptteil hinzugefügt haben, steuert das remote ausgeführte JavaScript die Anzeige der Informationen auf der Seite.

Wenn Sie den Add-In-Skriptteil im Bearbeitungsmodus anzeigen, sehen Sie, dass er die JavaScript-Datei einbettet, die remote ausgeführt wird. Das userprofileinformation.js-Skript verwendet den JSON-Code, um Benutzerprofilinformationen von der Hostwebsite abzurufen.

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

Bereitstellen von Veröffentlichungsfeatures

Das Beispiel Provisioning.PublishingFeatures zeigt, wie allgemeine Aufgaben mit Veröffentlichungswebsites ausgeführt werden, die auf Office 365 gehostet werden, z. B. das Bereitstellen und Verwenden von Seitenlayouts, master Seiten und Designs oder das Einbetten von JavaScript in Seitenlayouts. Außerdem wird gezeigt, wie Filter angewendet werden, die steuern, welche Websitevorlagen für Unterwebsites verfügbar sind und welche Seitenlayouts im Hostweb verfügbar sind.

Das vom Anbieter gehostete Add-In verwendet CSOM, um häufig verwendete Benutzeroberflächenelemente auf Veröffentlichungswebsites bereitzustellen, und es verwendet JavaScript, um dynamischere Oberflächen in Seitenlayouts zu erstellen, die Sie auf Veröffentlichungswebsites bereitstellen können. Es zeigt auch die Unterschiede zwischen der Verwendung von master Seiten und Designs in Veröffentlichungswebsites.

Wichtig

Damit die Funktionalität in diesem Beispiel funktioniert, müssen Sie die Veröffentlichungsfeatures auf Ihrer Website aktivieren. Weitere Informationen finden Sie unter Aktivieren von Veröffentlichungsfeatures.

Die Beispielstartseite enthält drei Szenarien zum Anpassen der Benutzeroberfläche von Veröffentlichungswebsites:

  • Stellen Sie Seitenlayouts bereit.
  • Stellen Sie master Seiten und Designs bereit.
  • Filtern Sie die verfügbaren Seitenlayouts und Websitevorlagen auf der Hostwebsite.

Szenario 1: Bereitstellen von Seiten

Szenario 1 zeigt, wie Sie ein benutzerdefiniertes Seitenlayout bereitstellen. Die Schaltfläche Seitenlayouts bereitstellen in der folgenden Abbildung erstellt ein neues Seitenlayout und eine Seite, die dieses Layout verwendet.

Schaltfläche zum Bereitstellen von Seitenlayouts

Schaltfläche, mit der Seitenlayouts bereitgestellt werden

Sie können die neue Seite anzeigen, indem Sie zur neu erstellten Demoseite auf Ihrer Hostwebsite wechseln, die eingebettetes JavaScript enthält und Benutzerprofilinformationen anzeigt.

Im Beispiel werden die Informationen dieses Benutzers auf der Seite angezeigt. Außerdem wird eine Seite hinzugefügt, obwohl in diesem Fall ein PublishingPageInformation-Objekt zum Erstellen der neuen Seite verwendet wird.

Im Beispiel wird ein neues Seitenlayout hinzugefügt, indem eine Datei in den Gestaltungsvorlagenkatalog hochgeladen und ihr der Inhaltstyp des Seitenlayouts zugewiesen wird. Der folgende Code verwendet den Pfad zu einer *.aspx-Datei (die Sie als Ressource in Ihrem Visual Studio-Projekt bereitstellen können) und fügt sie als Seitenlayout im Gestaltungsvorlagenkatalog hinzu.

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

Sie können überprüfen, ob ihre neue Seite das neue Seitenlayout verwendet, indem Sie zur Seitenbibliothek der Hostwebsite wechseln.

Szenario 2: Bereitstellen master Seiten und Designs

Szenario 2 zeigt, wie Sie master Seiten und Designs für die Hostwebsite über ein vom Anbieter gehostetes Add-In bereitstellen und festlegen. Wenn Sie master bereitstellen auswählen und auf der Beispielstartseite verwenden, wird im Beispiel eine benutzerdefinierte master Seite bereitgestellt und auf die Hostwebsite angewendet. Sie können die neue master Seite anzeigen, indem Sie zur Startseite der Website wechseln.

Im Beispiel wird eine neue master Seite hinzugefügt, indem eine *.master-Datei in den Gestaltungsvorlagenkatalog hochgeladen und ihr der master Seiteninhaltstyp zugewiesen wird. Der folgende Code verwendet den Pfad zu einer *.master-Datei (die Sie als Ressource in Ihrem Visual Studio-Projekt bereitstellen können) und fügt sie als master-Seite im Gestaltungsvorlagenkatalog hinzu.

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

Der nächste Schritt besteht darin, die URL der neuen master Seite als Wert für die Eigenschaften MasterUrl und CustomMasterUrl des Webobjekts festzulegen, das die Website darstellt. Im Beispiel wird dies mit einer einzelnen Methode behandelt, die die URL der neuen master Seite im Gestaltungsvorlagenkatalog abruft und diesen Wert dann den Eigenschaften Web.MasterUrl und Web.CustomMasterUrl zuweist.

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

Wenn Sie Design bereitstellen auswählen und es verwenden, wird im Beispiel ein benutzerdefiniertes Design auf die Hostwebsite bereitgestellt und angewendet. Das Beispiel legt die Farbpalette, das Hintergrundbild und das Schriftartenschema des Designs fest, indem dem Designkatalog ein neues Design mit diesen Werten hinzugefügt wird (die Sie als Ressourcen in Ihrem Visual Studio-Projekt bereitstellen können). Der folgende Code erstellt das neue Design.

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

Der nächste Schritt besteht darin, dieses neue Design als Design für die Website festzulegen. Der folgende Code ruft dazu das Design aus dem Designkatalog ab und wendet dann seine Werte auf die Hostwebsite an.

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

Szenario 3: Filtern verfügbarer Seitenlayouts und Websitevorlagen

In Szenario 3 wird gezeigt, wie Sie die Optionen einschränken, die Benutzer haben, wenn sie Vorlagen auf neue Websites anwenden, und Layouts auf neue Seiten. Wenn Sie auf der Beispielstartseite Filter zum Hosten von Web anwenden auswählen, legt das Beispiel ein benutzerdefiniertes Seitenlayout als Standard und ein zusätzliches Seitenlayout als einzige andere Option für alle neuen Seiten fest, die ein Benutzer erstellt. Im Beispiel wird auch die Anzahl der verfügbaren Optionen für Benutzer reduziert, wenn sie neue Unterwebsites erstellen. Die folgende Abbildung zeigt, wie das Auswahlfeld der Websitevorlage sowohl vor als auch nach dem Anwenden der Filter aussieht.

Websitevorlagenauswahl vor und nach Anwendung von Beispielfiltern

Websitevorlagenauswahl vor und nach Anwendung von Beispielfiltern

Im Beispiel werden sowohl die Standard- als auch die verfügbaren Seitenlayouts festgelegt, indem die zugeordneten *.aspx-Dateien an Methoden an Erweiterungsmethoden übergeben werden, wie im Code gezeigt.

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

Im Beispiel werden die verfügbaren Websitevorlagen auf ähnliche Weise festgelegt. In diesem Fall werden die WebTemplateEntity-Instanzen , die jede Websitevorlage definieren, an eine Erweiterungsmethode namens SetAvailableWebTemplates übergeben.

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

Alle drei Erweiterungsmethoden – SetAvailablePageLayouts, SetDefaultPageLayoutForSite und SetAvailableWebTemplates – funktionieren auf die gleiche Weise. Sie erstellen XML-Dokumente, die Schlüssel-Wert-Paare enthalten, die die verfügbaren und Standardlayouts sowie die verfügbaren Vorlagen definieren. Anschließend übergeben sie diese Dokumente an eine zusätzliche Erweiterungsmethode namens SetPropertyBagValue. Diese Methode wird in der OfficeDevPnP Core-Erweiterung implementiert. Nachdem die entsprechenden Eigenschaftenbehälter eingerichtet wurden, werden diese Eigenschaftenbehälter verwendet, um Optionen in der Schnittstelle zu filtern.

Von den drei Methoden zeigt SetAvailableWebTemplates das vollständige Muster an.

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

Der Eigenschaftenbehälter InheritWebTemplates stellt sicher, dass alle Vorlagen, die normalerweise von der übergeordneten Website geerbt werden, beim Erstellen von Unterwebsites ebenfalls ignoriert werden.

Siehe auch