MOSS 2007构建于WSS 3.0(Windows SharePoint Services 3.0)对象的基础之上的,并且在这个基础之上,增加了诸如InfoPath Form Services、Excel Services、BDC(商业数据目录)、企业级搜索等一系列功能应用,并且还整合了微软的一个产品企业内容服务(Content Management Server 2002)。
在Windows 2003中,WSS 3.0作为一个免费组件来提供,安装后,可以进行应用的开发,只不过很多重要功能没有。MOSS 2007和WSS 3.0在一些底层的服务架构方面都是完全一致的,所以了解了WSS 3.0,就能很好地理解MOSS 2007。
通过图8-1相信读者可以理解MOSS 2007和WSS 3.0的关系。
图8-1 MOSS2007与WSS 3.0的关系
利用WSS 3.0对象,几乎无所不能,比如定制网站界面、管理网站设置、自定义界面、查询网站内容等。MOSS本身提供的功能,想要完成一些与业务相关的定制化功能还是有困难的。比如对列表、文档库的添加、删除、修改的同时还要进行其他业务逻辑的处理。
由于WSS 3.0的对象模型非常庞大,不能一一介绍。微软提供了两个SDK,里面有WSS所有对象的示例,推荐读者安装相关的SDK。
8.1 对象介绍
新建一个C#的Windows工程,命名为“WindowsWSS”。引用“Microsort SharePoint Services”对象。在代码中添加以下命名空间。
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Administration;
以下的代码段均在这个工程内进行测试。
8.1.1 SPWebApplication
当创建了一个Web应用程序后,就能够通过SPWebApplication来获取相关信息。Web应用程序包含网站集,是网站集的容器。
以下的代码可以返回当前系统下所有Web应用程序的集合。
请在窗体上添加一个按钮、一个listbox控件、一个文本控件(设置为多行),按照默认命名即可,代码如下所示:
private void button1_Click(object sender, EventArgs e)
{
SPWebApplicationCollection webs=SPWebService.ContentService.WebApplications;
foreach (SPWebApplication web in webs)
{
listBox1.Items.Add(web.DisplayName);
}
}
运行后会打印出当前所有网站应用程序,如SharePoint –9000。
8.1.2 SPSite和SPWeb
SPSite是Web应用程序下的网站集。SPSite的RootWeb代表网站集的顶级网站(首要网站)。
SPWeb代表网站集下面的每个站点和子站点。访问站点的任何内容都需要从SPWeb 对象开始。从上面两者的关系可以看出来,网站集SPSite是SPWeb的集合。
可以使用以下方式来获取网站:
Ø 先得到SPSite。
SPSite site=new SPSite(“http://yang:9000”);
Ø 使用AllWebs方法得到当前网站集下面的知识库子站点kb。
SPWeb web1=site.AllWebs["kb"];
MessageBox.Show(web1.Title);
Ø 使用OpenWeb方法得到当前网站集下面的知识库子站点kb。
SPWeb web2 = site.OpenWeb("kb");
MessageBox.Show(web2.Title);
Ø 注意的是OpenWeb方法中的参数是一个相对的地址。
当在SharePoint环境中开发程序的时候,可以使用Http当前上下文Context来取得当前URL所对应的站点。
Ø 利用SPControl控件的上下文。
SPWeb web = SPControl.GetContextWeb(Context);
Ø 利用WSS对象的上下文SPContent。
SPWeb web = SPContext.Current.Web;
顶级网站(首要网站)访问子网站的列表
以下代码段展示了顶级网站(首要网站)访问子网站kb下的KB列表。
string listname = "KB";
SPWeb web = SPControl.GetContextWeb(Context).Webs["kb"];
SPList treeList = web.Lists[listname];
子网站访问顶级网站(首要网站)的列表
以下代码段展示了在子网站访问根网站(首要网站)下的tree分类列表。
string sourceTreeList = "tree";
SPWeb rootweb = SPControl.GetContextSite(Context).RootWeb;
SPList treeList = rootweb.Lists[sourceTreeList];
8.1.3 列表对象
表8-1列表开发的一些对象模型如表8-1所示。
表8-1 列表开发的对象模型
对 象 | 说 明 |
SPList | 列表对象 |
续表
对 象 | 说 明 |
SPListCollection | 列表集合。SPWeb.Lists返回网站所有列表的集合 |
SPListItem | 列表的项 |
SPListItemCollection | 列表项的集合。SPList.Items返回列表的所有列表项 |
SPViewCollection | 视图集合对象 |
SPViewFieldCollection | 视图字段集合 |
SPView | 视图对象 |
SPDocumentLibrary | 特殊的列表-文档库对象 |
SPField | 列表字段 |
SPFieldCollection | 列表字段集合 |
其他更多的模型对象请参考SDK。
1.获取列表Case
SPWeb web = SPControl.GetContextWeb(Context);
SPList list = web.Lists["Case"];
2.添加列表
SPWeb web = SPControl.GetContextWeb(Context);
SPList list = web.Lists["Case"];
SPListItemCollection lists = list.Items;
SPListItem listItem = list.Items.Add();
listItem["标题"] = this.TextBoxTitel.Text;
listItem["详细内容"] = this.TextBoxContent.Text;
listItem["联系电话"] = this.TextBoxTel.Text;
listItem["电子邮件"] = this.TextBoxEmail.Text;
listItem["紧急程度"] = this.DropDownListPRI.SelectedItem.Text;
listItem["分类"] = getTyepName();
listItem.Update();
3.得到列表项
SPWeb web =SPControl.GetContextWeb(Context);
SPList list=web.Lists["case"];
//得到列表项的ID
Int ID=1;
SPListItem listItem = list.Items.GetItemById(ID);
if (listItem["分类"] != null)
{
this.Label1.Text = listItem["分类"].ToString();
}
4.列表循环输出
SPSite site = new SPSite("http://yang:9000");
SPWeb spweb = site.OpenWeb();
SPList list = spweb.Lists["case"];
SPListItemCollection listitems = list.Items;
foreach (SPListItem listitem in listitems)
{
this.textBox1.Text += listitem["标题"].ToString () + Environment.NewLine;
}
这段代码的运行结果将列表Case里的标题列内容全部输出。
5.删除列表
SPSite site = new SPSite("http://yang:9000");
SPWeb web = site.OpenWeb();
SPListCollection lists = web.Lists;
//删除Case列表
SPList list = lists["case"];
System.Guid listGuid = list.ID;
lists.Delete(listGuid);
6.操作视图
SPSite site = new SPSite("http://yang:9000");
SPWeb spweb = site.OpenWeb();
SPList list = spweb.Lists["case"];
//case列表下的所有视图
SPViewCollection listitems = list.Views;
MessageBox.Show(listitems.Count.ToString ());
//列出标题
foreach (SPView oViewSrc in listitems)
{
this.textBox1.Text += oViewSrc.Title + Environment.NewLine;
}
//得到单个视图的信息
Guid viewguid=list.Views["我的日常工作"].ID;
SPView sv=list.GetView(viewguid);
SPListItemCollection collItemsSrc= list.GetItems(sv);
SPViewFieldCollection collViewFields = sv.ViewFields;
foreach (SPListItem oItemSrc in collItemsSrc)
{
for (int intIndex = 0; intIndex < collViewFields.Count; intIndex++)
{
this.textBox1.Text +=collViewFields[intIndex].ToString () + " :: " + oItemSrc[collViewFields[intIndex]].ToString() + Environment.NewLine; ;
}
}
运行结果如图8-2所示。
图8-2 视图信息
7.列表到DataTable
对列表的循环访问,不仅代码多,而且容易出错,一般处理的办法是把一个列表转成DataTable来操作,相信读者对DataTable很熟悉,这样降低了代码的复杂度,简化了开发过程。
Ø 使用SPListItemCollection列表项的集合对象的GetDataTable方法
private DataTable GetData()
{
string sourceTreeList = "tree";
SPWeb rootweb = SPControl.GetContextSite(Context).RootWeb;
SPList treeList = rootweb.Lists[sourceTreeList];
SPListItemCollection Items = treeList.Items;
return Items.GetDataTable();
}
Ø 自定义实现列表对象转化为DataTable
Ø 定义一个字段列表
string[] pSourceSPListFields ={ "ClassID", "ClassName", "ParentID" };
Ø 构造内容
private DataTable _sourceDataTable;
private DataTable ListToDataTable()
{
//构造表头
this._sourceDataTable = this.ColumnNameDataTable();
SPWeb web = SPControl.GetContextWeb(Context);
SPList treeList = web.Lists[sourceTreeList];
DataTable dt = new DataTable();
SPListItemCollection caseItems = treeList.Items;
foreach (SPListItem caseItem in caseItems)
{
try
{
DataRow rowNew = _sourceDataTable.NewRow();
for (int intI = 0; intI < this.pSourceSPListFields.Length; intI++)
{
if (caseItem[pSourceSPListFields[intI]] != null)
{
if (treeList.Fields[this.pSourceSPListFields[intI]]. FieldValueType.ToString() == "Microsoft.SharePoint.SPFieldUserValue")
{
string createrUserName = caseItem[this.pSourceSPList Fields[intI]].ToString();
int index = createrUserName.IndexOf(';');
int createrUserID = Convert.ToInt32(createrUserName. Substring(0, index));
SPUser createrUser = web.AllUsers.GetByID(creater UserID);
rowNew[pSourceSPListFields[intI]] = createrUser.Name;
}
else
{
Object columnType = _sourceDataTable.Columns [this.pSourceSPListFields[intI]].DataType;
rowNew[pSourceSPListFields[intI]] = caseItem[pSource SPListFields[intI]];
}
}
}
_sourceDataTable.Rows.Add(rowNew);
}
catch
{
}
}
return this._sourceDataTable;
}
Ø 构造表头
public DataTable ColumnNameDataTable()
{
SPWeb web = SPControl.GetContextWeb(Context);
SPList treeList = web.Lists[sourceTreeList];
DataTable dt = new DataTable();
SPListItemCollection caseItems = treeList.Items;
for (int intI = 0; intI < pSourceSPListFields.Length; intI++)
{
DataColumn columnNew = new DataColumn();
columnNew.ColumnName = pSourceSPListFields[intI];
dt.Columns.Add(columnNew);
}
return dt;
}
Ø 调用过程
private DataTable GetData()
{
Return ListToDataTable();
}
在案例中,这两种方式使用得都非常频繁,为了减少代码量,后面章节代码中只会写方法名而忽略具体过程实现。
8.1.4 操作文档库
文档库具有列表的所有属性,但是作为文档库又具有文件格式的所有属性。列表中每个项目可以使用SPListItem对象来操作。文档库中的项目则可以使用SPFile对象来操作。
通过以下代码来学习文件对象。
SPSite site = new SPSite("http://yang:9000/kb");
SPWeb spweb = site.OpenWeb();
SPFolder myFolder = spweb.GetFolder("kb");
SPFileCollection myFiles = myFolder.Files;
FileStream fStream = File.OpenRead(@"c:\aaa.txt");
byte[] contents = new byte[fStream.Length];
fStream.Read(contents, 0, (int)fStream.Length);
fStream.Close();
string path = site.Url + "/kb/KB/";
string filename=DateTime.Now.ToString().Replace(" ","").Replace(":","")+".txt";
SPFile file = myFiles.Add(path+filename,contents);
file.Item["知识库标题"] = "测试知识库";
file.Item["内容"] = "计算机的管理是一个非常复杂的工作";
file.Item["分类"] = "4";
file.Item.Update();
使用SPFolder myFolder = spweb.GetFolder("kb")代码段来得到文档库KB的文件夹。
之后使用SPFileCollection myFiles = myFolder.Files来得到文件集合。然后使用myFiles.Add对象来添加一个文本文件,这里可以为任意文件。最后通过file对象的item来赋值并完成更新操作。
最终界面如图8-3所示。
图8-3 添加记录后的文档库
当然,还可以用操作列表的方法来操作文档库。通过file对象能够获取文件的版本、大小等信息。在此不再阐述,请参考WSS SDK。
8.1.5 操作用户和用户组
表8-2用户和用户组的对象模型如表8-2所示。
表8-2 用户和用户组的对象模型
对 象 | 说 明 |
SPUser | 用户对象、域用户或者表单验证用户 |
SPUserCollection | 用户对象的集合 |
SPGroup | 用户组 |
SPGroupCollection | 用户组的集合 |
1.得到站点下所有的用户
SPSite site = new SPSite("http://yang:9000");
SPWeb web= site.OpenWeb();
foreach (SPUser user in web.SiteUsers)
{
this.textBox1.Text+=user.Name+Environment.NewLine;
}
输入结果:
CaoJing
DuanFuFeng
GanShiQiang
NT AUTHORITY\authenticated users
NT AUTHORITY\local service
YangYongGang
ZhongDeRong
系统账户
2.得到站点下所有的用户组
SPSite site = new SPSite("http://yang:9000");
SPWeb web = site.OpenWeb();
foreach (SPGroup group in web.Groups)
{
this.textBox1.Text += group.Name +Environment.NewLine;
}
输出结果:
helpdesk
manager
support
test成员
test访问者
test所有者
层次结构管理者
查看者
快速部署用户
设计者
审批者
受限制读者
样式资源读者
8.1.6 查询
通过SPQuery类来完成对列表的查询操作。
使用SPQuery需要设置如表8-3所示的几个属性。
表8-3 SPQuery类的属性说明
属 性 | 说 明 |
ViewFields | 查询结果中要显示的字段。这个属性的设置如:<FieldRef Name=”Title” /> |
Query | 查询条件包括分组与排序条件 |
RowLimit | 返回结果的行数 |
其中查询条件的设置需要满足CAML(协作应用程序标记语言)的语法标准。关于CAML具体的语法在这里不作具体介绍。这里仅介绍CAML语言的操作符,如表8-4所示。
表8-4 CAML语言的操作符说明
操 作 符 | 说 明 |
Geq | >= |
EQ | = |
Gt | > |
Lt | < |
Neq | != |
Contains | 包含 |
使用CAML查询有一点需要清楚,在CAML语法中使用的字段名并不是创建的字段名,而是MOSS系统内部的名称InternalName,先看以下代码。
SPSite site = new SPSite("http://yang:9000");
SPWeb spweb = site.OpenWeb();
SPList list = spweb.Lists["case"];
SPListItemCollection myItems = list.Items;
SPListItem item = myItems[0];
for (int i = 0; i < item.Fields.Count; i++)
{
this.textBox1.Text += item.Fields[i].Title + ":" + item.Fields[i].InternalName + Environment.NewLine;
}
运行后结果如下所示:
内容类型 ID:ContentTypeId
标题:Title
审批者注释:_ModerationComments
文件类型:File_x0020_Type
维护人员:_x7ef4__x62a4__x4eba__x5458_
故障大类:_x6545__x969c__x5927__x7c7b_
故障小类:_x6545__x969c__x5c0f__x7c7b_
状态:_x72b6__x6001_
用户处室名称:_x7528__x6237__x5904__x5ba4__x54
用户姓名:_x7528__x6237__x59d3__x540d_
用户电话:_x7528__x6237__x7535__x8bdd_
电子邮件:_x7535__x5b50__x90ae__x4ef6_
故障情况说明:_x6545__x969c__x60c5__x51b5__x8b
派工时间:_x6d3e__x5de5__x65f6__x95f4_
紧急程度:_x7d27__x6025__x7a0b__x5ea6_
实际故障情况说明:_x5b9e__x9645__x6545__x969c__x60
解决方案:_x89e3__x51b3__x65b9__x6848_
处理时长:_x5904__x7406__x65f6__x957f_
解决问题时间:_x89e3__x51b3__x95ee__x9898__x65
问题解决方式:_x95ee__x9898__x89e3__x51b3__x65
是否加入知识库:_x662f__x5426__x52a0__x5165__x77
已打分:_x5df2__x6253__x5206_
分类:_x5206__x7c7b_
rr:rr
case发送邮件:case_x53d1__x9001__x90ae__x4ef6_
正文:Body
到期日期:Expires
Meeting Room#:Meeting_x0020_Room_x0023_
内容类型:ContentType
ID:ID
修改时间:Modified
创建时间:Created
创建者:Author
修改者:Editor
通过这个运行结果,看到前面的正是MOSS中创建的字段,而冒号之后的则是系统内部处理的名称。如果把一个栏删除,再添加同样的栏,相应的InternalName会发生变化,需要重新获取。
接下来进行CAML的查询,从列表Case的标题中找到包含“计算机”的记录,并且显示5条。
在界面上增加了一个dataGridView表格控件。代码如下所示:
SPSite site = new SPSite("http://yang:9000");
SPWeb spweb = site.OpenWeb();
SPList list = spweb.Lists["case"];
SPQuery query = new SPQuery();
query.ViewFields = "<FieldRef Name='Title'/>
<FieldRef Name='_x95ee__x9898__x89e3__x51b3__x65'/><FieldRef Name='Created'/>";
query.Query = "<Where><Contains><FieldRef Name='Title'/><Value Type='Text'>计算机</Value></Contains></Where>";
query.RowLimit = 5;
SPListItemCollection myItems = list.GetItems(query);
this.dataGridView1.DataSource = myItems.GetDataTable();
运行结果如图8-4所示。
图8-4 绑定后的数据表格控件
注意这个表格显示了标题、故障情况说明和创建时间。其中_x6545__x969c__x60c5__ x51b5__x8b是故障情况说明字段的InternalName。如果把代码中<FieldRef Name='Title'/>改成<FieldRef Name='标题'/>,即
query.Query = "<Where><Contains><FieldRef Name='标题'/><Value Type='Text'>计算机</Value></Contains></Where>";
则系统会报告一个错误。错误如下:
一个或多个域类型未正确安装。请转到列表设置页删除这些域。