Модули в модели надстройки SharePoint
Подход, который вы используете для развертывания артефактов в среде SharePoint, отличается в новой модели надстроек SharePoint, чем в коде полного доверия. В типичном сценарии полного доверия (FTC) или решения фермы модули, определенные в декларативном коде (XML-файлы платформы компонентов), были добавлены в функции SharePoint. Модули включали список артефактов для развертывания на сервере SharePoint. Модули были добавлены в функции SharePoint и развернуты с помощью решений SharePoint. После активации компонента артефакты, определенные в модулях, были развернуты в среде SharePoint.
В сценарии модели надстройки SharePoint шаблон удаленной подготовки используется для развертывания артефактов в средах SharePoint.
Терминология
Термин артефакты упоминается в этой статье. Артефакты относятся к элементам, которые обычно развертываются в среде SharePoint. Артефакты обычно включают:
- файлы JavaScript;
- CSS-файлы
- Файлы изображений (.jpg, .gif, .png и т. д.)
- Главные страницы
- Макеты страниц
- Элементы списка
Рекомендации высокого уровня
Как правило, мы хотели бы предоставить следующие общие рекомендации по развертыванию артефактов в средах SharePoint.
- Используйте шаблон удаленной подготовки (клиентская объектная модель SharePoint и REST API SharePoint) для развертывания артефактов в средах SharePoint.
- Не используйте декларативные модули кода или XML-файлы платформы компонентов для развертывания артефактов в средах SharePoint.
Отладка
Большим преимуществом использования кода для развертывания артефактов является возможность отладки процесса развертывания при использовании кода. Невозможно выполнить отладку процесса развертывания при использовании декларативных модулей кода или XML-файлов платформы компонентов для развертывания артефактов в средах SharePoint.
Начало работы
В следующих примерах кода O365 PnP показано, как создать надстройки SharePoint, использующие шаблон удаленной подготовки для развертывания артефактов в среде SharePoint.
В этом примере показано, как создать новые папки в библиотеке стилей и добавить файлы и изображения JavaScript в новые файлы.
- Branding.ClientSideRendering (пример кода O365 PnP)
Дополнительные сведения см. в методах UploadJSFiles и UploadFileToFolder в классе Default.aspx.cs .
Эти методы также отображаются ниже для краткой справки.
Создайте папку и отправьте в нее файлы JavaScript:
void UploadJSFiles(Web web) { //Delete the folder if it exists Microsoft.SharePoint.Client.List list = web.Lists.GetByTitle("Style Library"); IEnumerable<Folder> results = web.Context.LoadQuery<Folder>(list.RootFolder.Folders.Where(folder => folder.Name == "JSLink-Samples")); web.Context.ExecuteQuery(); Folder samplesJSfolder = results.FirstOrDefault(); if (samplesJSfolder != null) { samplesJSfolder.DeleteObject(); web.Context.ExecuteQuery(); } //Create new folder samplesJSfolder = list.RootFolder.Folders.Add("JSLink-Samples"); web.Context.Load(samplesJSfolder); web.Context.ExecuteQuery(); //Upload JavaScript files to folder UploadFileToFolder(web, Server.MapPath("../Scripts/JSLink-Samples/Accordion.js"), samplesJSfolder); UploadFileToFolder(web, Server.MapPath("../Scripts/JSLink-Samples/ConfidentialDocuments.js"), samplesJSfolder); UploadFileToFolder(web, Server.MapPath("../Scripts/JSLink-Samples/DisableInput.js"), samplesJSfolder); UploadFileToFolder(web, Server.MapPath("../Scripts/JSLink-Samples/HiddenField.js"), samplesJSfolder); UploadFileToFolder(web, Server.MapPath("../Scripts/JSLink-Samples/PercentComplete.js"), samplesJSfolder); UploadFileToFolder(web, Server.MapPath("../Scripts/JSLink-Samples/PriorityColor.js"), samplesJSfolder); UploadFileToFolder(web, Server.MapPath("../Scripts/JSLink-Samples/ReadOnlySPControls.js"), samplesJSfolder); UploadFileToFolder(web, Server.MapPath("../Scripts/JSLink-Samples/RegexValidator.js"), samplesJSfolder); UploadFileToFolder(web, Server.MapPath("../Scripts/JSLink-Samples/SubstringLongText.js"), samplesJSfolder); UploadFileToFolder(web, Server.MapPath("../Scripts/JSLink-Samples/DependentFields.js"), samplesJSfolder); //Create another folder inside the folder that was just created Folder imgsFolder = samplesJSfolder.Folders.Add("imgs"); web.Context.Load(imgsFolder); web.Context.ExecuteQuery(); //Upload image files to folder UploadFileToFolder(web, Server.MapPath("../Scripts/JSLink-Samples/imgs/Confidential.png"), imgsFolder); }
Создайте папку и отправьте в нее файлы изображений:
public static void UploadFileToFolder(Web web, string filePath, Folder folder) { //Create a FileStream to the file to upload using (FileStream fs = new FileStream(filePath, FileMode.Open)) { //Create FileCreationInformation object to set file metadata FileCreationInformation flciNewFile = new FileCreationInformation(); flciNewFile.ContentStream = fs; flciNewFile.Url = System.IO.Path.GetFileName(filePath); flciNewFile.Overwrite = true; //Upload file to SharePoint Microsoft.SharePoint.Client.File uploadFile = folder.Files.Add(flciNewFile); //Check in the file uploadFile.CheckIn("CSR sample js file", CheckinType.MajorCheckIn); folder.Context.Load(uploadFile); folder.Context.ExecuteQuery(); } }
В этом примере показано, как отправить master страницы, задать метаданные master страницы и применить страницу master к сайту, задав свойство CustomMasterUrl в веб-объекте.
- Branding.ApplyBranding (пример кода O365 PnP)
Дополнительные сведения см. в методах UploadPageLayout, CreatePublishingPage и SetSupportCaseContent класса BrandingHelper.cs .
Помимо создания новых элементов в SharePoint, в этом примере показано, как удалять элементы. Ниже перечислены методы удаления элементов. Удаление файла:
private static void DeleteFile(Web web, string fileName, string serverPath, string serverFolder) { var fileUrl = string.Concat(serverPath, serverFolder, (string.IsNullOrEmpty(serverFolder) ? string.Empty : "/"), fileName); var fileToDelete = web.GetFileByServerRelativeUrl(fileUrl); fileToDelete.DeleteObject(); web.Context.ExecuteQuery(); }
Удаление папки:
public static void RemoveFolder(ClientContext clientContext, string folder, string path) { var web = clientContext.Web; var filePath = web.ServerRelativeUrl.TrimEnd(Program.trimChars) + "/" + path + "/"; var folderToDelete = web.GetFolderByServerRelativeUrl(string.Concat(filePath, folder)); Console.WriteLine("Removing folder {0} from {1}", folder, path); folderToDelete.DeleteObject(); clientContext.ExecuteQuery(); }
Отмена назначения страницы master и удаление страницы master
public static void RemoveMasterPage(ClientContext clientContext, string name, string folder) { var web = clientContext.Web; clientContext.Load(web, w => w.AllProperties); clientContext.ExecuteQuery(); Console.WriteLine("Deactivating and removing {0} from {1}", name, web.ServerRelativeUrl); //set master pages back to the defaults that were being used if (web.AllProperties.FieldValues.ContainsKey("OriginalMasterUrl")) { web.MasterUrl = (string)web.AllProperties["OriginalMasterUrl"]; } if (web.AllProperties.FieldValues.ContainsKey("CustomMasterUrl")) { web.CustomMasterUrl = (string)web.AllProperties["CustomMasterUrl"]; } web.Update(); clientContext.ExecuteQuery(); //now that the master page is set back to its default, re-reference the web from context and delete the custom master pages web = clientContext.Web; var lists = web.Lists; var gallery = web.GetCatalog(116); clientContext.Load(lists, l => l.Include(ll => ll.DefaultViewUrl)); clientContext.Load(gallery, g => g.RootFolder.ServerRelativeUrl); clientContext.ExecuteQuery(); var masterPath = gallery.RootFolder.ServerRelativeUrl.TrimEnd(new char[] { '/' }) + "/"; DeleteFile(web, name, masterPath, folder); }
Удаление макета страницы
public static void RemovePageLayout(ClientContext clientContext, string name, string folder) { var web = clientContext.Web; var lists = web.Lists; var gallery = web.GetCatalog(116); clientContext.Load(lists, l => l.Include(ll => ll.DefaultViewUrl)); clientContext.Load(gallery, g => g.RootFolder.ServerRelativeUrl); clientContext.ExecuteQuery(); Console.WriteLine("Removing page layout {0} from {1}", name, clientContext.Web.ServerRelativeUrl); var masterPath = gallery.RootFolder.ServerRelativeUrl.TrimEnd(Program.trimChars) + "/"; DeleteFile(web, name, masterPath, folder); }
Ознакомьтесь с этим примером в статье Применение фирменной символики к сайтам SharePoint с помощью надстройки для SharePoint (Office 365 видео PnP).
В этом примере есть немного всего. В ней показано, как активировать функции публикации, отправлять макеты страниц, создавать страницы публикации, создавать списки, типы контента и элементы списка, а также создавать страницы pblishing и добавлять веб-части и части надстроек на страницы. В нем также показано, как развертывать элементы списка как на хост-сайте, так и на сайте надстройки.
- Branding.ClientSideRendering (пример кода O365 PnP)
Примеры этих операций см. в методах класса Utils.cs .
Эти методы перечислены ниже для справки.
Активация функций публикации на уровне веб-сайтов и семейства веб-сайтов:
public static void ActivePublishingFeature(ClientContext ctx) { Guid publishingSiteFeatureId = new Guid("f6924d36-2fa8-4f0b-b16d-06b7250180fa"); Guid publishingWebFeatureId = new Guid("94c94ca6-b32f-4da9-a9e3-1f3d343d7ecb"); Site clientSite = ctx.Site; ctx.Load(clientSite); FeatureCollection clientSiteFeatures = clientSite.Features; ctx.Load(clientSiteFeatures); //Activate the site feature clientSiteFeatures.Add(publishingSiteFeatureId, true, FeatureDefinitionScope.Farm); ctx.ExecuteQuery(); FeatureCollection clientWebFeatures = ctx.Web.Features; ctx.Load(clientWebFeatures); //Activate the web feature clientWebFeatures.Add(publishingWebFeatureId, true, FeatureDefinitionScope.Farm); ctx.ExecuteQuery(); }
Создайте список:
public static List CreateList(ClientContext ctx, int templateType, string title, string url, QuickLaunchOptions quickLaunchOptions) { ListCreationInformation listCreationInfo = new ListCreationInformation { TemplateType = templateType, Title = title, Url = url, QuickLaunchOption = quickLaunchOptions }; List spList = ctx.Web.Lists.Add(listCreationInfo); ctx.Load(spList); ctx.ExecuteQuery(); return spList; }
Создание типа контента
public static ContentType CreateContentType(ClientContext ctx, string ctyName, string group, string ctyId) { ContentTypeCreationInformation contentTypeCreation = new ContentTypeCreationInformation(); contentTypeCreation.Name = ctyName; contentTypeCreation.Description = "Custom Content Type"; contentTypeCreation.Group = group; contentTypeCreation.Id = ctyId; //Add the new content type to the collection ContentType ct = ctx.Web.ContentTypes.Add(contentTypeCreation); ctx.Load(ct); ctx.ExecuteQuery(); return ct; }
Отправка макета страницы
public static void UploadPageLayout(ClientContext ctx, string sourcePath, string targetListTitle, string targetUrl) { using (FileStream fs = new FileStream(sourcePath, FileMode.Open, FileAccess.Read)) { byte[] data = new byte[fs.Length]; fs.Read(data, 0, data.Length); using (MemoryStream ms = new MemoryStream()) { ms.Write(data, 0, data.Length); var newfile = new FileCreationInformation(); newfile.Content = ms.ToArray(); newfile.Url = targetUrl; newfile.Overwrite = true; List docs = ctx.Web.Lists.GetByTitle(targetListTitle); Microsoft.SharePoint.Client.File uploadedFile = docs.RootFolder.Files.Add(newfile); uploadedFile.CheckOut(); uploadedFile.CheckIn("Data storage model", CheckinType.MajorCheckIn); uploadedFile.Publish("Data storage model layout."); ctx.Load(uploadedFile); ctx.ExecuteQuery(); } } }
Создайте страницу публикации и задайте ее макет:
public static void CreatePublishingPage(ClientContext clientContext, string pageName, string pagelayoutname, string url, string queryurl) { var publishingPageName = pageName + ".aspx"; Web web = clientContext.Web; clientContext.Load(web); List pages = web.Lists.GetByTitle("Pages"); clientContext.Load(pages.RootFolder, f => f.ServerRelativeUrl); clientContext.ExecuteQuery(); Microsoft.SharePoint.Client.File file = web.GetFileByServerRelativeUrl(pages.RootFolder.ServerRelativeUrl + "/" + pageName + ".aspx"); clientContext.Load(file, f => f.Exists); clientContext.ExecuteQuery(); if(file.Exists) { file.DeleteObject(); clientContext.ExecuteQuery(); } PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(clientContext, web); clientContext.Load(publishingWeb); if (publishingWeb != null) { List publishingLayouts = clientContext.Site.RootWeb.Lists.GetByTitle("Master Page Gallery"); ListItemCollection allItems = publishingLayouts.GetItems(CamlQuery.CreateAllItemsQuery()); clientContext.Load(allItems, items => items.Include(item => item.DisplayName).Where(obj => obj.DisplayName == pagelayoutname)); clientContext.ExecuteQuery(); ListItem layout = allItems.Where(x => x.DisplayName == pagelayoutname).FirstOrDefault(); clientContext.Load(layout); PublishingPageInformation publishingpageInfo = new PublishingPageInformation() { Name = publishingPageName, PageLayoutListItem = layout, }; PublishingPage publishingPage = publishingWeb.AddPublishingPage(publishingpageInfo); publishingPage.ListItem.File.CheckIn(string.Empty, CheckinType.MajorCheckIn); publishingPage.ListItem.File.Publish(string.Empty); clientContext.ExecuteQuery(); } SetSupportCaseContent(clientContext, "SupportCasesPage", url, queryurl); }
Создание элементов списка
public static void AddDemoDataToSupportCasesList(ClientContext ctx, List list, string title, string status, string csr, string customerID) { ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation(); ListItem newItem = list.AddItem(itemCreateInfo); newItem["Title"] = title; newItem["FTCAM_Status"] = status; newItem["FTCAM_CSR"] = csr; newItem["FTCAM_CustomerID"] = customerID; newItem.Update(); ctx.ExecuteQuery(); }
Подготовьте содержимое на странице публикации (веб-часть "Контент по поиску", веб-часть редактора скриптов, часть надстройки) и опубликуйте страницу:
public static void SetSupportCaseContent(ClientContext ctx, string pageName, string url, string queryurl) { List pages = ctx.Web.Lists.GetByTitle("Pages"); ctx.Load(pages.RootFolder, f => f.ServerRelativeUrl); ctx.ExecuteQuery(); Microsoft.SharePoint.Client.File file = ctx.Web.GetFileByServerRelativeUrl(pages.RootFolder.ServerRelativeUrl + "/" + pageName + ".aspx"); ctx.Load(file); ctx.ExecuteQuery(); file.CheckOut(); LimitedWebPartManager limitedWebPartManager = file.GetLimitedWebPartManager(PersonalizationScope.Shared); string quicklaunchmenuFormat = @"<div><a href='{0}/{1}'>Sample Home Page</a></div> <br /> <div style='font-weight:bold'>CSR Dashboard</div> <div class='cdsm_mainmenu'> <ul> <li><a href='{0}/CSRInfo/{1}'>My CSR Info</a></li> <li><a href='{0}/CallQueue/{1}'>Call Queue</a></li> <li> <span class='collapse_arrow'></span> <span><a href='{0}/CustomerDashboard/{1}'>Customer Dashboard</a></span> <ul> <li><a href='{0}/CustomerDashboard/Orders{1}'>Recent Orders</a></li> <li><a class='current' href='#'>Support Cases</a></li> <li><a href='{0}/CustomerDashboard/Notes{1}'>Notes</a></li> </ul> </li> </ul> </div> <div class='cdsm_submenu'> </div>"; string quicklaunchmenu = string.Format(quicklaunchmenuFormat, url, queryurl); string qlwebPartXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><webParts><webPart xmlns=\"http://schemas.microsoft.com/WebPart/v3\"><metaData><type name=\"Microsoft.SharePoint.WebPartPages.ScriptEditorWebPart, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c\" /><importErrorMessage>Cannot import this web part.</importErrorMessage></metaData><data><properties><property name=\"Content\" type=\"string\"><![CDATA[" + quicklaunchmenu + "]]></property><property name=\"ChromeType\" type=\"chrometype\">None</property></properties></data></webPart></webParts>"; WebPartDefinition qlWpd = limitedWebPartManager.ImportWebPart(qlwebPartXml); WebPartDefinition qlWpdNew = limitedWebPartManager.AddWebPart(qlWpd.WebPart, "SupportCasesZoneLeft", 0); ctx.Load(qlWpdNew); //Customer Dropdown List Script web part string dpwebPartXml = System.IO.File.ReadAllText(System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath + "Assets/CustomerDropDownlist.webpart"); WebPartDefinition dpWpd = limitedWebPartManager.ImportWebPart(dpwebPartXml); WebPartDefinition dpWpdNew = limitedWebPartManager.AddWebPart(dpWpd.WebPart, "SupportCasesZoneTop", 0); ctx.Load(dpWpdNew); //Support Case CBS Info web part string cbsInfoWebPartXml = System.IO.File.ReadAllText(System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath + "Assets/SupportCaseCBSWebPartInfo.webpart"); WebPartDefinition cbsInfoWpd = limitedWebPartManager.ImportWebPart(cbsInfoWebPartXml); WebPartDefinition cbsInfoWpdNew = limitedWebPartManager.AddWebPart(cbsInfoWpd.WebPart, "SupportCasesZoneMiddle", 0); ctx.Load(cbsInfoWpdNew); //Support Case Content By Search web part string cbswebPartXml = System.IO.File.ReadAllText(System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath + "Assets/SupportCase CBS Webpart/SupportCaseCBS.webpart"); WebPartDefinition cbsWpd = limitedWebPartManager.ImportWebPart(cbswebPartXml); WebPartDefinition cbsWpdNew = limitedWebPartManager.AddWebPart(cbsWpd.WebPart, "SupportCasesZoneMiddle", 1); ctx.Load(cbsWpdNew); //Support Cases App Part string appPartXml = System.IO.File.ReadAllText(System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath + "Assets/SupportCaseAppPart.webpart"); WebPartDefinition appPartWpd = limitedWebPartManager.ImportWebPart(appPartXml); WebPartDefinition appPartdNew = limitedWebPartManager.AddWebPart(appPartWpd.WebPart, "SupportCasesZoneBottom", 0); ctx.Load(appPartdNew); //Get Host Web Query String and show support case list web part string querywebPartXml = System.IO.File.ReadAllText(System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath + "Assets/GetHostWebQueryStringAndShowList.webpart"); WebPartDefinition queryWpd = limitedWebPartManager.ImportWebPart(querywebPartXml); WebPartDefinition queryWpdNew = limitedWebPartManager.AddWebPart(queryWpd.WebPart, "SupportCasesZoneBottom", 1); ctx.Load(queryWpdNew); file.CheckIn("Data storage model", CheckinType.MajorCheckIn); file.Publish("Data storage model"); ctx.Load(file); ctx.ExecuteQuery(); }
Дополнительные сведения о развертывании
FillHostWebSupportCasesToThreshold
элементов списка на хост-сайте иFillAppWebNotesListToThreshold
в веб-сайте надстройки см. в и методах класса SharePointService.cs .Важно!
Те же подходы к хост-сайту и веб-сайту надстроек, показанные в этом примере, могут применяться к артефактам любого типа, чтобы развернуть их в соответствующем расположении.
Добавьте элементы списка в список на хост-сайте:
public string FillHostWebSupportCasesToThreshold() { using (var clientContext = SharePointContext.CreateUserClientContextForSPHost()) { List supportCasesList = clientContext.Web.Lists.GetByTitle("Support Cases"); ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation(); for (int i = 0; i < 500; i++) { ListItem newItem = supportCasesList.AddItem(itemCreateInfo); newItem["Title"] = "Wrong product received." + i.ToString(); newItem["FTCAM_Status"] = "Open"; newItem["FTCAM_CSR"] = "bjones"; newItem["FTCAM_CustomerID"] = "thresholds test"; newItem.Update(); if (i % 100 == 0) clientContext.ExecuteQuery(); } clientContext.ExecuteQuery(); clientContext.Load(supportCasesList, l => l.ItemCount); clientContext.ExecuteQuery(); if(supportCasesList.ItemCount>=5000) return "The Host Web Support Cases List has " + supportCasesList.ItemCount + " items, and exceeds the threshold."; else return 500 + " items have been added to the Host Web Support Cases List. " + "There are " + (5000 - supportCasesList.ItemCount) + " items left to add."; } }
Добавьте элементы списка в список на веб-сайте надстройки:
public string FillAppWebNotesListToThreshold() { using (var clientContext = SharePointContext.CreateUserClientContextForSPAppWeb()) { List notesList = clientContext.Web.Lists.GetByTitle("Notes"); var itemCreateInfo = new ListItemCreationInformation(); for (int i = 0; i < 500; i++) { ListItem newItem = notesList.AddItem(itemCreateInfo); newItem["Title"] = "Notes Title." + i.ToString(); newItem["FTCAM_Description"] = "Notes description"; newItem.Update(); if (i % 100 == 0) clientContext.ExecuteQuery(); } clientContext.ExecuteQuery(); clientContext.Load(notesList, l => l.ItemCount); clientContext.ExecuteQuery(); if (notesList.ItemCount >= 5000) return "The Add-in Web Notes List has " + notesList.ItemCount + " items, and exceeds the threshold."; else return 500 + " items have been added to the Add-in Web Notes List. " + "There are " + (5000-notesList.ItemCount) + " items left to add."; } }
См. также
- Главные страницы (рецепт надстройки SharePoint)
- Статьи руководства на https://aka.ms/OfficeDevPnPGuidance
- Ссылки в MSDN на https://aka.ms/OfficeDevPnPMSDN
- Видео на https://aka.ms/OfficeDevPnPVideos
Образцы PnP
- Branding.ClientSideRendering (пример кода O365 PnP)
- Branding.ApplyBranding (пример кода O365 PnP)
- Branding.ClientSideRendering (пример кода O365 PnP)
- Примеры и содержимое на сайте https://github.com/SharePoint/PnP
Область применения
- Office 365 Multi Tenant (MT)
- Office 365 Dedicated (D) частично
- Локальная среда SharePoint 2013 — частично
Шаблоны для выделенных и локальных служб идентичны методам модели надстроек SharePoint с отличиями, связанными с возможностью применения технологий.