SharePoint2010内容类型剖析(四)
更新内容类型
SharePoint允许对内容类型进行修改,即使已经将内容类型部署到网站、列表或已经创建了相应的列表项。
更新内容类型的2条标准途径
途径1:对内容类型进行修改,并推送更新。这条途径主要用在对使用中的内容类型进行有针对性的,离散的变更。比如,需要在网站内容类型上添加一列。
途径2:新建一个内容类型,以满足变更的需求。然后把原来的内容类型加到_Hidden组。这条途径主要用在需要彻底替换原有内容类型,但是还需要保留已有的列表数据的情况下。比如,我们有一个已经用了数年的产品规格内容类型。现在,不得不变更产品的规格,以便体现新的要求。但由于历史的原因,我们仍然要保留原有的产品规格内容类型,因为之前已经有无数的列表项是基于它来创建的。这时,我们可以新建一个内容类型叫产品规格2010。然后通过Feature打包分发,并在激活的代码中通过程序将新的内容类型添加到所有旧内容类型存在的列表中。然后将原来的产品规格内容类型的Hidden属性设为true。这样,当用户创建产品规格时,他们只能选择产品规格2010,原先的产品规格不在列出。但是,原先的列表项却保持不变。
SharePoint提供了一种机制,允许通过改变SPContentType或ContentType对象的ReadOnly 和Sealed属性来控制变更。因此,在修改内容类型前,应该首先检查这些属性设置。
将更新内容类型的代码放在SPFeatureReceiver对象的FeatureActivated(SPFeatureReceiverProperties)方法内。在完成对相应对象中内容类型的修改后,要记得调用该对象的Update方法将变更提交到数据库中。我们可以在Update方法中指定是否要更新子内容类型。
SharePoint并不会将内容类型的更新写回到内容类型定义文件,而是存在SharePoint数据库中。所有的列表内容类型也都是存放在SharePoint数据库中的。因此,任何时候都不要更改已经安装并激活使用的内容类型的定义文件。SharePoint不会跟踪这种变化。
使用对象模型更新内容类型
- 使用对象模型可以提供更细的推送粒度。
- 记得调用Update方法将变更写回数据库。
- 无法通过修改Feature XML文件中的网站内容类型定义来添加栏
{
SPList oList = oWebsite.GetList("MyWebSite/Lists/MyList");
SPFieldCollection collFields = oWebsite.Fields;
//新建一个网站栏
string strNewColumn = collFields.Add("出处", SPFieldType.Text, false);
//添加到网站内容类型“消息”中
SPFieldLink oFieldLink = new SPFieldLink(fields[strNewColumn]);
SPContentType oContentType = oList.ContentTypes["消息"];
oContentType.FieldLinks.Add(oFieldLink);
oContentType.Update(true);
}
内容类型中的自定义信息
- 按照设计初衷,内容类型是可扩展的。
- 每个内容类型都有一个XML集,用于存储第三方解决方案中用到的一些信息(XML节点所代表的XmlDocument元素)。通过添加XML节点,可以为内容类型加入自定义信息。通过在内容类型中包含自定义的XmlDocument元素,可以将我们解决方案中所需的自定义信息进行一个封装,使其成为内容类型的一部分。
可以通过两种方式为内容类型添加自定义信息:
- 对于已经部署好的内容类型,我们可以通过OM以编程的方式访问该集合中的Xml。只需要调用SPXmlDocumentCollection的Add方法即可。
- 对于未进行部署的内容类型,我们可以在内容类型架构定义XML中添加一个XmlDocument元素。
- 内容类型可以包含任何数目的XmlDocument元素。每个XmlDocument可以有自己的架构,只要是有效的XML即可。
- 网站内容类型中包含的XmlDocument内容会自动复制到任何子代。推送的粒度也可以基于Xml节点。但是,SharePoint在覆盖该xml节点前不会去判断该节点是否正在被使用或是否为某个处理过程所必需。我们也可以删除某个xml,并作为变更推送下去。
关联工作流
SPContentType.WorkflowAssociations 属性返回一个SPWorkflowAssociationCollection对象代表了内容类型所关联的所有工作流。我们可以通过Id属性或GetAssociationByName方法取到其中某个工作流。每个集合内的工作流的Name是唯一的。通过GetAssociationByName的返回值判断是否重名。
代码
{
contentType.WorkflowAssociations.Add(workflowAssociation);
}
else
{
contentType.WorkflowAssociations.Update(workflowAssociation);
}
实例:创建一个工作流关联,将其添加到某个网站内容类型的工作流集合,然后推送到子代内容类型
(注意:在新建项目的框架版本选择时要选择.Net Framework 3.5,还需要添加Microsoft.SharePoint.dll的引用)。下图是实例中要用到的工作流模板,首先要确保其已激活。
完成后查看工作流关联,可以看到下图:
以下是添加关联的代码:
using Microsoft.SharePoint;
using Microsoft.SharePoint.Workflow;
namespace TestWorkflowAssociation
{
class ConsoleAdd
{
static void Main(string[] args)
{
Console.WriteLine();
SPSite siteCollection = new SPSite("http://sp2010u/sites/contoso/Docs");
SPWeb site = siteCollection.OpenWeb();
SPContentType siteContentType = site.ContentTypes["费用报告"];
string taskListTitle = "任务";
string historyListTitle = "工作流历史记录";
string workflowName = "红-黄-绿";
// 获取一个模板.
SPWorkflowTemplate workflowTemplate = null;
foreach (SPWorkflowTemplate template in site.WorkflowTemplates)
{
workflowTemplate = template;
// 这里找一个自带的模板.
if (workflowTemplate.Name == "三态") break;
}
if (workflowTemplate != null)
{
// 创建一个关联.
SPWorkflowAssociation workflowAssociation = SPWorkflowAssociation.CreateWebContentTypeAssociation(workflowTemplate, workflowName, taskListTitle, historyListTitle);
// 添加一个关联到该内容类型,如果关联已存在则更新它.
Console.Write("工作流 {0} 已关联 ", workflowAssociation.Name);
if (siteContentType.WorkflowAssociations.GetAssociationByName(workflowAssociation.Name, site.Locale) == null)
{
siteContentType.WorkflowAssociations.Add(workflowAssociation);
Console.WriteLine("添加.");
}
else
{
siteContentType.WorkflowAssociations.Update(workflowAssociation);
Console.WriteLine("更新.");
}
// 将工作流关联更新推送到子代内容类型.
siteContentType.UpdateWorkflowAssociationsOnChildren(false, // 不需要生成完整变更清单
true, // 推送到继承的子代网站内容类型
true, // 推送到列表内容类型
false); // 如果遇到密封或只读内容类型不抛异常
}
site.Dispose();
siteCollection.Dispose();
Console.WriteLine();
Console.Write("任意键继续...");
Console.ReadLine();
}
}
}
以下代码删除刚才关联的工作流:
using Microsoft.SharePoint;
using Microsoft.SharePoint.Workflow;
namespace TestWorkflowAssociation
{
class ConsoleDel
{
static void Main(string[] args)
{
using (SPSite site = new SPSite("http://sp2010u/sites/contoso/Docs"))
{
using (SPWeb web = site.OpenWeb())
{
string ctName = "费用报告";
string wfName = "红-黄-绿";
SPContentType contentType = web.ContentTypes[ctName];
if (null != contentType)
{
SPWorkflowAssociation wfAssociation =
contentType.WorkflowAssociations.GetAssociationByName(wfName, web.Locale);
if (null != wfAssociation)
{
// 移除该工作流关联.
contentType.WorkflowAssociations.Remove(wfAssociation);
Console.WriteLine("与工作流 {0} 的关联已移除.", wfAssociation.Name);
}
else
{
Console.WriteLine("名为 {0} 的工作流关联未找到.", wfName);
}
}
else
{
Console.WriteLine("内容类型 {0} 不存在.", ctName);
}
// 将工作流关联更新推送到子代内容类型.
contentType.UpdateWorkflowAssociationsOnChildren(false, true, true, false);
}
}
Console.Write("\n任意键继续...");
Console.ReadLine();
}
}
}
内容类型XML架构定义
- 应用一:可以在Feature中通过内容类型的SchemaXml 新建一个内容类型定义。
- 应用二:可以通过List定义中的ContentTypes元素在创建列表时自动附加一个已有的内容类型
网站内容类型定义中引用的栏或内容类型可以包含在另一个Feature里。这种情况下,我们要注意创建相应的激活依赖性。
内容类型架构定义位于C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\XML\wss.xsd
可以打开ctypewss.xml(位于%ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\FEATURES\ctype)。其中包含了大多数SharePoint内置的内容类型定义。我们可以将其做为一个很好的学习内容类型架构的例子。