替换使用服务器场解决方案中的模块部署的 SharePoint 文件
如果你已通过使用服务器场解決方案中的模块以声明方式部署文件,请了解如何将它们转换为更新对文件的引用并使用客户端对象模型 (CSOM) 提供类似功能的新解决方案。 在过去,模块用于部署母版页和页面布局等文件。
重要
不能将场解决方案迁移到 SharePoint Online。 通过采用本文中介绍的方法和代码,可以生成与场解决方案的功能类似的新解决方案,并能更新对文件的引用,这样便可以将新解决方案部署到 SharePoint Online。 然后,可以禁用功能,并撤消旧场解决方案。
若要采用本文中的代码,还需要使用其他代码,才能生成完全正常的解决方案。 例如,本文并未介绍如何使用 Office 365 身份验证、如何实现必需的异常处理等。 有关其他代码示例,请参阅 Office 365 开发人员模式和做法项目。
注意
本文中的代码按原样提供,不提供任何明示或暗示的担保,包括对特定用途适用性、适销性或不侵权的默示担保。
本文介绍了如何使用转换过程执行以下操作:
- 上载母版页并更新对母版页的引用
- 上载页面布局并更新对页面布局的引用
在以下情况下使用此转换过程:
- 您的现有服务器场解决方案之前使用模块部署文件。
- 您想将服务器场解决方案中的母版页和页面布局替换为新的母版页和页面布局,以便可以迁移到 SharePoint Online。
- 以声明方式在场解决方案中的母版页或页面布局上设置了文档内容类型。
准备工作
理想情况下,应查看现有的场解決方案、了解本文中介绍的技巧,然后计划如何将这些技巧应用到你的方案。 如果不熟悉场解决方案或不具有要使用的现有场解决方案,则了解场解决方案对你可能很有帮助。
有关详细信息,请参阅在 SharePoint 中生成场解决方案。
启用网站集和网站上的发布功能
在运行你的代码示例之前,通过以下过程启用网站集和网站上的发布功能:
启用网站集上的 SharePoint Server 发布基础架构功能:
打开你的 SharePoint 网站并转到“设置”>“网站设置”。
在“网站集管理”中,选择“网站集功能”。
在“SharePoint Server 发布基础架构”上,选择“激活”。
在网站上启用 SharePoint Server 发布功能
打开你的 SharePoint 网站并转到“设置”>“网站设置”。
在“网站操作”中,选择“管理网站功能”。
在“SharePoint 服务器发布”中,选择“激活”。
设置 Visual Studio 项目
下载示例项目。 选择“查看原始项目”开始下载示例项目,从 zip 文件中提取文件,然后浏览到 Module9/ModuleReplacement 文件夹。
打开 ModuleReplacement.sln。
打开 settings.xml。 查看并更新以下属性,以满足您的要求:
masterpage 元素 - 使用 file 属性指定要部署到母版页库的新母版页,使用 replaces 属性指定母版页样式库中的现有母版页。
pagelayout 元素 - 使用 file 属性指定新的页面布局文件,使用 replaces 属性指定现有的页面布局文件,并使用多个其他属性指定其他页面布局信息。
<?xml version="1.0" encoding="utf-8" ?> <branding> <masterPages> <masterPage file="contoso_app.master" replaces="contoso.master"/> </masterPages> <pageLayouts> <pageLayout file="ContosoWelcomeLinksApp.aspx" replaces="ContosoWelcomeLinks.aspx" title="Contoso Welcome Links add-in" associatedContentTypeName="Contoso Web Page" defaultLayout="true" /> </pageLayouts> </branding>
在 MasterPageGalleryFiles.cs 中,MasterPageGalleryFile 和 LayoutFile 类定义了业务对象,其中存储了关于要上载到 SharePoint 的新母版页和页面布局的信息。
上载母版页并更新对母版页的引用
在 SharePoint 网站上载新母版页并更新对新母版页的引用:
在 Program.cs 中,添加以下 using 语句。
using System.Security;
在 Program.cs 中,添加以下方法以执行向 Office 365 的身份验证。
static SecureString GetPassword()
{
SecureString sStrPwd = new SecureString();
try
{
Console.Write("Password: ");
for (ConsoleKeyInfo keyInfo = Console.ReadKey(true); keyInfo.Key != ConsoleKey.Enter; keyInfo = Console.ReadKey(true))
{
if (keyInfo.Key == ConsoleKey.Backspace)
{
if (sStrPwd.Length > 0)
{
sStrPwd.RemoveAt(sStrPwd.Length - 1);
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
Console.Write(" ");
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
}
}
else if (keyInfo.Key != ConsoleKey.Enter)
{
Console.Write("*");
sStrPwd.AppendChar(keyInfo.KeyChar);
}
}
Console.WriteLine("");
}
catch (Exception e)
{
sStrPwd = null;
Console.WriteLine(e.Message);
}
return sStrPwd;
}
static string GetUserName()
{
string strUserName = string.Empty;
try
{
Console.Write("Username: ");
strUserName = Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
strUserName = string.Empty;
}
return strUserName;
}
在 Program.cs 中,将以下代码添加到 Main 方法,此代码将执行以下任务:
加载 settings.xml 文件。
使用 GetCatalog (116) 获取对母版页样式库的引用。
母版页将上载到母版页样式库。 母版页样式库列表模板的标识符是 116。 有关详细信息,请参阅 ListTemplate 元素(网站)。 将加载有关母版页样式库的其他详细信息,包括使用 List.ContentTypes 以获取与母版页样式库相关的内容类型。
加载 Web 对象的属性,包括 Web.ContentTypes、Web.MasterUrl 和 Web.CustomMasterUrl。
将 parentMasterPageContentTypeId 设置为母版页的内容类型 ID。 有关详细信息,请参阅基础内容类型层次结构。
从母版页样式库获取母版页的内容类型。 此内容类型用于在本文后面设置新母版页的内容类型。
static void Main(string[] args) { string userName = GetUserName(); SecureString pwd = GetPassword(); // End program if no credentials were entered. if (string.IsNullOrEmpty(userName) || (pwd == null)) return; using (var clientContext = new ClientContext("http://sharepoint.contoso.com")) { clientContext.AuthenticationMode = ClientAuthenticationMode.Default; clientContext.Credentials = new SharePointOnlineCredentials(userName, pwd); XDocument settings = XDocument.Load("settings.xml"); // Get a reference to the Master Page Gallery which will be used to upload new master pages. Web web = clientContext.Web; List gallery = web.GetCatalog(116); Folder folder = gallery.RootFolder; // Load additional Master Page Gallery properties. clientContext.Load(folder); clientContext.Load(gallery, g => g.ContentTypes, g => g.RootFolder.ServerRelativeUrl); // Load the content types and master page information from the web. clientContext.Load(web, w => w.ContentTypes, w => w.MasterUrl, w => w.CustomMasterUrl); clientContext.ExecuteQuery(); // Get the content type for the master page from the Master Page Gallery using the content type ID for masterpages (0x010105). const string parentMasterPageContentTypeId = "0x010105"; ContentType masterPageContentType = gallery.ContentTypes.FirstOrDefault(ct => ct.StringId.StartsWith(parentMasterPageContentTypeId)); UploadAndSetMasterPages(web, folder, clientContext, settings, masterPageContentType.StringId); } }
在 Program.cs 中,添加 UploadAndSetMasterPages 方法,此方法将通过以下方式创建 MasterPageGalleryFile 业务对象的列表:
读取 settings.xml 中定义的所有 masterPage 元素。
对于每个 masterPage 元素(指定要上传到 SharePoint 的母版页),将属性值加载到 MasterPageGalleryFile 业务对象。
对于列表中的每个 MasterPageGalleryFile 业务对象,调用 UploadAndSetMasterPage。
private static void UploadAndSetMasterPages(Web web, Folder folder, ClientContext clientContext, XDocument settings, string contentTypeId) { IList<MasterPageGalleryFile> masterPages = (from m in settings.Descendants("masterPage") select new MasterPageGalleryFile { File = (string)m.Attribute("file"), Replaces = (string)m.Attribute("replaces"), ContentTypeId = contentTypeId }).ToList(); foreach (MasterPageGalleryFile masterPage in masterPages) { UploadAndSetMasterPage(web, folder, clientContext, masterPage); } }
在 Program.cs 中,添加 UploadAndSetMasterPage 方法,此方法将执行以下任务:
签出母版页。
使用 FileCreationInformation 上载新文件。
获取与新上载的文件关联的列表项。
设置列表项上的各种属性,包括将列表项的内容类型 ID 设置为母版页的内容类型 ID。
签入、发布和批准新的母版页。
如果将当前网站的母版页或自定义母版页 URL 设置为旧的母版页,请将 Web.MasterUrl(web.masterurl) 或 Web.CustomMasterUrl(#web.custommasterurl) 更新为使用新上传的母版页 URL。
private static void UploadAndSetMasterPage(Web web, Folder folder, ClientContext clientContext, MasterPageGalleryFile masterPage) { using (var fileReadingStream = System.IO.File.OpenRead(masterPage.File)) { // If necessary, ensure that the master page is checked out. PublishingHelper.CheckOutFile(web, masterPage.File, folder.ServerRelativeUrl); // Use the FileCreationInformation class to upload the new file. var fileInfo = new FileCreationInformation(); fileInfo.ContentStream = fileReadingStream; fileInfo.Overwrite = true; fileInfo.Url = masterPage.File; File file = folder.Files.Add(fileInfo); // Get the list item associated with the newly uploaded master page file. ListItem item = file.ListItemAllFields; clientContext.Load(file.ListItemAllFields); clientContext.Load(file, f => f.CheckOutType, f => f.Level, f => f.ServerRelativeUrl); clientContext.ExecuteQuery(); item["ContentTypeId"] = masterPage.ContentTypeId; item["UIVersion"] = Convert.ToString(15); item["MasterPageDescription"] = "Master Page Uploaded using CSOM"; item.Update(); clientContext.ExecuteQuery(); // If necessary, check in, publish, and approve the new master page file. PublishingHelper.CheckInPublishAndApproveFile(file); // On the current site, update the master page references to use the new master page. if (web.MasterUrl.EndsWith("/" + masterPage.Replaces)) { web.MasterUrl = file.ServerRelativeUrl; } if (web.CustomMasterUrl.EndsWith("/" + masterPage.Replaces)) { web.CustomMasterUrl = file.ServerRelativeUrl; } web.Update(); clientContext.ExecuteQuery(); } }
上传并更新对页面布局的引用
注意
此部分中的代码示例是在本文中上一部分内的代码示例的基础之上生成。
若要替换之前使用场解决方案中的模块部署的页面布局,并上载和更新引用以使用新的页面布局,请执行以下操作:
使用以下代码更新 Main 方法。 此代码包含上载页面布局文件并更新对页面布局文件的引用的其他步骤,包括:
将 parentPageLayoutContentTypeId 设置为页面布局的内容类型 ID。
从母版页样式库获取与 parentPageLayoutContentTypeId 匹配的内容类型。
调用 UploadPageLayoutsAndUpdateReferences。
static void Main(string[] args) { string userName = GetUserName(); SecureString pwd = GetPassword(); // End program if no credentials were entered. if (string.IsNullOrEmpty(userName) || (pwd == null)) return; using (var clientContext = new ClientContext("http://sharepoint.contoso.com")) { clientContext.AuthenticationMode = ClientAuthenticationMode.Default; clientContext.Credentials = new SharePointOnlineCredentials(userName, pwd); XDocument settings = XDocument.Load("settings.xml"); // Get a reference to the Master Page Gallery, which will be used to upload new master pages. Web web = clientContext.Web; List gallery = web.GetCatalog(116); Folder folder = gallery.RootFolder; // Load additional Master Page Gallery properties. clientContext.Load(folder); clientContext.Load(gallery, g => g.ContentTypes, g => g.RootFolder.ServerRelativeUrl); // Load the content types and master page information from the web. clientContext.Load(web, w => w.ContentTypes, w => w.MasterUrl, w => w.CustomMasterUrl); clientContext.ExecuteQuery(); // Get the content type for the master page from the Master Page Gallery using the content type ID for masterpages (0x010105). const string parentMasterPageContentTypeId = "0x010105"; ContentType masterPageContentType = gallery.ContentTypes.FirstOrDefault(ct => ct.StringId.StartsWith(parentMasterPageContentTypeId)); UploadAndSetMasterPages(web, folder, clientContext, settings, masterPageContentType.StringId); // Get the content type ID for the page layout, and then update references to the page layouts. const string parentPageLayoutContentTypeId = "0x01010007FF3E057FA8AB4AA42FCB67B453FFC100E214EEE741181F4E9F7ACC43278EE811"; ContentType pageLayoutContentType = gallery.ContentTypes.FirstOrDefault(ct => ct.StringId.StartsWith(parentPageLayoutContentTypeId)); UploadPageLayoutsAndUpdateReferences(web, folder, clientContext, settings, pageLayoutContentType.StringId); } }
在 Program.cs 中,添加 UploadPageLayoutsAndUpdateReferences 方法,此方法将执行以下步骤:
读取页面布局替换信息,方法是从 settings.xml 读取 pagelayout 元素、将页面布局替换信息存储在 LayoutFile 业务对象中,然后将所有业务对象添加到列表中。
对于要替换的每个页面布局:
使用 Web.ContentTypes 查询网站的内容类型,以查找匹配的内容类型,即网站的内容类型名称等于新页面布局的 settings.xml 中指定的 associatedContentTypeName 。
调用 UploadPageLayout。
调用 UpdatePages,将现有页面更新为使用新页面布局。
private static void UploadPageLayoutsAndUpdateReferences(Web web, Folder folder, ClientContext clientContext, XDocument settings, string contentTypeId) { // Read the replacement settings stored in pageLayout elements in settings.xml. IList<LayoutFile> pageLayouts = (from m in settings.Descendants("pageLayout") select new LayoutFile { File = (string)m.Attribute("file"), Replaces = (string)m.Attribute("replaces"), Title = (string)m.Attribute("title"), ContentTypeId = contentTypeId, AssociatedContentTypeName = (string)m.Attribute("associatedContentTypeName"), DefaultLayout = m.Attribute("defaultLayout") != null && (bool)m.Attribute("defaultLayout") }).ToList(); // Update the content type association. foreach (LayoutFile pageLayout in pageLayouts) { ContentType associatedContentType = web.ContentTypes.FirstOrDefault(ct => ct.Name == pageLayout.AssociatedContentTypeName); pageLayout.AssociatedContentTypeId = associatedContentType.StringId; UploadPageLayout(web, folder, clientContext, pageLayout); } UpdatePages(web, clientContext, pageLayouts); }
在 Program.cs 中,添加 UploadPageLayout 方法,此方法将执行以下任务:
签出页面布局文件。
使用 FileCreationInformation 上载新文件。
获取与新上载的文件关联的列表项。
设置列表项上的各种属性,包括 ContentTypeId 和 PublishingAssociatedContentType。
签入、发布和批准新的页面布局文件。
要删除对旧页面布局文件的引用,请调用 PublishingHelper.UpdateAvailablePageLayouts ,以更新存储在当前网站的 PageLayouts 属性中的 XML。
如果 settings.xml 中新上载的页面布局文件的 defaultLayout 属性设置为 true,请使用 PublishingHelper.SetDefaultPageLayout 更新存储在当前网站的 DefaultPageLayout 属性中的 XML。
private static void UploadPageLayout(Web web, Folder folder, ClientContext clientContext, LayoutFile pageLayout) { using (var fileReadingStream = System.IO.File.OpenRead(pageLayout.File)) { PublishingHelper.CheckOutFile(web, pageLayout.File, folder.ServerRelativeUrl); // Use FileCreationInformation to upload the new page layout file. var fileInfo = new FileCreationInformation(); fileInfo.ContentStream = fileReadingStream; fileInfo.Overwrite = true; fileInfo.Url = pageLayout.File; File file = folder.Files.Add(fileInfo); // Get the list item associated with the newly uploaded file. ListItem item = file.ListItemAllFields; clientContext.Load(file.ListItemAllFields); clientContext.Load(file, f => f.CheckOutType, f => f.Level, f => f.ServerRelativeUrl); clientContext.ExecuteQuery(); item["ContentTypeId"] = pageLayout.ContentTypeId; item["Title"] = pageLayout.Title; item["PublishingAssociatedContentType"] = string.Format(";#{0};#{1};#", pageLayout.AssociatedContentTypeName, pageLayout.AssociatedContentTypeId); item.Update(); clientContext.ExecuteQuery(); PublishingHelper.CheckInPublishAndApproveFile(file); PublishingHelper.UpdateAvailablePageLayouts(web, clientContext, pageLayout, file); if (pageLayout.DefaultLayout) { PublishingHelper.SetDefaultPageLayout(web, clientContext, file); } } }
在 Program.cs 中,添加 UpdatePages 方法,此方法将执行以下任务:
获取 Pages 库,然后获取 Pages 库中的所有列表项。
对于每个列表项,使用列表项的 PublishingPageLayout 字段查找要更新的匹配页面布局,此页面布局之前在 settings.xml 中指定,现在存储在 pagelayouts 中。 如果需要更新列表项的 PublishingPageLayout 字段以引用新的页面布局,请执行以下操作:
使用 PublishingHelper.CheckOutFile 签出列表项的文件。
将页面布局的 URL 更新为引用新的页面布局文件,然后更新列表项的 PublishingPageLayout 字段。
签入、发布和批准列表项引用的文件。
private static void UpdatePages(Web web, ClientContext clientContext, IList<LayoutFile> pageLayouts) { // Get the Pages Library, and then get all the list items in it. List pagesList = web.Lists.GetByTitle("Pages"); var allItemsQuery = CamlQuery.CreateAllItemsQuery(); ListItemCollection items = pagesList.GetItems(allItemsQuery); clientContext.Load(items); clientContext.ExecuteQuery(); foreach (ListItem item in items) { // Only update those pages that are using a page layout which is being replaced. var pageLayout = item["PublishingPageLayout"] as FieldUrlValue; if (pageLayout != null) { LayoutFile matchingLayout = pageLayouts.FirstOrDefault(p => pageLayout.Url.EndsWith("/" + p.Replaces)); if (matchingLayout != null) { // Check out the page so that we can update the page layout. PublishingHelper.CheckOutFile(web, item); // Update the pageLayout reference. pageLayout.Url = pageLayout.Url.Replace(matchingLayout.Replaces, matchingLayout.File); item["PublishingPageLayout"] = pageLayout; item.Update(); File file = item.File; // Get the file and other attributes so that you can check in the file. clientContext.Load(file, f => f.Level, f => f.CheckOutType); clientContext.ExecuteQuery(); PublishingHelper.CheckInPublishAndApproveFile(file); } } } }