MOSS中创建自定义内容类型

一:简要介绍

某类内容的一组可重用设置,使用内容类型可一致管理元数据,模板以及项目和文档的行为。内容类型在网站级别定义并用于列表和库。站点内容类型是可以独立于任何特定站点集合、站点、列表或文档库而建立的元数据定义。

MOSS系统提供的在 %CommonProgramFiles%\Microsoft Shared\Web Server Extensions\12\Template\Features\Ctypes 文件夹中定义,你可以查看该文件夹中的ctypeswss.xml文件包含所有内容类型的定义。

二:系统内容类型

<ContentType ID="0x"
Name="$Resources:System"
Group="_Hidden"
Sealed="TRUE"
Version="0">
<FieldRefs>
<FieldRef ID="{c042a256-787d-4a6f-8a8a-cf6ab767f12d}" Name="ContentType"/>
</FieldRefs>
</ContentType>

在ctypeswss.xml文件中的第一个内容类型为System,所有的内容类型最终都是从System内容类型继承而来的,从下图可以清晰的看出:

ct1

三:内容类型的ID

上图右边代表内容类型的ID,每一个内容类型都有一个ID的属性,这个ID是内容类型的唯一标识,每一个内容类型的ID都包括他父内容类型的ID,所以所有内容类型的前两位就是Sytem内容类型的ID,这样我们就可以通过解析内容类型的id来分析内容类型的继承体系。

内容类型的ID有两种命名的方式:

1. 父内容类型ID+两个十六进制数(不包括”00”)

2. 父内容类型id+“00”+十六进制的GUID

这里System内容类型是个特例,他的id是“0x”,其他的内容类型必须遵守上面两种约定的一种。第一种由于只包括两个十六进制数所以他强调的是id的简洁性,第二种包括GUID强调的是该内容类型的唯一性。每一种都有适合的情况。

父内容类型ID+两个十六进制数(不包括”00”)

Wss一般使用这种方式来生成默认的内容类型,比如基本的内容类型Item的id是0x01,Item是System的儿子,Document的id是0x0101,folder的id是0x0120,通过解析id你可以看出Document和folder都是Item的儿子,如下图:

clip_image004

ct2

父内容类型id+“00”+十六进制的GUID

Wss使用这种命名id的方式创建内容类型主要在以下两种情况

1. 站点内容类型(基于其他类型创建的)

2. 列表类型(将站点类型添加到列表中时,得到的站点类型实例的拷贝)

例如有一个内容类型的id是"0x010100D5C2F139516B419D801AC6C18942554D",通过这个id可以看出以下两点:

1. 是Document内容类型的直接基础而来的。

2. 当Document内容类型被填加到列表时一个列表内容类型被创建,。

当父类型是以下情况时,我们推荐你使用GUID的方式创建内容类型的id

1. 父类型为wss默认的内容类型,比如Document

2. 通过第三方工具开发的内容类型,这种你要确定第三方开发的内容类型和wss默认提供的没有冲突。

内容类型id的长度最大是512bytes即不能超过 1,024 个字符。由于有这个限制我们的id命名可以根据需要灵活的去控制,否则就会出现超出的情况。

四:创建自定义内容类型

1. 我们创建一个基于Event类型的自定义内容类型,项目结构如下图:

ct3

2. OnlineEvent.xml文件是内容类型的定义文件,每一个Field节点代表不同的网站栏,FieldRef节点代表网站栏在该内容类型中的引用。我们定义了四个网站栏分别为Status Field,Number Of Attendees,Event Type,Audience Type。具体代码如下:

<?xml version="1.0" encoding="utf-8"?>

<Elements Id="5f0c1259-87bc-4d63-bfc8-6f4997ec3f20" xmlns="http://schemas.microsoft.com/sharepoint/">
<ContentType ID="0x010200730061eb7d784bb786f4ac157702b8c7" Name="OnlineEvent" Group="Development" 
Description="Developing Content Type" Version="0"> <FieldRefs> <FieldRef ID="{5FCA124E-66FD-4e70-B3CE-D4B7C97D8A0E}" Name="StatusField"
DisplayName="Status Field" Required="TRUE" /> <FieldRef ID="{9451F129-78A1-47f0-8CA5-AB7F0D51902C}" Name="NumberOfAttendeesField"
DisplayName="Number Of Attendees" Required="FALSE" /> <FieldRef ID="{63DF93F8-B6E8-4c66-A1CB-66C1E677AF45}" Name="EventTypeField"
DisplayName="Event Type" Required="TRUE" /> <FieldRef ID="{2071EA0D-3F61-4de4-9A68-7C9C1047AC19}" Name="AudienceTypeField"
DisplayName="Audience Type" Required="TRUE" /> </FieldRefs> </ContentType> <Field ID="{5FCA124E-66FD-4e70-B3CE-D4B7C97D8A0E}" Type="Choice" Name="StatusField"
DisplayName="Status" StaticName="StatusField" Hidden="FALSE" Required="TRUE" Sealed="FALSE"> <CHOICES> <CHOICE>None</CHOICE> <CHOICE>Approved </CHOICE> <CHOICE>Rejected</CHOICE> </CHOICES> <Default>None</Default> </Field> <Field ID="{9451F129-78A1-47f0-8CA5-AB7F0D51902C}" Type="Number" Name="NumberOfAttendeesField"
DisplayName="Number Of Attendees" StaticName="NumberOfAttendeesField" Hidden="FALSE" Required="FALSE" Sealed="FALSE" /> <Field ID="{63DF93F8-B6E8-4c66-A1CB-66C1E677AF45}" Type="Choice" Name="EventTypeField"
DisplayName="Event Type" StaticName="EventTypeField" Hidden="FALSE" Required="TRUE" Sealed="FALSE"> <CHOICES> <CHOICE>Business/Career</CHOICE> <CHOICE>Classes &amp; Lectures</CHOICE> <CHOICE>Dinners/Galas</CHOICE> <CHOICE>Fundraiser</CHOICE> <CHOICE>Misc</CHOICE> <CHOICE>Seminars</CHOICE> <CHOICE>Support Groups</CHOICE> </CHOICES> </Field> <Field ID="{2071EA0D-3F61-4de4-9A68-7C9C1047AC19}" Type="MultiChoice" Name="AudienceTypeField"
DisplayName="Audience Type" StaticName="AudienceTypeField" Hidden="FALSE" Required="TRUE" Sealed="FALSE"> <CHOICES> <CHOICE>Adults</CHOICE> <CHOICE>Everyones' Invited</CHOICE> <CHOICE>Families</CHOICE> <CHOICE>Kids</CHOICE> <CHOICE>Men</CHOICE> <CHOICE>Seniors</CHOICE> <CHOICE>Singles</CHOICE> <CHOICE>Teens</CHOICE> <CHOICE>Women</CHOICE> </CHOICES> </Field> </Elements>

3.部署完成后我们去“网站设置—网站内容类型库”中即可以看到,下面是部分截图:

ct4

其中的标题,地点,开始时间等都是由Event继承而来的。

4.我们新创建一个日历的列表,然后去“设置—列表设置—高级设置”中设置为允许管理内容类型,如下图:

ct5

5.然后该日历列表的设置面板里就会出现内容类型的设置,如下图:

ct6

6.我们点击“从现有网站内容类型添加”来把我们刚才自定义的内容类型OnlineEvent添加进来,并删除原来的事件内容类型。

7.这时我们返回日历列表,点击新建就会使用我们自定义的内容类型来创建,如下图“

ct7

8.如果现在我们创建OnlineEvent内容类型的项目他会使用MOSS本身的样式,MOSS本身使用NewForm.aspx ,EditForm.aspx,DispForm.aspx。每一个页面都包含一个ListFormWebPart。接着我们就创建自定义的新建,编辑和显示页面,OnlineEventAdd.aspx,OnlineEventDisplay.aspx,OnlineEventEdit.aspx分别对应新建,显示和编辑页面,相应的.cs文件为他们后台的代码文件。

我们在新建,显示和编辑页面时他们产生的URL是有一定规律的,例

http://carysun/_layouts/ONLINEEVENT/OnlineEventAdd.aspx?List=22c18dec%2D33f7%2D42eb%2D9632%2D630384b99e52&RootFolder=%2FLists%2FCalendar&ContentTypeId=0x010200730061EB7D784BB786F4AC157702B8C70010F7F00BE575CB40AA1B113235A36982&Source=http%3A%2F%2Fcarysun%2FLists%2FCalendar%2Fcalendar%2Easpx

主要包括以下几部分:

List:列表的id

ID:这个条目的ID

Source: 返回的URL

我们使用SetValues方法来给相应的item赋值,注意最后一定要调用Update方法来更新。

protected bool SetValues()
{
     SPWeb web = SPContext.Current.Web;
     web.AllowUnsafeUpdates = true;
     SPListItem item = SPContext.Current.Item as SPListItem;

     if (item != null)
     {
        if (!Page.IsValid) return false;
        item[FLD_TITLE] = txtTitle.Text;
        item[FLD_LOCATION] = txtLocation.Text;
        item[FLD_STARTTIME] = spDTStartTime.Value;
        item[FLD_ENDTIME] = spDTEndTime.Value;
        item[FLD_NUMBER_OF_ATTENDEES] = nfAttendees.Value;
        item[FLD_EVENTTYPE] = ddEventType.Value;
        item[FLD_AUDIENCETYPE] = ccAudienceType.Value;
        item[FLD_DESCRIPTION] = rhTxtDescription.Value;
        item[FLD_STATUS] = ddStatus.Value;
        item.Update();
    }
    return true;
}

具体的代码请参考附件中的源代码。

9.我们要在OnlineEvent.xml中添加如下设置来指定使用我们自定义的页面:

<XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url">
<FormUrls xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url">
<Edit>_layouts/ONLINEEVENT/OnlineEventEdit.aspx</Edit>
<New>_layouts/ONLINEEVENT/OnlineEventAdd.aspx</New>
<Display>_layouts/ONLINEEVENT/OnlineEventDisplay.aspx</Display>
</FormUrls>
</XmlDocument>

10.填加EventHandler事件处理程序,在新建和更新的时候判断来保证该列表中的项目标题不能重复,我们需要重写ItemAdding和ItemUpdating,代码如下:

public override void ItemAdding(SPItemEventProperties properties)
{
     using (SPSite site = new SPSite(properties.WebUrl))
     {
         using (SPWeb web = site.OpenWeb())
         { 
             SPList list = (web.Lists[properties.ListId]);
             string theTitle = (string)properties.AfterProperties["Title"];
             if (findItemWithTitle(theTitle, -1, list))
             {
                 properties.ErrorMessage =
                               string.Format("A title of {0} is already in use", theTitle);
                 properties.Cancel = true;
             }
         }
     }
}

public override void ItemUpdating(SPItemEventProperties properties)
{
    SPListItem item = properties.ListItem;
    SPList list = item.ParentList;
    string theTitle = (string)properties.AfterProperties["Title"];
    if (findItemWithTitle(theTitle, item.ID, list))
    {
       properties.ErrorMessage =
                            string.Format("A title of {0} is already in use", theTitle);
       properties.Cancel = true;
    }

}

private bool findItemWithTitle(string title, int id, SPList list)
{
     string camlQuery = @"
              <Where> <And> <Eq> <FieldRef Name='LinkTitle' />
                      <Value Type='Text'>{0}</Value></Eq><Neq>
                      <FieldRef Name='ID' /><Value Type='Counter'>{1}
</Value></Neq></And> </Where>"; SPQuery query = new SPQuery(list.DefaultView); query.Query = string.Format(camlQuery, title, id); SPListItemCollection results = list.GetItems(query); return (results.Count > 0); }

重新编译后OnlineEvent.xml中会自动填加如下部分:

<XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/events">
<Receivers>
<Receiver>
<Name>ItemAdding</Name>
<Type>ItemAdding</Type>
<SequenceNumber>1</SequenceNumber>
<Class>5109fd1e-10f1-4f27-9448-b06dc140ec78</Class>
</Receiver>
<Receiver>
<Name>ItemUpdating</Name>
<Type>ItemUpdating</Type>
<SequenceNumber>1</SequenceNumber>
<Class>5109fd1e-10f1-4f27-9448-b06dc140ec78</Class>
</Receiver>
</Receivers>
</XmlDocument>

成功部署后我们自定义的页面如下图

ct8

如果我新建的项目的标题和现有的冲突就会有如下提示:

ct9

代码下载:CustomContentTypesDemo


本blog文章如无特殊说明,均属原创,转载请注明出处。
posted @ 2008-06-12 20:23 生鱼片 阅读(1507) 评论(19)  编辑 收藏 所属分类: Sharepoint

  回复  引用    
#1楼 2008-06-13 11:50 | MossFans [未注册用户]
不错,讲的很详细。兄弟,继续…………
  回复  引用    
#2楼 2008-06-13 23:27 | 郁闷中的程序员 [未注册用户]
学习了,让我对内容类型有了新的了解。我想问一下Sharepoint 的每个列表和网站栏都应该有一段用xml描述的定义,来规定列表或网站栏的属性,这段xml文件用Sharepoint的API可以查看。我想了解这些xml是根据网站定义和Feature动态生成还是存放在sharepoint的内容数据库中。
  回复  引用  查看    
#3楼 2008-06-14 17:53 | 王孟军!      
学习了,感觉你挺上进的
  回复  引用  查看    
#4楼 [楼主]2008-06-14 18:12 | 生鱼片      
@郁闷中的程序员
这个一两句也说不清,我会持续写这方面的文章,麻烦你稍等等。你可以看下SDK中的说明。
  回复  引用    
#5楼 2008-06-15 21:28 | 郁闷中的程序员 [未注册用户]
是这样的,我现在正在做sharepoint的一个项目,用户对速度要求比较高,所以我们现在采用直接查询sharepoint数据库的做法,由于sharepoint的字段是动态生成的,给我们的查询带来很大的不便,麻烦楼主提供一个方向或思路。
  回复  引用  查看    
#6楼 [楼主]2008-06-16 07:34 | 生鱼片      
@郁闷中的程序员
呵呵,优化这方面我也是初学,不过我觉得MOSS服务器的架构和里面的缓存以及其他的配置对优化都比较重要。相反编码上我觉得不是最重要的。
  回复  引用    
#7楼 2008-06-16 21:38 | 郁闷中的程序员 [未注册用户]
谢谢楼主的回复,我已经加了缓存,速度加快也很明显,可与用户的预期有一定差距,可能缓存配置文件没有仔细配置的原因吧。准备好好看一下缓存配置文件的设置,或者干脆让用户提高服务器的配置,呵呵。谢谢楼主的恢复,可能我解决问题思路不太对头。
  回复  引用  查看    
#8楼 2008-06-24 14:30 | 黎波      
good!
  回复  引用    
#9楼 2008-07-21 10:14 | sqycyl [未注册用户]
请教博主一个问题,我在安装Visual Studio 2008 extensions for Windows SharePoint Services3.0之后,在VS2008新建项目时,怎么看不到SharePoint的相关项目模板呢?

注:我是在SharePoint服务器上安装的,WF\OfficeServerSDK\WssSDK都已经安装.
  回复  引用    
#10楼 2008-07-21 10:30 | sqycyl [未注册用户]
博主你好,我在部署你上述示例时,VS2008提示"试图执行未经授权的操作.",急,感谢!
  回复  引用    
#11楼 2008-07-21 15:53 | qinsg [未注册用户]
非常详细,博主辛苦。
谢谢了。

  回复  引用  查看    
#12楼 [楼主]2008-07-24 13:01 | 生鱼片      
@sqycyl
第一个应该是你的VS2008是中文版的,你把原来的拷贝一份放到2052,1033这个你看看哪个文件夹没有就放哪里,具体我忘了,第二个应该是权限问题吧。
  回复  引用    
#13楼 2008-08-25 16:08 | zhenjjdog1 [未注册用户]
好文章,多谢!!
  回复  引用    
#14楼 2008-09-22 15:53 | bmy_light [未注册用户]
请问博主
我想不弹到那个出错页面
而是弹出一个错误提示框然后继续留在Online Event Edit Page怎么做?
谢谢
  回复  引用  查看    
#15楼 2008-09-22 16:35 | zhenjjdog      
我用Visual Studio 2008 extensions for Windows SharePoint Services3.0的自定义网站内容类型的方式定义了一个工作安排的内容类型,并同时以应用程序页的方式自定义了新增、修改、显示3个页面。

但不知如何实现增加附件的功能!我已如下方式无法实现,望指教:

<SharePoint:AttachmentsField ID="txtAttachments" FieldName="arrangAttachments" ControlMode="New" runat="server" Visible="true"></SharePoint:AttachmentsField>


我的实现过程参考了生鱼片的:

http://www.cnblogs.com/carysun/archive/2008/06/12/contenttypes.html

  回复  引用  查看    
#16楼 [楼主]2008-09-22 17:05 | 生鱼片      
@bmy_light
在ItemAdding和ItemUpdating等事件中自己程序判断,然后使用向客户端注册js。
  回复  引用  查看    
#17楼 2008-09-22 19:43 | zhenjjdog      
http://www.cnblogs.com/ocean/archive/2008/07/30/1256039.html

看看这个是不是你想要的功能。


这个地址我已看过! 不是我想要得. 默认列表(如:通知)在都有添加附件功能. 我也想在自定义的新增列表页中实现此功能.但我用的是Application Page,不知道如何实现.
这样说清了吗!!!!!

  回复  引用  查看    
#18楼 2008-09-22 19:45 | zhenjjdog      
<%@ Page Language="C#" MasterPageFile="~/_layouts/simplezhen.master" AutoEventWireup="true" Inherits="WorkArrangCType.WorkArrangAdd,WorkArrangCType, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9f4da00116c38ec5" %>
<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls"
Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%--<asp:Content ID="Content1" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
新增工作安排
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="PlaceHolderTitleBreadcrumb" runat="server">
&nbsp;
</asp:Content>
--%>

<asp:Content ID="Content3" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server">新增工作安排</asp:Content>
<%--<asp:Content ID="Content4" ContentPlaceHolderID="PlaceHolderSiteName" runat="server">新增工作安排</asp:Content>--%>
<asp:Content ID="Content5" ContentPlaceHolderID="PlaceHolderMain" runat="server">
<table border="1" >
<tr>
<td align="right">标题</td>
<td><SharePoint:TextField ID="txtTitle" FieldName="Title" ControlMode="New" runat="server" /></td>
</tr>

<tr>
<td align="right">主执行人</td>
<td><SharePoint:UserField ID="txtAssignedToMain" FieldName="arrangAssignedToMain" ControlMode="New" runat="server" /></td>
</tr>

<tr>
<td align="right">工作描述</td>
<td><SharePoint:NoteField ID="txtDescrible" FieldName="arrangDescrible" ControlMode="New" runat="server" /></td>
</tr>

<tr>
<td align="right">开始日期</td>
<td><SharePoint:DateTimeField ID="dtBeginDate" FieldName="arrangBeginDate" ControlMode="New" runat="server" /></td>
</tr>


<tr>
<td align="right">结束日期</td>
<td><SharePoint:DateTimeField ID="dtEndDateTwo" FieldName="arrangEndDateTwo" ControlMode="New" runat="server" /></td>
</tr>

<tr>
<td align="right">需要物资</td>
<td><SharePoint:NoteField ID="txtMaterial" FieldName="arrangMaterial" ControlMode="New" runat="server" /></td>
</tr>

<tr>
<td align="right">其他条件</td>
<td><SharePoint:NoteField ID="txtOther" FieldName="arrangOther" ControlMode="New" runat="server" /></td>
</tr>

<tr>
<td align="right">协助部门</td>
<td><SharePoint:NoteField ID="txtDepartmentTwo" FieldName="arrangDepartmentTwo" ControlMode="New" runat="server" /></td>
</tr>


<tr>
<td align="right">意见交流</td>
<td><SharePoint:NoteField ID="txtMessageTwo" FieldName="arrangMessageTwo" ControlMode="New" runat="server" /></td>
</tr>

<tr style="display:none">
<td align="right" style="display:none">工作汇报</td>
<td ><SharePoint:NoteField ID="txtReportWork" FieldName="arrangReportWork" ControlMode="Display" runat="server" /></td>
</tr>

<tr style="display:none">
<td align="right">主管评语</td>
<td ><SharePoint:NoteField ID="txtCommentOne" FieldName="arrangCommentOne" ControlMode="Display" runat="server" /></td>
</tr>

<tr style="display:none">
<td align="right">考核评语</td>
<td><SharePoint:NoteField ID="txtCommentTwo" FieldName="arrangCommentTwo" ControlMode="Display" runat="server" /></td>
</tr>

<tr style="display:none">
<td align="right">状态</td>
<td><SharePoint:TextField ID="txtState" FieldName="arrangState" ControlMode="Display" runat="server" /></td>
</tr>

<tr>
<td align="right">附件</td>
<SharePoint:AttachmentsField ID="txtAttachments" FieldName="arrangAttachments" ControlMode="New" runat="server" Visible="true"></SharePoint:AttachmentsField>
</tr>

<tr>
<td align="right">上传</td>
<SharePoint:AttachmentUpload Visible="true" ID="AttachmentUpload1" runat="server" />
</tr>

<%--
<tr>
<td colspan="2">
<asp:Button ID="btnUpdate" Text="保存" runat="server" OnClick="btnUpdate_Click" />
<asp:Button ID="btnCancel" CausesValidation="false" Text="取消" runat="server" OnClick="btnCancel_Click" />
</td>
</tr>--%>

</table>
</asp:Content>



这是代码!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  回复  引用    
#19楼 2008-09-25 10:57 | HIDY [未注册用户]
你好!我在做这个的时候出现这个错误:用于更新此列表项的数据无效,您试图更新的域可能只读。
不知道怎么回事,请指教

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-06-12 20:54 编辑过


相关链接: