SharePoint2010内容类型剖析(三)
查找内容类型
- 查找哪些内容类型在当前网站上下文可用——使用SPWeb(服务端)或Web(客户端)对象的AvailableContentTypes属性。返回包含了SPContentType (服务端)或ContentType (客户端)的一个只读集合。可供枚举,也可以传递内容类型名称索引到指定内容类型。
之所以只读是因为该集合不仅包含当前网站内定义的内容类型,还包含父或祖先网站内的内容类型。 - 查找已经附加到某个列表或文档库上的内容类型——通过SPList (服务端)或List (客户端)对象的ContentTypes属性。返回一个可枚举、可索引的可改写的集合。
要注意,当网站内容类型附加到列表或文档库时,并不会真的添加该内容类型,而是会添加该内容类型的一个副本。该副本是一个列表内容类型。它的ID号基于相应的网站内容类型的ID而生成,但是是不相同的。这意味着我们可能无法通过父内容类型ID从集合中直接得到相应内容类型。应该学会使用集合的BestMatch方法。 - 如果希望知道某个网站内容类型在哪里被使用了,不管是被附加到一个列表还是被作为其他内容类型的父内容类型,我们可以调用静态方法GetUsages(SPContentType),它是SPContentTypeUsage类的成员
新建内容类型
- 可作为Feature部署的一部分,来新建一个自定义内容类型。创建自定义内容类型的代码写在FeatureActivate里。
如果内容类型中需要使用新的网站栏,要先处理网站栏的创建。 - 内容类型的显示名称不能包含特定的一些字符,可以调用静态方法ValidateName对将要使用的现实名称进行验证。注意,该方法不会验证重名。
- 通过SPContentType类构造函数创建一个内容类型对象。必须的一个参数是SPContentTypeCollection对象。该参数代表了将要在其上创建内容类型的SPWeb所对应的ContentTypes。虽然如此,并不表示构造函数会自动添加新建的内容类型。我们仍旧需要单独调用同一集合的Add方法才行。
- 如果在网站上需要添加一个新的内容类型或删除一个已有的内容类型,我们需要访问站点对象的ContentTypes属性,返回一个允许修改的集合。然后就可以使用Add或Delete方法了。
如要进一步设置内容类型的属性,可以将Add方法返回的SPContentType对象保存起来。通过SPFieldLink对象可以引用一个栏,然后调用内容类型的FieldLinks属性返回的SPFieldLinkCollection对象的Add方法来为内容类型添加栏。完成修改后记得调用Update()方法将设置写回数据库。 - 要在网站上创建或维护内容类型,我们必须具有网站设计者的访问权限。特别是需要在内容类型所宿主的网站上具有管理列表和添加并自定义页面的访问权限。
示例:以第一篇开头的财务文档的例子,通过编程实现创建。注释很详细,就不多做解释了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWeb web = null;
if (properties.Feature.Parent is SPSite)
{
SPSite sites = (SPSite)properties.Feature.Parent;
web = sites.RootWeb;
}
else
{
web = (SPWeb)properties.Feature.Parent;
}
if (web == null)
return;
/* 创建网站栏 */
string columnGroup = "Financial Columns";
// Amount
string amountFieldName = web.Fields.Add("Amount", SPFieldType.Currency, false);
SPFieldCurrency amountField = (SPFieldCurrency)web.Fields.GetFieldByInternalName(amountFieldName);
amountField.Group = columnGroup;
amountField.DisplayFormat = SPNumberFormatTypes.TwoDecimals;
amountField.MinimumValue = 0;
amountField.Update();
// Client Name
string clientFieldName = web.Fields.Add("Client Name", SPFieldType.Text, false);
SPFieldText clientField = (SPFieldText)web.Fields.GetFieldByInternalName(clientFieldName);
clientField.Group = columnGroup;
clientField.Update();
// Date Opened
string dateOpenedFieldName = web.Fields.Add("Date Opened", SPFieldType.DateTime, false);
SPFieldDateTime dateOpenedField = (SPFieldDateTime)web.Fields.GetFieldByInternalName(dateOpenedFieldName);
dateOpenedField.Group = columnGroup;
dateOpenedField.DisplayFormat = SPDateTimeFieldFormatType.DateOnly;
dateOpenedField.DefaultValue = "[today]";
dateOpenedField.Update();
// Cost Center Name
string costCenterFieldName = web.Fields.Add("Cost Center", SPFieldType.Choice, false);
SPFieldChoice costCenterField = (SPFieldChoice)web.Fields.GetFieldByInternalName(costCenterFieldName);
costCenterField.Choices.Add("Administration");
costCenterField.Choices.Add("Information Services");
costCenterField.Choices.Add("Facilities");
costCenterField.Choices.Add("Operations");
costCenterField.Choices.Add("Sales");
costCenterField.Choices.Add("Marketing");
costCenterField.Group = columnGroup;
costCenterField.Update();
/* 创建网站内容类型 */
string contentTypeGroup = "Financial Content Types";
// 获取文档内容类型,作为财务文档的父内容类型.
SPContentType documentCType = web.AvailableContentTypes[SPBuiltInContentTypeId.Document];
// 创建财务文档内容类型.
SPContentType financialDocumentCType = new SPContentType(documentCType, web.ContentTypes, "Financial Document");
// 注意: 内容类型在添加前不会被初始化.
financialDocumentCType = web.ContentTypes.Add(financialDocumentCType);
financialDocumentCType.Group = contentTypeGroup;
// 添加 Date Opened 栏. 子代将继承该栏.
SPFieldLink dateOpenedFieldRef = new SPFieldLink(dateOpenedField);
dateOpenedFieldRef.Required = true;
financialDocumentCType.FieldLinks.Add(dateOpenedFieldRef);
// 添加 Amount 栏. 子代将继承该栏.
SPFieldLink amountFieldRef = new SPFieldLink(amountField);
financialDocumentCType.FieldLinks.Add(amountFieldRef);
// 提交变更.
financialDocumentCType.Update();
// 创建发票内容类型.
SPContentType invoiceCType = new SPContentType(financialDocumentCType, web.ContentTypes, "Invoice");
invoiceCType = web.ContentTypes.Add(invoiceCType);
invoiceCType.Group = contentTypeGroup;
// 修改从父内容类型继承而来的Title栏.
SPFieldLink serviceFieldRef = invoiceCType.FieldLinks[SPBuiltInFieldId.Title];
serviceFieldRef.DisplayName = "Service";
serviceFieldRef.Required = true;
// 添加 Client 栏.
SPFieldLink clientFieldRef = new SPFieldLink(clientField);
clientFieldRef.Required = true;
invoiceCType.FieldLinks.Add(clientFieldRef);
// 指定一个文档模板.
invoiceCType.DocumentTemplate = "Invoice.docx";
// 提交变更.
invoiceCType.Update();
// 创建一个采购订单内容类型.
SPContentType purchaseOrderCType = new SPContentType(financialDocumentCType, web.ContentTypes, "Purchase Order");
purchaseOrderCType = web.ContentTypes.Add(purchaseOrderCType);
purchaseOrderCType.Group = contentTypeGroup;
// 修改从父内容类型继承而来的Title栏.
SPFieldLink itemFieldRef = purchaseOrderCType.FieldLinks[SPBuiltInFieldId.Title];
itemFieldRef.DisplayName = "Item";
itemFieldRef.Required = true;
// 添加 Department 栏.
SPFieldLink departmentFieldRef = new SPFieldLink(costCenterField);
departmentFieldRef.DisplayName = "Department";
departmentFieldRef.Required = true;
purchaseOrderCType.FieldLinks.Add(departmentFieldRef);
// 指定一个文档模板.
purchaseOrderCType.DocumentTemplate = "PurchaseOrder.docx";
// 提交变更.
purchaseOrderCType.Update();
}
{
SPWeb web = null;
if (properties.Feature.Parent is SPSite)
{
SPSite sites = (SPSite)properties.Feature.Parent;
web = sites.RootWeb;
}
else
{
web = (SPWeb)properties.Feature.Parent;
}
if (web == null)
return;
/* 创建网站栏 */
string columnGroup = "Financial Columns";
// Amount
string amountFieldName = web.Fields.Add("Amount", SPFieldType.Currency, false);
SPFieldCurrency amountField = (SPFieldCurrency)web.Fields.GetFieldByInternalName(amountFieldName);
amountField.Group = columnGroup;
amountField.DisplayFormat = SPNumberFormatTypes.TwoDecimals;
amountField.MinimumValue = 0;
amountField.Update();
// Client Name
string clientFieldName = web.Fields.Add("Client Name", SPFieldType.Text, false);
SPFieldText clientField = (SPFieldText)web.Fields.GetFieldByInternalName(clientFieldName);
clientField.Group = columnGroup;
clientField.Update();
// Date Opened
string dateOpenedFieldName = web.Fields.Add("Date Opened", SPFieldType.DateTime, false);
SPFieldDateTime dateOpenedField = (SPFieldDateTime)web.Fields.GetFieldByInternalName(dateOpenedFieldName);
dateOpenedField.Group = columnGroup;
dateOpenedField.DisplayFormat = SPDateTimeFieldFormatType.DateOnly;
dateOpenedField.DefaultValue = "[today]";
dateOpenedField.Update();
// Cost Center Name
string costCenterFieldName = web.Fields.Add("Cost Center", SPFieldType.Choice, false);
SPFieldChoice costCenterField = (SPFieldChoice)web.Fields.GetFieldByInternalName(costCenterFieldName);
costCenterField.Choices.Add("Administration");
costCenterField.Choices.Add("Information Services");
costCenterField.Choices.Add("Facilities");
costCenterField.Choices.Add("Operations");
costCenterField.Choices.Add("Sales");
costCenterField.Choices.Add("Marketing");
costCenterField.Group = columnGroup;
costCenterField.Update();
/* 创建网站内容类型 */
string contentTypeGroup = "Financial Content Types";
// 获取文档内容类型,作为财务文档的父内容类型.
SPContentType documentCType = web.AvailableContentTypes[SPBuiltInContentTypeId.Document];
// 创建财务文档内容类型.
SPContentType financialDocumentCType = new SPContentType(documentCType, web.ContentTypes, "Financial Document");
// 注意: 内容类型在添加前不会被初始化.
financialDocumentCType = web.ContentTypes.Add(financialDocumentCType);
financialDocumentCType.Group = contentTypeGroup;
// 添加 Date Opened 栏. 子代将继承该栏.
SPFieldLink dateOpenedFieldRef = new SPFieldLink(dateOpenedField);
dateOpenedFieldRef.Required = true;
financialDocumentCType.FieldLinks.Add(dateOpenedFieldRef);
// 添加 Amount 栏. 子代将继承该栏.
SPFieldLink amountFieldRef = new SPFieldLink(amountField);
financialDocumentCType.FieldLinks.Add(amountFieldRef);
// 提交变更.
financialDocumentCType.Update();
// 创建发票内容类型.
SPContentType invoiceCType = new SPContentType(financialDocumentCType, web.ContentTypes, "Invoice");
invoiceCType = web.ContentTypes.Add(invoiceCType);
invoiceCType.Group = contentTypeGroup;
// 修改从父内容类型继承而来的Title栏.
SPFieldLink serviceFieldRef = invoiceCType.FieldLinks[SPBuiltInFieldId.Title];
serviceFieldRef.DisplayName = "Service";
serviceFieldRef.Required = true;
// 添加 Client 栏.
SPFieldLink clientFieldRef = new SPFieldLink(clientField);
clientFieldRef.Required = true;
invoiceCType.FieldLinks.Add(clientFieldRef);
// 指定一个文档模板.
invoiceCType.DocumentTemplate = "Invoice.docx";
// 提交变更.
invoiceCType.Update();
// 创建一个采购订单内容类型.
SPContentType purchaseOrderCType = new SPContentType(financialDocumentCType, web.ContentTypes, "Purchase Order");
purchaseOrderCType = web.ContentTypes.Add(purchaseOrderCType);
purchaseOrderCType.Group = contentTypeGroup;
// 修改从父内容类型继承而来的Title栏.
SPFieldLink itemFieldRef = purchaseOrderCType.FieldLinks[SPBuiltInFieldId.Title];
itemFieldRef.DisplayName = "Item";
itemFieldRef.Required = true;
// 添加 Department 栏.
SPFieldLink departmentFieldRef = new SPFieldLink(costCenterField);
departmentFieldRef.DisplayName = "Department";
departmentFieldRef.Required = true;
purchaseOrderCType.FieldLinks.Add(departmentFieldRef);
// 指定一个文档模板.
purchaseOrderCType.DocumentTemplate = "PurchaseOrder.docx";
// 提交变更.
purchaseOrderCType.Update();
}
注意:FeatureActived方法的properties参数是一个SPFeatureReceiverProperties对象。该对象的Feature属性返回一个SPFeature对象,它的Parent属性包含了一个装箱的SPWeb或SPSite对象。我们不应该销毁这些对象。但是,所有我们在代码中新建的SPWeb或SPSite对象都要记得销毁。
在网站中删除内容类型
如果要从内容类型定义所在网站集中删除该内容类型,首先要保证GetUsages方法要返回一个空表,也就是说,该内容类型不能被附加到任何列表,或者被作为其他内容类型的父内容类型。
需要注意的是,AvailableContentTypes属性返回的是只读的内容类型集合。无法从中删除对象。因为其中包含的不仅仅是本网站内的内容类型。
示例:删除一个网站内容类型
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using System.Collections.Generic;
using Microsoft.SharePoint;
namespace Test
{
class ConsoleCTDel
{
static void Main(string[] args)
{
using (SPSite siteCollection = new SPSite("http://sp2010u/sites/contoso"))
{
using (SPWeb webSite = siteCollection.OpenWeb())
{
// 获取准备删除的内容类型.
SPContentType obsolete = webSite.ContentTypes["客户"];
if (obsolete != null) // 得到.
{
IList<SPContentTypeUsage> usages = SPContentTypeUsage.GetUsages(obsolete);
if (usages.Count > 0) // 被使用.
{
Console.WriteLine("The content type is in use in the following locations:");
foreach (SPContentTypeUsage usage in usages)
Console.WriteLine(usage.Url);
}
else // 未被使用.
{
// 删除.
Console.WriteLine("Deleting content type {0}...", obsolete.Name);
webSite.ContentTypes.Delete(obsolete.Id);
}
}
else // 未找到.
{
Console.WriteLine("The content type does not exist in this site collection.");
}
}
}
Console.Write("\nPress ENTER to continue...");
Console.ReadLine();
}
}
}
using System.Collections.Generic;
using Microsoft.SharePoint;
namespace Test
{
class ConsoleCTDel
{
static void Main(string[] args)
{
using (SPSite siteCollection = new SPSite("http://sp2010u/sites/contoso"))
{
using (SPWeb webSite = siteCollection.OpenWeb())
{
// 获取准备删除的内容类型.
SPContentType obsolete = webSite.ContentTypes["客户"];
if (obsolete != null) // 得到.
{
IList<SPContentTypeUsage> usages = SPContentTypeUsage.GetUsages(obsolete);
if (usages.Count > 0) // 被使用.
{
Console.WriteLine("The content type is in use in the following locations:");
foreach (SPContentTypeUsage usage in usages)
Console.WriteLine(usage.Url);
}
else // 未被使用.
{
// 删除.
Console.WriteLine("Deleting content type {0}...", obsolete.Name);
webSite.ContentTypes.Delete(obsolete.Id);
}
}
else // 未找到.
{
Console.WriteLine("The content type does not exist in this site collection.");
}
}
}
Console.Write("\nPress ENTER to continue...");
Console.ReadLine();
}
}
}
在列表中添加和删除内容类型
- 为列表或文档库附加内容类型前,首先要使该列表支持内容类型。在UI 里,可以选择列表设置,然后选高级设置。在是否允许内容类型里选择是。也可以通过编程设置,获得SPList对象后,设置其ContentTypesEnabled属性为true。
- 对于已存在的列表,我们可以添加到列表内容类型集合里的内容类型必须是一个已存在的网站内容类型。我们可以通过UI中列表设置页的可用内容类型清单来为其添加。编程实现的话可以先从SPWeb对象(服务端)或Web对象(客户端)得到AvailableContentTypes,从中选择一个内容类型。
- 添加的规则:最重要的3个内置的内容类型是项目,文档和文件夹。基于这3者派生的内容类型SharePoint会套用相应的某些规则。文档类的只能加到文档库,项目类的只能加到列表。文件夹类的是个例外。文件夹可以加到列表也可以加到文档库。可以通过调用列表对象的IsContentTypeAllowed方法来验证我们所选择的内容类型是否被接受。
- 调用SPList (服务端)或List (客户端)对象的ContentTypes的Add方法即可添加。
当通过对象模型将网站内容类型添加到列表后,SharePoint会自动添加所有内容类型中包含但列表中不存在的栏。这一点与通过列表定义的方式添加明显的不同。 - 删除文档库或列表的内容类型可以直接调用Delete方法,并传递一个标识要删除内容类型对象的SPContentTypeId (服务端)或ContentTypeId (客户端)。
我们无法删除使用中的内容类型。因此,在删之前首先需要遍历列表项,并查看其ContentType属性来确保没有列表项使用到该内容类型。SharePoint此时并不考虑已经被移到回收站中的列表项。如果这些项在内容类型删除后又被恢复回来,这将被指派到该列表的默认内容类型。
示例:下面的命令行应用程序将添加一个网站内容类型到共享文档列表。生产环境下,往往将类似的操作同样也应该放在SPFeatureReceiver对象的FeatureActived方法内。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using Microsoft.SharePoint;
namespace Test
{
class Program
{
static void Main(string[] args)
{
using (SPSite siteCollection = new SPSite("http://sp2010u/sites/contoso"))
{
using (SPWeb site = siteCollection.OpenWeb())
{
// 获取一个内容类型
SPContentType ct = site.AvailableContentTypes["Financial Document"];
if (ct != null) // 得到一个内容类型
{
try // 获取一个列表
{
SPList list = site.Lists["共享文档"]; // 如果没找到就抛出异常
// 确认列表允许内容类型
list.ContentTypesEnabled = true;
// 将内容类型添加到列表
if (!list.IsContentTypeAllowed(ct))
Console.WriteLine("The {0} content type is not allowed on the {1} list",ct.Name, list.Title);// 不符合原则
else if (list.ContentTypes[ct.Name] != null)
Console.WriteLine("The content type name {0} is already in use on the {1} list",ct.Name, list.Title);// 已存在
else
list.ContentTypes.Add(ct);
}
catch (ArgumentException ex) // 未找到列表
{
Console.WriteLine("The list does not exist.");
}
}
else // 未找到内容类型
{
Console.WriteLine("he content type is not available in this site.");
}
}
}
Console.Write("\nPress ENTER to continue...");
Console.ReadLine();
}
}
}
using Microsoft.SharePoint;
namespace Test
{
class Program
{
static void Main(string[] args)
{
using (SPSite siteCollection = new SPSite("http://sp2010u/sites/contoso"))
{
using (SPWeb site = siteCollection.OpenWeb())
{
// 获取一个内容类型
SPContentType ct = site.AvailableContentTypes["Financial Document"];
if (ct != null) // 得到一个内容类型
{
try // 获取一个列表
{
SPList list = site.Lists["共享文档"]; // 如果没找到就抛出异常
// 确认列表允许内容类型
list.ContentTypesEnabled = true;
// 将内容类型添加到列表
if (!list.IsContentTypeAllowed(ct))
Console.WriteLine("The {0} content type is not allowed on the {1} list",ct.Name, list.Title);// 不符合原则
else if (list.ContentTypes[ct.Name] != null)
Console.WriteLine("The content type name {0} is already in use on the {1} list",ct.Name, list.Title);// 已存在
else
list.ContentTypes.Add(ct);
}
catch (ArgumentException ex) // 未找到列表
{
Console.WriteLine("The list does not exist.");
}
}
else // 未找到内容类型
{
Console.WriteLine("he content type is not available in this site.");
}
}
}
Console.Write("\nPress ENTER to continue...");
Console.ReadLine();
}
}
}
添加网站内容类型到列表所造成的影响
- 本地副本中所有的栏作为栏引用(FieldRef)添加到列表栏。
- 副本作为网站内容类型的子代存在。Id不同。内容在初始时是相同的。
- 作为子代的列表内容类型可以随意定制,变更只在列表范围内。并不会更新到父内容类型。
- 父可以影响子。当然,也可以通过设置变更控制保护一个内容类型,使其避免被影响。
最佳的做法是尽量在网站层次上应用更改,尽量限制列表层次上的自定义。