SharePoint 自定义预配提供程序
关于站点创建方法和使用自定义预配提供程序的讨论
引言
在 SharePoint 中,有多种创建网站的方法,每种方法都有其优点和缺点。自定义网站部署提供程序被许多人认为是创建自定义 SharePoint 网站的最佳方法。在这篇简短的文章中,我将演示如何创建和使用自定义网站部署提供程序。
背景
本文中的示例使用了 SharePoint Server 2010,尽管这些技术也适用于 Microsoft Office SharePoint Server 2007。
创建网站的多种方法
在 SharePoint 中,有多种方法可以创建自定义网站定义:网站模板、网站定义和网站部署提供程序。
网站模板
Microsoft SharePoint 可由具有适当权限和授权的最终用户进行高度配置;这就是它的设计初衷。用户可能花费大量时间配置包含列表、页面和视图的网站,并希望将其复制到其他环境中。这时网站模板就很有用了。
要创建网站模板,请转到“网站操作 -> 网站设置
”,然后在“网站操作
”标题下,单击“另存为模板”链接。

显示的页面将要求输入文件名、模板名称和描述,以及是否包含内容。填写此信息并单击“确定”按钮后,SharePoint 将此网站打包为 WSP 文件,并附带适当的配置文件,然后将其放置在解决方案库中。从此,它可用于在网站集内创建新网站。要将模板用于其他网站集,您必须转到“解决方案库
”并将模板保存到外部位置,即您的硬盘驱动器,然后在另一个网站集中上传并激活该模板在“解决方案库
”中。
以上所有过程都可以由开发人员和场管理员介入完成。但是,这种方法也有缺点。
使用网站模板并不是真正可重用的解决方案。是的,它可以保存并上传到不同的网站集,但它可能依赖于其他网站集不存在的功能或安全性。使用“包含内容”选项时,您还受 10MB 的限制,如果网站仅用于结构创建,这还可以,但如果它使用了大量需要功能的列表项,那您就无能为力了。网站模板也不支持项级安全性,因此在从模板创建新网站后,可能需要重置所有安全性。
网站模板打包为 WSP,使用 Visual Studio 2010,可以将其导入到新项目中。但是,它会带来大量无关的附加信息。

尽管您可以选择或取消选择您想要的元素,但其中一些确实存在依赖关系,您需要将它们导入项目才能完成。

网站定义
网站定义是 SharePoint 的基础,在从内置模板创建网站时使用。如上所述,保存网站模板时也会创建自定义网站定义。由于有许多资源涵盖 SharePoint 网站定义,我将不详细介绍,只为本次讨论提供一些要点。
网站定义由一个 `Onet.xml` 文件组成;我在下面包含了一个基本版本。有关此文件的更多信息可以在此处找到。
<?xml version="1.0" encoding="utf-8"?>
<Project Title="SiteDefinitionProject1"
Revision="2"
ListDir=""
xmlns:ows="Microsoft SharePoint"
xmlns="http://schemas.microsoft.com/sharepoint/">
<NavBars>
</NavBars>
<Configurations>
<Configuration ID="0" Name="SiteDefinitionProject1">
<Lists/>
<SiteFeatures>
</SiteFeatures>
<WebFeatures>
</WebFeatures>
<Modules>
<Module Name="DefaultBlank" />
</Modules>
</Configuration>
</Configurations>
<Modules>
<Module Name="DefaultBlank" Url="" Path="">
<File Url="default.aspx">
</File>
</Module>
</Modules>
</Project>
正如您所看到的,`Onet.xml` 中可以包含大量信息,用于定义网站的创建方式及其将使用的功能。您可以定义网站使用的列表模板、导航元素以及站点页面等文件。该文件还描述了需要在网站和 Web 级别激活的任何功能。
尽管这种方法多年来一直与 SharePoint 一起使用,并且内置网站模板也使用此方法,但它有一个显著的缺点。对网站定义的任何更改都会影响使用该定义创建的所有网站。为了说明这一点,请生成并部署本文代码下载中提供的 CPSiteDef 解决方案,并使用此定义添加一个新网站。在创建网站案例后,主页应如下所示

现在,在 Visual Studio 项目中,展开“网站定义”模块并打开 `default.aspx` 页面,添加或修改任何您想要的文本。当您重新部署解决方案并刷新页面时,您应该会看到所做的更改。

您无需创建另一个使用该模板的网站即可看到此更改,它将自动更新所有网站。显然,这可能会产生一些非常不受欢迎和意外的影响。
网站部署提供程序
提供创建新网站机制的新推荐方法是使用自定义网站部署提供程序。
当然,首先我们需要在 Visual Studio 中创建一个新的空 SharePoint 2010 项目。然后,向该项目添加一个代码文件,并使其派生自 `SPWebProvisioningProvider`。此基类有一个需要实现的方法,即 `Provision`。
public class CPSiteProvider : SPWebProvisioningProvider
{
public override void Provision(SPWebProvisioningProperties props)
{
throw new NotImplementedException();
}
}
当然,无论使用网站定义还是自定义部署提供程序(如本例),都必须有一个 `webtemp*` 文件,以便 SharePoint 能够识别并使模板可用。有关此文件的详细信息可以在此处找到。但对于本次讨论,关键点是 `ProvisioningAssembly`、`ProvisioningClass` 和 `ProvisionData` 属性。
<?xml version="1.0" encoding="utf-8" ?>
<Templates>
<Template Name="CPSiteProvider" ID="25001">
<Configuration
ID="0"
Title="Code Project Site Provisioning Provider"
Hidden="false"
ImageUrl="/_layouts/images/CPSiteProvider/CP_Logo.gif"
Description="A demonstration or using a custom Site
Provisioning Provider"
DisplayCategory="Code Project"
ProvisionAssembly="$SharePoint.Project.AssemblyFullName$"
ProvisionClass="CPSiteProvider.ProvisioningProvider"
ProvisionData="TEMPLATE\FEATURES\
CPSiteProvider_SiteData\ProvisioningData\CPSiteProvider.xml"
SubWebOnly="TRUE">
</Configuration>
</Template>
</Templates>
对于 `ProvisioningAssembly`,我们将只使用内置的 Visual Studio 宏,它会在项目生成时扩展为正确的四部分条目。`ProvisioningClass` 是实现 `SPWebProvisioningProvider` 的类的完全限定名称,在本例中为 `ProvisioningProvider`。`ProvisionData` 属性用于指定部署提供程序所需的任何额外数据。在这种情况下,我使用的是相对于 SharePoint 根文件夹的 XML 文件的路径,该文件将包含其他信息。
部署提供程序是一个空白画布,除非被告知,否则不会创建任何内容。考虑到这一点,我们将首先使用 `SPWeb` 对象的 `ApplyWebTemplate` 方法来创建一个空白网站作为开始。如您所见,`web` 是传递到 `Provision` 方法的 `SPWebProvisioningProperties` 属性中可用的属性之一。另一个属性是 `Data`,它将包含我们在 `webtemp` 文件中指定的 `ProvisionData` 属性的值,我们将稍后利用它。
private const string SITE_TEMPLATE = "STS#1";
public override void Provision(SPWebProvisioningProperties props)
{
// Create a blank site to begin from
props.Web.ApplyWebTemplate(SITE_TEMPLATE);
}
现在基本网站已创建,我们将添加必要的特性来适当地配置我们的网站。
public override void Provision(SPWebProvisioningProperties props)
{
// Create a blank site to begin from
props.Web.ApplyWebTemplate(SITE_TEMPLATE);
// Save this so it is available in other methods
Properties = props;
SPSecurity.CodeToRunElevated code =
new SPSecurity.CodeToRunElevated(CreateSite);
SPSecurity.RunWithElevatedPrivileges(code);
}
private void CreateSite()
{
using(SPSite site = new SPSite(Properties.Web.Site.ID))
{
using(SPWeb web = site.OpenWeb(Properties.Web.ID))
{
// Add specified features to this site
AddSiteFeatures(site);
// Add specified features to this web
AddWebFeatures(web);
// Add new default page
AddDefaultPage(web);
}
}
}
由于网站可以由任何人创建,因此我们需要确保拥有适当的安全权限,这就是 `SPSecurity.RunWithElevatedPrivileges` 有用的地方。这将使任何在其上下文中运行的代码都以 Web 应用程序 `AppPool` 的凭据执行。尽管您可以使用匿名委托来使用此方法,但为了提高可读性和可维护性,我将创建一个 `SPSecurity.CodeToRunElevated` 对象并分配一个要运行的方法。这有一个小问题,该方法的委托不接受任何参数。为了能够访问 `SPWebProvisioningProperties`,我将其分配给一个 `private` 属性。
在 `AddSiteFeatures` 方法中,首先要做的是访问包含我们希望在此站点中启用的特性的 XML 文件。由于路径是相对的,我们需要找到 `SharePoint` 根文件夹。尽管它通常是 `"%Program Files%\Common Files\Microsoft Shared\Web Server Extensions\14"`,但作为优秀的开发人员,我们不能依赖它,而应专门查询对象模型以获取正确的路径。这在 `DataFile` 属性中使用 `SPUtility.GetGenericSetupPath` 方法完成。
private void AddSiteFeatures(SPSite site)
{
List<xelement> features = (from f in DataFile.Elements("SiteFeatures")
.Elements("Feature")
select f).ToList();
foreach(XElement feature in features)
{
// Make sure the feature hasn't already been activated
SPFeature f = site.Features[new Guid(feature.Attribute("ID").Value)];
if(f == null)
{
site.Features.Add(new Guid(feature.Attribute("ID").Value));
}
}
}
private XElement DataFile
{
get
{
XElement featuresXml = null;
if(Properties != null)
{
// Construct the path from the SharePoint root folder to
// the file specified in the webtemp
string path = SPUtility.GetGenericSetupPath
(Path.GetDirectoryName(Properties.Data));
path = Path.Combine(path, Path.GetFileName(Properties.Data));
// Load the xml file
featuresXml = XElement.Load(path);
}
return featuresXml;
}
}
</xelement>
获取 XML 文件后,只需将指定的特性添加到站点或 Web 即可。我使用了与网站定义项目相同的特性,以便进行比较,但您可以添加任何其他需要的特性。此时也可以将任何列表或其他项添加到网站。
这种方法的好处是网站直到运行时才创建,并且不像网站定义方法那样依赖于文件系统中的文件。为了进行精确比较,我们需要包含 `default.aspx` 页面。
private void AddDefaultPage(SPWeb web)
{
string file = (from f in DataFile.Elements("DefaultPage")
select f).Single().Attribute("file").Value;
string filePath = FeaturePath + "\\" + file;
TextReader reader = new StreamReader(filePath);
MemoryStream outStream = new MemoryStream();
StreamWriter writer = new StreamWriter(outStream);
writer.Write(reader.ReadToEnd());
writer.Flush();
web.Files.Add("Default.aspx", outStream, true);
}
在这里您可以看到,我们从部署文件中提取了将使用的 `Default.aspx` 页面的路径,然后将文件的内容读入 `Stream` 并将其写回 `StreamWriter`。在 `SPWeb.Files.Add` 方法中使用 `true` 将允许现有 `Default.aspx` 页面被我们的新文件覆盖。由于这会将页面添加到内容数据库,因此它现在是非托管的,因此独立于文件系统中的文件。
您可以像以前一样测试此过程。基于此模板创建新网站。在 Visual Studio 项目中更新 `default.aspx` 页面并重新部署解决方案。如果刷新页面,您将看不到任何更改。如果您创建一个新网站,那么将使用修改后的 `default.aspx` 页面。
结论
本文旨在解释在 SharePoint 中创建和应用网站模板的不同方法以及它们各自的优缺点。希望这能让读者对选择最适合项目的方法有一个很好的理解。
历史
- 首次发布:11/04/21