2007年4月17日,星期二

将内容类型部署为功能

这是我的系列文章中的第三篇,该系列文章如何创建常见的SharePoint网站工件作为功能。上次我们看了 创建网站栏作为功能.

网站列通常用于列表和/或内容类型。概念非常简单,因为内容类型实际上也是具有一系列列的列表(对于使用CMS2002的人员,等效实体是模板定义)。这些列提供输入到页面上要存储的内容区域中的数据。但是,SharePoint中的内容类型也是粒度的重要单位,因为可以在此级别配置工作流,文档生命周期策略和其他几个关键功能。

您创建为网站上的页面模板的任何页面布局都必须与内容类型相关联。通常,内容类型中的每一列都将与页面布局上的字段控件匹配,从而允许作者输入要存储在每一列中的内容。

幸运的是,将内容类型创建为功能非常简单-仅使用CAML,不需要功能接收器(代码)。 Alas VSeWSS不能为我们提供很多帮助,但请不要忘记通过确保将要编写的XML文件链接到C:\ Program Files \ Common Files \ Microsoft Shared上的架构,在VS中使用CAML架构智能感知\ web服务器扩展\ 12 \ TEMPLATE \ XML \ wss.xsd。

最初,看似复杂的部分是内容类型的ID结构。每次我们为功能中的某项功能需要一个ID时(尽管从不信任第一个;-),就一直在Visual Studio中愉快地使用GuidGen,这令人惊讶,不得不阅读有关ID的文档。如中所述 内容类型ID,该结构反映了内容类型的祖先/父级,这意味着可以非常有效地确定父级内容类型,因为可以使用简单的字符串/字节匹配,从而减少了数据库查找。

基础知识是:

  • 找到您要派生的内容类型的ID。 可以通过检查SharePoint UI中的内容类型来完成此操作(“网站设置”>管理内容类型),然后从URL查询字符串中复制ID。或者,搜索MS开发人员用来部署即用型内容类型的功能文件。前者可能更简单,但后者提供了更多的学习空间。
  • 在末尾添加“ 00”,然后添加您生成的GUID(即使用GuidGen)。 您现在有了一个有效的内容类型ID。请注意,如果没有激活,您将获得有意义的异常。
  • 对于任何子内容类型,现在可以生成其ID add 上一步中生成的ID的简单ID,例如“ 01”或“ 02”。 因为您的唯一ID在字符串中,所以现在不必在ID后面加上'00'和另一个GUID。这意味着您生成的任何ID都会与其他任何ID不同,因此您可以使用简单的选项并使用2位数字而不是其他GUID。这意味着您的内容类型ID不能过长。

的 rest is fairly simple. Just 加 a FieldRef element for each site column the content type uses, specifying it's ID 和 name. Note that 我们用来创建网站列的方法 意味着我们必须在CAML中为其指定ID。我们只需要挖掘我们在此处指定的ID。因此,在这两个功能中,ID都是易于编辑的XML,而不是任何已编译的代码或类似代码,因此它们之间的耦合很松散。当然,在许多情况下,您会选择将两个工件都放在同一个要素中,并且将来我将发布有关要素要素分解的信息。

对于所部署的每种内容类型,最终应该得到类似以下内容:


<!-- this content type is derived from the 'Page' content type from the 'Publishing' feature -->
<ContentType ID="0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900CF38CCD7FC6E8247AA124F3EE5796C20"
Name="COBArticle"
Group="COB demo 内容类型"
Description="Base content type for articles."
Version="0">
<FieldRefs>
<FieldRef ID="{ae8e6ab8-b151-4fa4-8694-3cd24ad3b1bc}" Name="Locations" />
<FieldRef ID="{A4983A93-4B14-4a61-AE08-09108A718628}" Name="Sectors" />
<FieldRef ID="{6F26090A-C2AE-44d7-8F70-EE1663FE29F1}" Name="Disciplines" />
<FieldRef ID="{71316CEA-40A0-49f3-8659-F0CEFDBDBD4F}" Name="Article Date" />
</FieldRefs>
</ContentType>

请注意,虽然我注意到某些Microsoft功能会执行此操作,但不必重复在父内容类型中声明的字段。

因此,现在您已经部署了内容类型,它们现在可以在文档库/列表中使用或与您拥有的任何页面布局相关联。因此,下一次要部署母版页/页面布局/ CSS等作为功能,包括使布局自动绑定到内容类型。

33条评论:

匿名 said...

感谢提供这篇好文章!

我有一个问题。我添加了一个自定义内容类型,其中包含与我在此处概述的方式相同的文档库的四个自定义站点列。我已经在我的onet.xml文件中包含了用于网站定义的功能。我遇到的唯一问题是,为了查看/编辑自定义网站列,我首先需要对我在网站上创建的每个文档库执行以下操作:
Navigate to My Document Library->Settings->Advanced Settings 和 Allow management of 内容类型.
然后,将内容类型设置为默认内容类型。

我是否缺少自动执行此操作的步骤?

克里斯·奥'Brien 说过...

这是一个好点-在WCM网站中通常不会出现,因此我没有遇到过。

如果您的文档库是自定义文档库,那么如果您将其部署为功能部件,则应该可以修改“ ListInstance”标记中的属性来执行此操作。

但是,如果您使用的是默认文档库,我认为答案将是创建功能接收器,并使用API​​更改代码中文档库的属性。如果您不确定,我会在以下位置显示使用功能接收器的机制(尽管此处有所不同)
示例代码-创建基于列表的网站列作为功能.

克里斯·奥'Brien 说过...

匿名-自从我的回复以来,我考虑了您的问题,然后发生了一些事情。

您实际需要做的是在功能中使用ContentTypeBinding元素-这执行将内容类型与文档库关联的步骤。

该元素的文档位于 http://msdn2.microsoft.com/en-US/library/aa543152.aspx.

HTH,

克里斯。

匿名 said...

克里斯你好

感谢您的精彩文章。

我想知道我们是否可以使用功能部署自定义用户组?

谢谢你的帮助。

问候

克里斯·奥'Brien 说过...

关于将安全组创建为功能的问题。这不可能直接实现,但是由于可以使用API​​来控制SharePoint安全性,因此可以通过功能接收器来完成。

本质上,您编写代码以在事件处理程序中执行任务-在激活自定义功能时执行。也可以编写一些代码(可能会逆转操作),这些代码将在功能启用时执行 停用 .

对于您的情况,我建议您看一下 SPRole定义角色分配 类。

我的文章 创建网站栏作为功能 显示有关如何使用功能接收器的一些信息。

HTH,

克里斯。

匿名 said...

嘿克里斯,

感谢您的精彩文章,但是当我按照您的指示进行操作时,我遇到了一个小错误。

当我尝试从自定义内容类型创建子内容类型时,当我使用'00'将父内容类型的GUID与子内容类型的ID分开时,会收到错误消息。但是,当我添加子级内容类型的ID时,在本例中,在父级GUID之后立即添加“ 01”,该功能将正常部署。

问候,

本杰明

克里斯·奥'Brien 说过...

本杰明

是的,我在那儿还不够清楚,因为我设法用两个相邻的句子来矛盾自己。

的 '00' separator is only required if the suffix being 加ed to the ID is a GUID. Article now updated!

非常感谢,

克里斯。

匿名 said...

克里斯,你好
感谢这篇好文章。我已经从Item创建了一种内容类型,我想通过Feature接收器中的一段代码将其添加到自定义列表中。我可以将自定义内容类型添加到列表中,但父内容类型Item也在那里。有什么方法可以通过C#代码删除基本内容类型Item?

克里斯·奥'Brien 说过...

嗨,阿伦,

是的,您可以使用API​​从列表中删除内容类型,但是只有在不使用该内容类型时,它才有效。进行的调用将是SPList.ContentTypes.Delete()。

另请注意,您不会 需要 使用代码将内容类型与列表关联-如前面的注释所述,可以使用ContentTypeBinding元素在Feature XML中完成-请参见 http://msdn2.microsoft.com/en-US/library/aa543152.aspx 欲获得更多信息。

HTH,

克里斯。

阿米特·库尔卡尼(Amit Kulkarni) 说过...

你好

我有一个与内容类型ID生成相关的问题。请考虑以下情形:
1)我的服务器场包含3个网站集。
2)在每个网站集上,我都有一个使用Sharepoint UI创建的内容类型名称“ xyz”。据我所知(我是Sharepoint的新手),内容类型ID将是唯一的吞吐量网站集。因此,“ xyz”的3个实例(来自每个网站集)将具有3个不同的ID(它们是)。
3)现在,我要使用功能在“ xyz”下创建一个新的内容类型“ abc”。
4)我对ContentType.xml中的Content Type Id属性感到困惑。由于每个网站集上的“ xyz”将具有3个不同的ID,因此我应该通过哪个ID创建新的ID?而其他网站集的“ xyz”将如何处理?

请帮我。

提前致谢。

克里斯·奥'Brien 说过...

嗨,阿米特,

有趣的场景。是的,如果您的内容类型ID(因为它们是使用UI创建的)不具有相同的词干(开头),那么使用功能创建子内容类型将不容易。

虽然有一种方法。您可以做的是使用API​​创建内容类型,但是将代码编写为功能接收器的一部分(例如,与仅通过控制台应用程序针对SharePoint实例运行代码相反)。这将允许您使用功能激活/停用等功能,但可以使用代码完全控制内容类型的创建。使用此技术,您可以使用API​​来“询问”该内容类型/网站集的内容类型ID,然后将后缀添加到末尾以生成子内容类型的ID。

HTH,

克里斯。

匿名 said...

克里斯,你好

内容类型版本控制如何工作?我已经为新闻页面创建了内容类型,并添加了一些新闻-到目前为止一切都很好。现在,当我在内容类型功能中更改某些列类型并进行部署时-旧新闻引发异常...

克里斯·奥'Brien 说过...

嗨,安德里斯,

这么晚才回复很抱歉。内容类型没有这样明确地版本化,并且管理内容类型更新可能很复杂。实际上,这不应通过更改功能XML来完成,而应使用标准屏幕或API来完成。如果您希望更改可以轻松地在不同环境中复制,建议使用后者是一种好方法(例如在功能接收器中)。

MSDN文档对此有一些不错的阅读-请参阅:

- 更新内容类型
- 更新子内容类型
- 内容类型变更控制

如果仍然不清楚,并且您认为我可以提供帮助,请给我另一条评论。

HTH,

克里斯。

匿名 said...

嗨,谢谢你的一篇非常好的文章。您能否发布一个在CAML功能中部署查找字段的示例?

克里斯·奥'Brien 说过...

服从

看一下 将查找列创建为功能.

谢谢,

克里斯。

来源 说过...

克里斯,

我需要获取给定URL的列表的GUID,例如http://server/site/list/Lists/AllItems.aspx

有没有办法做到这一点?当我尝试获取URL的ID时,出现错误消息:“值不在预期范围内”

请帮忙 :)

-亚当

克里斯·奥'Brien 说过...

嗨,亚当,

要获取列表的GUID,请转到列表的设置页面。然后,GUID将显示在URL中,您将看到类似以下内容的内容:

/_layouts/listedit.aspx?List=%7B72201421%2D14DC%2D4E71%2DA553%2D65BA1AA22881%7D

然后,您可以执行以下替换:

左括号({)为%7B
右括号(})为%7D
带破折号(-)的%2D

然后,您将获得列表GUID的未编码形式。

HTH,

克里斯。

来源 说过...

谢谢克里斯,但我需要以编程方式进行此操作。有办法吗?

克里斯·奥'Brien 说过...

好的-在这种情况下,请使用以下代码获取对列表的引用:

SPList列表= site.AllWebs [sWebName] .Lists [sListName];

您需要引用SPSite对象(上面的代码中为“ site”),以及Web和列表名称。获得SPList对象后,只需调用SPList.ID即可获取ID-在代码中获取ID时无需取消编码。

HTH,

克里斯。

匿名 said...

我们是否可以使用仅具有站点列名称但没有站点列的向导的功能来部署内容类型。

另一个问题是内容类型的ID我们如何生成它,它是中间没有'-'的GUID吗?

克里斯·奥'Brien 说过...

你好

不幸的是,您需要列的ID才能使内容类型正确地引用它们-无法绕过此AFAIK。

关于内容类型ID的工作方式,本文上面介绍了基本步骤(请参见上面的“基础知识”),但是 有关内容类型ID的MSDN文档 有更多详细信息。

HTH,

克里斯。

马达瓦 说过...

克里斯,您好,谢谢。
我有一个小问题。

我们是否可以将内容类型部署为具有某些扩展设置(例如“工作流程设置”和“信息管理策略设置”)的功能?

请给我解释一下。谢谢

匿名 said...

克里斯,您好,我只是想部署网站集功能。(它的内容类型是从另一个框架继承的)。我通过将GUID中的前几个字符更改为0x0101即打破了继承。用于文档内容类型。当我尝试部署时,提示“找不到文件”错误。可以吗丢点光?
拉吉

匿名 said...

亲爱的克里斯,

我看到了您对Andris关于内容类型更新的回复。我正在尝试更新页面布局,这需要向内容类型添加新字段。一个全新的字段,需要将其添加到站点列xml中,并将另一个字段添加为站点列。

我正在尝试通过更新原始xml文件,升级解决方案以及停用/激活功能来实现此目的,现在我已经知道在阅读后完全错了 微软的这篇文章.

如果我正确理解,我唯一的选择是“网站设置”和“ API”。我是否可以假设无法使用新的xml创建新功能,而该新xml仅包含具有引用父级ID的内容类型的更新?

我真的不明白在哪里放置任何代码,因为这无法通过UI完成。

非常感谢您的协助。

克里斯·奥'Brien 说过...

@匿名,

是的,您是正确的,因为UI和代码是更新现有内容类型的唯一选项。避免创建新功能以尝试更新最初从其他功能部署的工件。

就将代码放置在何处而言,这无关紧要。一些选项可能是:

-创建一个具有功能接收器的新功能,该功能接收器包含用于更新内容类型的代码
-其他位置,例如您将在服务器上运行的控制台应用程序

HTH,

克里斯。

克里斯·奥'Brien 说过...

@Madhawa,

很抱歉延迟回复,您张贴时我正在度假。

是的,可以申请吗 一些 扩展设置-我知道可以执行IM策略,不确定工作流程。该区域尚未有据可查的AFAIK。他们的关键是在内容类型架构中使用XmlDocuments元素-请参阅 http://msdn.microsoft.com/en-us/sharepoint/ms549876.aspx 有关一些信息。您可能还需要做一些额外的搜索才能使用完整模式查找示例。

HTH,

克里斯。

来源 说过...

可以在ContentTypes :: Definition的内容数据库的数据库中看到XmlDocuments模式。如果您已有带有IM策略的内容类型,则可以在此处查看。您需要解码base64字符串才能使用。

我尝试将其手动插入elements.xml文件中,并使用到期策略作为功能来部署内容类型,但到目前为止尚未成功。如果有人找到了窍门,请发布。

克里斯·奥'Brien 说过...

@Raj,

很抱歉延迟回复,您张贴时我正在度假。

我怀疑您看到的错误是由您所使用的内容类型ID之外的其他原因引起的。就功能文件/解决方案清单中的文件路径/文件名而言,一切是否正确(如果通过解决方案进行部署)?

建议使用原始内容类型ID进行测试-您始终可以在使用任何内容类型之前将其删除。

干杯,

克里斯。

劳尔·奎罗加(Raul Queiroga) 说过...

克里斯,你好
我正在合作网站上使用您的方法。我想使用在列表定义中具有查找字段的内容类型。
问题在于,在schema.xml中,必须描述内容类型字段,并且在查找的情况下,我们必须放置列表ID ...如果现在不知道该如何设置列表ID网站栏所指的ID?
有什么想法吗?

谢谢!

克里斯·奥'Brien 说过...

本蒂尼

为此,您必须编写代码-然后可以检索列表ID并设置查找。我在Codeplex上有一些执行此操作的代码,看看 http://www.codeplex.com/SP2007LookupFields

HTH,

克里斯。

匿名 said...

克里斯,

我注意到您关于无法通过功能更新内容类型的评论。我发现附带的评论对此很有趣,希望您也会如此。这是链接;
http://blog.sharepoint.ch/2008_06_01_archive.html

匿名 said...

这是一篇很棒的文章。我想知道的一件事...

我有一列要在多个网站集中使用的列,该列将是一个选择列,并且我希望各列的选择集中具有一致性。这就是为什么我要考虑通过功能进行部署。

我希望选择选项易于管理,以便非IT人员可以在情况发生变化时更新选项列表。

因此,您对查找列表的概念很有意义。但是,是否可以在一个中央列表中进行查找,该中央列表可能会或可能不会出现在将使用网站列的同一网站集中?

克里斯·奥'Brien 说过...

@本N,

是的,这是一个相当普遍的要求。使用我概述的方法/代码 示例代码-将查找列创建为功能,您可以指定要查找的网络和列表。

跨站点查找字段 from 的SharePoint Solutions 加resses this in the same way.

不幸的是,如果您需要在其他网站集中查找列表,那么您很不走运-您需要执行一些自定义操作来解决此问题。

HTH,

克里斯。