SharePoint 2010 在上一版本的基础上进一步发展了内容类型对象。本系列作为一个专题,试图对其进行一个深入的剖析。方便大家在自己的自定义解决方案中对其进行定制。
内容类型的定义
SharePoint 2010 中,内容类型是我们要应用到特定内容类别的可重用的设置集合。
- 利用内容类型,可通过集中、可重用的方式管理某类别的信息的基础架构(如文档、项目或文件夹类型的元数据和行为)。
- 定 义在网站层次上,独立于网站中的任何文档库或列表。
- 可以将一个内容类型用在多个列表或文档库中。甚至,可能是在整个网站集范围内共用。
- 一 个列表或文档库可以附加多个内容类型。在用户点击“新建”时 ,可以选一种内容类型。其中每个内容类型都可以拥有独立的栏设置,工作流设置和其它相关行为。
一个企业内部的典型应用
“费用报告”内容类型:用户完成一个费用报告的提交工作可以分成以下步骤
创建内容类型,然后挂到列表或库上,如下图:
内容标准化
公司内除了“费用报告”外,还有“采购订单”和“发票”两种文件类型。这三种类型的文档有一些共同点。比如,都是财务类文档,包含有金额信息。但 是,它们各自又都有些独有的要求,使用不同的文档模板和工作流。创建4个内容类型:第一个内容类型为“财务文件”,用于封装组织中所有财务文件公共的数据 需求。其余3个为“采购订单”、“发票”、“费用报告”,继承“财务文件”公共的部分,并在进一步扩展出自身独特的部分。可以把它们用在同一个文档库中, 以实现公司财务文档的统一管理。
内容类型的范围
- 内容类型的范围用于定义内容类型的可用性
- 内容类型的范围等于定义该内容类型的站点及其所有子站点
- 由 此可得,在顶级站点添加的内容类型将在整个网站集内可用。
下图为内容类型范围的一个例子。通过折线所框的面积,可以形象化的理解各个内容类型的可 用范围。
内容类型封装数据需求
内容类型是封装某类信息的数据需求的一种手段。它可以包含以下信息:
- 元数据属性(或称为栏)
- 特定的新建,编辑,查看表单
- 基于用户的操作,在特定情况下启动的工作流
- 对 于文档,指定其文档模板
- 其他自定义信息
继承原则
- 永远不会从头开始创建内容类型的。必须基于一个现有的内容类型来创建新内容类型会自动继承父代的元数据,工作流,以及其他行为。
- 实 际使用中,和构建基类一样。首先定义好基本内容类型。例如,我们构建一个财务文档内容类型,代表公司内所有财务类文档。然后,以此为父,新建两个内容类 型:发票和采购订单。根据继承原则,我们可以将一些共同的元素放在父内容类型中。这样新的内容类型将自动具有这些元素。
- “网站设置”里 面的“网站内容类型”库中就可以看到这种模式。可以用于列表的最基本的内容类型是项目。联系人、链接 、 评论、 任务、事件 、通知、文章、问题、 消息等等都继承自项目。同理,可以用于文档库的最基本内容类型是文档。Wiki网页、表单、都柏林核心栏、空白页、母版页、图片、资产等等都继承自文档。 而图像、音频、视频由继承自资产。因此,在创建内容类型时,一定要首先想好将来是用于文档库还是用于列表。
- 继承原则的主要用途是隔离变 化的传播。理解时可以类比为NTFS文件系统的权限继承或者对于开发人员可以类比为类的继承。
下图为 SharePoint自带的一些内容类型及其在内容类型管理页面中显示的层次关系:
子内容类型
每个内容类型都包含父内容类型的一个引用。这样,就实现了从父代到子代的变更推送。
推送只会覆盖发生变更的部分,不会整个覆盖。
通过UI更新子内容类型
内容类型的某个设置页面包含的所有设置项都将在推送过程中被覆盖.
内容类型通常包含下列设置页
- 高级设置页面
- 文档模板的URL
- 实际的文档模板文件,可选择网站中现有的模板,或上传一个模板文件
- 只读属性
- 添 加网站栏到网站内容类型的页面
- 修改栏设置的页面
- 必需(必须包含信息
- 隐藏(将不显示在表单中)
- 从内容类型中删除该栏
更新子内容类型的注意事项
- 如果子内容类型的更改落在父内容类型的推送粒度范围内,则将被覆盖。比如,我们在子内容类型的一栏上修改了某些设置,然后对父级的栏模板 上又进行修改,甚至删除,这时的推送就会覆盖子内容类型原有的修改
- 每个推送操作只会针对当时的父内容类型的变更。也就是说,一旦错过了 这次推送。以后再想推送这些变化就很困难了。这种情况下只能撤销原先的更改,然后重做,再进行推送。比如,我们从父CT删除了以栏,但当时没有推送。任何 后续的推送过程都将不会包含从子CT中删除该列的操作。若要从子CT删除该栏,我们必须在父CT上将栏添加回去,然后再删除它,然后再执行推送操作。
- 如 果所推送的变更对子代不再适用,则会被忽略。比如,如果我们推送一个在子代中已被删除的栏的设置,该设置将被忽略。这种情况下SharePoint是不会 从新把栏添加回CT的。
- 如果子代设置为只读,则推送操作会失败。除非推送操作包含父代的只读/可写设置。
- 变更的推送 不是一个全或无的过程。指定子CT上报的错不会影响其他的子CT。在推送结束后,SharePoint会给出一个错误清单。
- 如果子CT 定义为密封的,则推送操作会失败。
- 创建或管理网站内容类型需要有网站设计者权限。如果你在某个子网站上没有所需的权限,则在该子站点中 包含的CT上推送会失败。
内容类型变更控制
- 阻止用户对某个内容类型的修改
- 无法阻止用户继承某网站内容类型创建新内容类型
- 只读内容类型:用于警 告用户不应该对该内容类型进行更改。如果需要更改,用户必须关闭只读。
- 密封内容类型:用于更严格的控制。
- 无法通过SharePoint UI修改。
- 必须是网站集管理员才可以通过OM修改成非密封。(通过 SPContentType类的Sealed属性)
网站内容类型与列表内容类型
内容类型的创建与维护通常都在网站层次上进行。当新内容类型创建并添加到网站内容类型集合后,它是一个网站内容类型。 此时该内容类型可以被添加到列表或文档库,但是还没有被添加。
当我们将一个网站内容类型添加到列表时,SharePoint会生成一个该网站内容 类型的本地副本,并将该副本添加到列表。这个本地的实例被称为列表内容类型。它只能用在该列表内。
默 认列表内容类型:SharePoint里的每个项都必然会属于一个内容类型。这可能是显性的也可能是隐性的。对未允许内容类型的列表栏 的更改实际上发生在其默认列表内容类型上。
内容类型分组
内置的内容类型分了很多组。如下图所示:
通过内容类型对象的Group属性获取所属分组。
_Hidden组 的内容类型不会显示在UI中。也就无法派生或添加到列表中
内容类型对象模型
- 包括内容类型的创建,查找,添加,更新,删除
- 服务器端代码使用Microsoft.SharePoint命名空间。此 命名空间的绝大多数类都有对应的Microsoft.SharePoint.Client命名空间类,供客户端调用
- 要得到某个特定内容 类型定义在哪个网站上,可以通过SPContentType对象的ParentWeb属性获得。也可以通过该对象的Scope属性获得一个定义网站的服务 器相对URL地址。
内容类型Id
内容类型的ID用于在网站集内唯一标识内容类型,并设计为可递归。ID封装了该内容类型的血统,或者说是从父内容类型到该内容类型的一条继承线。 每个内容类型的ID都包含了父内容类型的ID,一次类推,直到系统内容类型的ID。推送操作正式基于此。
可以基于以下两个公约之一构造一个有效的 内容类型ID。两者强调的重点不同,一个强调简洁,一个强调独特。可根据具体情况选择。
- 父内容类型ID + 两位16进制值(“00”除外)。通常默认的内容类型都是采用这种方式。
- 父内容类型ID + “00” + 16进制的GUID。通常我们继承的网站内容类型和列表内容类型都是采用这种方式。通常我们的自定义解决方案中推荐使用这种方式。以保证唯一性。但对于其 子代,推荐使用上一种方式,这样可以有效减少ID的长度,以保证可以继承的代数。因为ID的长度是有一个512字节的限制。由于一个字节可以存放两位16 机制值,所以内容类型ID不能大于1024个字符。
下图展示了SharePoint自带的文档内容类型的ID,以及基于两种公约进行的一些ID继承变化:
获取内容类型ID
- 通过网站设置,内容类型管理页面:鼠标悬停在内容类型名称上时,状态栏上可以看到ID。
- 通过 SPBuiltInContentTypeId类,可以很方便的获取SharePoint内置的内容类型的ID。
如:获取文档内容类型
SPContentType documentCType = web.AvailableContentTypes[SPBuiltinContentTypeId.Document]; - 通过 SPContentType对象(服务端)或ContentType对象(客户端)获取的Id属性并不是一个16进制的字符串,而是一个 SPContentTypeId对象(服务端)或ContentTypeId对象(客户端)。如果我们知道ID的16进制串,可以通过构造器得到对应的对 象。
如:获取文档内容类型
SPContentTypeId documentCTypeId = new SPContentTypeId("0x0101");
例子:获取网站内容类型——“项目”有多少子代。统计其被其他网站内容类型和列表内容类型继承的次数。
using System.Collections.Generic;
using Microsoft.SharePoint;
namespace Test
{
class ConsoleApp
{
static void Main(string[] args)
{
using (SPSite siteCollection = new SPSite("http://sp2010u/sites/contoso"))
{
using (SPWeb rootWeb = siteCollection.RootWeb)
{
// 获取内容类型
SPContentType contentType =
rootWeb.AvailableContentTypes[SPBuiltInContentTypeId.Item];
//获取使用情况的集合.
IList<SPContentTypeUsage> usages = SPContentTypeUsage.GetUsages(contentType);
// 对网站内容类型和列表内容类型进行计数.
int listTypes = 0;
int siteTypes = 0;
foreach (SPContentTypeUsage usage in usages)
{
if (usage.IsUrlToList)
listTypes++;
else
siteTypes++;
}
Console.Write("The content type is inherited by {0} site content types", siteTypes);
Console.WriteLine(" and {0} list content types.", listTypes);
}
}
Console.Write("\nPress ENTER to continue...");
Console.ReadLine();
}
}
}