SharePoint 外接程序模型中的模块

在新 SharePoint 外接程序模型中将项目部署到 SharePoint 环境的方法与使用完全信任代码不同。 在典型完全信任代码 (FTC)/场解决方案场景中,声明性代码(功能框架 XML)中定义的模块被添加到 SharePoint 功能。 该模块包含部署到 SharePoint 服务器的项目列表。 该模块被添加到 SharePoint 功能并通过 SharePoint 解决方案对其部署。 功能激活后,将模块中定义的项目部署到 SharePoint 环境。

在 SharePoint 外接程序模型场景中,远程预配模式用于将项目部署到 SharePoint 环境。

术语

术语“项目将在整篇文章引用。 项目指的是通常被部署到 SharePoint 环境的项目。 项目通常包括:

  • JavaScript 文件
  • CSS 文件
  • 图像文件(.jpg、.gif、.png 等)
  • 母版页
  • 页面布局
  • 列表项

高级别准则

作为经验法则,我们想提供以下有关将项目部署到 SharePoint 环境的高级别准则。

  • 使用远程预配模式(SharePoint 客户端对象模型和 SharePoint REST API),以将项目部署到 SharePoint 环境。
  • 请不要使用声明性代码模块或功能框架 XML 文件将项目部署到 SharePoint 环境。

调试

使用代码部署项目的最大优点是,使用代码时能够调试部署过程。 在使用声明性代码模块或功能框架 XML 文件将项目部署到 SharePoint 环境时,将无法调试部署过程。

入门

以下 O365 PnP 代码示例演示了如何创建使用远程预配模式的 SharePoint 外接程序,以将项目部署到 SharePoint 环境。

此示例演示了如何在样式库中新建文件夹,并向其添加 JavaScript 文件和图像。

  • Branding.ClientSideRendering(O365 PnP 代码示例)
    • 有关更多详细信息,请参阅 Default.aspx.cs 类中的 UploadJSFilesUploadFileToFolder 方法。

    • 以下介绍了这些方法,以供快速参考。

      创建文件夹并将 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();
              }
      }
      

此示例演示了如何通过设置 Web 对象上的 CustomMasterUrl 属性来上传母版页、设置母版页元数据并将其应用到网站。

  • Branding.ApplyBranding(O365 PnP 代码示例)
    • 有关更多详细信息,请参阅 BrandingHelper.cs 类中的 UploadPageLayoutCreatePublishingPageSetSupportCaseContent 方法。

    • 除了在 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();
      }
      

      取消分配母版页并删除母版页

      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 页面以及向页面添加 Web 部件和外接程序部件。 它还演示了如何将列表项部署到主机 Web 和外接程序 Web。

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

      预配内容至发布页面(“搜索内容”Web 部件、“脚本编辑器”Web 部件、外接程序部件)并发布页面:

      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有关将列表项部署到主机 Web 和外接程序 Web 的更多详细信息,请参阅 SharePointService.cs 类中的 和 FillAppWebNotesListToThreshold 方法。

      重要

      此示例中演示的相同主机 Web 和外接程序 Web 方法可以应用于任何类型的项目,以将它们部署到适当的位置。

      将列表项添加到主机 Web 中的列表:

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

      将列表项添加到外接程序 Web 中的列表:

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

PnP 示例

适用于

  • Office 365 多租户 (MT)
  • Office 365 专用 (D) 部分
  • SharePoint 2013 本地 – 部分

专用模式和本地模式在使用 SharePoint 外接程序模型技术方面完全相同,但在可以使用的可能的技术方面存在差异。