SharePoint2010内容类型剖析(二)
内容类型变更控制
- 阻止用户对某个内容类型的修改
- 无法阻止用户继承某网站内容类型创建新内容类型
- 只读内容类型:用于警告用户不应该对该内容类型进行更改。如果需要更改,用户必须关闭只读。
- 密封内容类型:用于更严格的控制。
- 无法通过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();
}
}
}
在我的环境中的运行结果如下:
Press ENTER to continue...
代码创建的内容类型的Id
当我们在代码中创建内容类型时,不必为它编个ID。相反,我们可以让系统基于父ID自动生成。
如下面的控制台应用程序所示,从项目内容类型派生出一个新的客户内容类型。并添加到网站内容类型集合中。然后又将该内容类型附加到某个列表内容类型集合上。最后,比较全部3个内容类型的ID:顺序为父内容类型,新的网站内容类型,新的列表内容类型。
using Microsoft.SharePoint;
namespace Test
{
class ConsoleCTId
{
static void Main(string[] args)
{
using (SPSite site = new SPSite("http://sp2010u/sites/contoso"))
{
using (SPWeb web = site.OpenWeb())
{
// 获得网站内容类型——项目
SPContentType parentCType = web.AvailableContentTypes[SPBuiltInContentTypeId.Item];
// 创建一个新的内容类型——客户,派生自项目.
SPContentType childCType = new SPContentType(parentCType, web.ContentTypes, "客户");
// 将新的内容联系添加到网站内容类型集合
childCType = web.ContentTypes.Add(childCType);
// 添加到一个列表.
SPList list = web.Lists["通知"];
list.ContentTypesEnabled = true;
SPContentType listCType = list.ContentTypes.Add(childCType);
// 输出全部的3个ID.
Console.WriteLine("Parent content type ID: {0}", parentCType.Id.ToString());
Console.WriteLine("Site content type ID: {0}", childCType.Id.ToString());
Console.WriteLine("List content type ID: {0}", listCType.Id.ToString());
}
}
Console.Write("\nPress ENTER to continue...");
Console.ReadLine();
}
}
}
在我的环境中的运行结果如下:
Site content type ID: 0x0100B0B200027F78F54CAA7B1095536B3DE4
List content type ID: 0x0100B0B200027F78F54CAA7B1095536B3DE40041C512D9D0D61741A4239F25B2988C48
Press ENTER to continue...
如何以指定Id的方式在代码中创建?
SPContentType构造器也可以通过SPContentTypeId方式创建。此时,其Parent属性的值实际上是通过分析所传递的内容类型ID而得到的。
内容类型的排序
代码中的注释很详细,就不多废话了:
using System.Collections;
using Microsoft.SharePoint;
namespace Test
{
class ConsoleCTGroup
{
static void Main(string[] args)
{
using (SPSite site = new SPSite("http://sp2010u/sites/contoso"))
{
using (SPWeb web = site.OpenWeb())
{
// 创建一个内容类型的可排序列表.
ArrayList list = new ArrayList();
foreach (SPContentType ct in web.AvailableContentTypes)
list.Add(ct);
// 以组名排序.
list.Sort(new CTComparer());
// 输出.
Console.WriteLine("{0,-35} {1,-12} {2}", "Site Content Type", "Parent", "Content Type ID");
for (int i = 0; i < list.Count; i++)
{
SPContentType ct = (SPContentType)list[i];
if (i == 0 || ((SPContentType)list[i - 1]).Group != ct.Group)
{
Console.WriteLine("\n{0}", ct.Group);
Console.WriteLine("------------------------");
}
Console.WriteLine("{0,-35} {1,-12} {2}", ct.Name, ct.Parent.Name, ct.Id);
}
}
}
Console.Write("\nPress ENTER to continue...");
Console.ReadLine();
}
}
// 基于 IComparer 接口实现比较操作.
// 先通过组名比较,然后通过内容类型Id比较.
class CTComparer : IComparer
{
// 实现 Compare 方法.
int IComparer.Compare(object x, object y)
{
SPContentType ct1 = (SPContentType)x;
SPContentType ct2 = (SPContentType)y;
// 首先比较组名 .
int result = string.Compare(ct1.Group, ct2.Group);
if (result != 0)
return result;
// 如果组名相同, 比较ID.
return ct1.Id.CompareTo(ct2.Id);
}
}
}
重点掌握实现的思路。