MOSS工作流 InfoPath+WorkFlow+Moss 开发要点

来源:互联网 发布:富士镜头推荐 知乎 编辑:程序博客网 时间:2024/05/01 23:45

转自:http://blog.chinaunix.net/uid-10951-id-1762452.html

 

项目基本完成了,一期的东西基本都做完了,采用的纯Moss下面的开发,也应该总结一些要点和东西,为了不使自己以后完全忘记,或者说:为了让广大困惑的人,能在此找到一些捷径和关键点,在此能有所用,也就欣慰不已。

 要点一:INFOPATHXML处理

          在对带有VBAINFOPATH编码当中,你会发现在支持浏览器的INFOPATH表单里面,能够使用的对象模型和处理十分有限,但是为了快捷,我们采用了它,因为利用现有的XML处理,加上页面当中的规则,基本上可以完成所有的业务逻辑。其实在简单的理解来说,InfoPath表单,其本质数据完全是XML,只是在展现的时候,用特定的控件来展示你XML的数据的,而控件基本都是采用绑定的形式和你的XML数据交互的,并且辅之以相当强大的规则和操作,来完成你的应用逻辑,所以处理方式比较特殊。所有规则能做的,代码都能做,但是目前在展示方面还有挺多的局限,后面会有所提到。

问题一:如何处理你的重复节?

重复节,也许就是我们在INFOPATH里面用的最多的东西,其十分类似于我们在Asp.NET里面的Reperate,可以用来存放重复数据字段,而其在INFOPATHXML数据的定义中,通常是三级层次结构的,如图,第一层是整个节点,第二层是每个子节点,每个子节点里面可以包含多个属性或者字段,用xpath来解析的话,就是类似于如此:

/my:HQContentType/my:Enclosuregroup

/my:HQContentType/my:Enclosuregroup/my:EnclosureNode

/my:HQContentType/my:Enclosuregroup/my:EnclosureNode/my:Enclosure

最前面的是我的表单的名字。而用来处理节的对象模型,在支持浏览器的InfoPath表单里面,我们用得最多的是:

XmlNamespaceManager XPathNavigatorXmlNamespaceManager是对整个XML的命名控件管理的对象模型,也可以理解为表单的命名空间,而XPathNavigator就是采用XPATH来对xml数据进行操作的对象模型。

读和取的办法如下代码:

 /// <summary>

        
/// 把节里的重复节里的DropDownlist的值连成字符串

        
/// </summary>

        
/// <param name="xpath_Group">节里的组</param>

        
/// <param name="strDll_Name">DropDownlist的名字</param>

        
/// <param name="splitchar">分割字符</param>

        
/// <returns>正却执行返回结果,异常返回空值</returns>

        
public string RepeatDllValuesTostring(string xpath_Group, string strDll_Name, char splitchar)

        {

            
string returnValue = string.Empty;

            
try

            {

                XmlNamespaceManager ns 
= this.NamespaceManager;

                XPathNavigator xpRoot 
= this.MainDataSource.CreateNavigator();

                XPathNavigator xpCompanyGroup 
= xpRoot.SelectSingleNode(xpath_Group, ns);

                XPathNodeIterator xpGroupIter 
= xpCompanyGroup.SelectChildren(XPathNodeType.Element);

                StringBuilder SBvalue 
= new StringBuilder();

                
while (xpGroupIter.MoveNext())

                {

                    XPathNavigator xpCurrent 
= xpGroupIter.Current;

                    
string strValue = xpCurrent.SelectSingleNode("my:" + strDll_Name, ns).Value;

                    
if (!(string.IsNullOrEmpty(strValue) || strValue.Trim().Length <= 0))

                    {

                        SBvalue.Append(strValue 
+ splitchar);

                    }

                }

 

                
if (SBvalue.Length > 0)

                {

                    returnValue 
= SBvalue.ToString();

                    returnValue 
= returnValue.Remove(returnValue.LastIndexOf(splitchar));

                }

            }

            
catch (SPException spex)

            {

                
//错误处理

                
return string.Empty;

            }

            
return returnValue;

        }


 

问题二:如何处理你的附件。

 

附件节的处理方式,比较多样,默认的如果附件节存储的地方是InfoPath本身的话,InfoPath有自己的一套编码转化方式,实际上还是将附件打成了流,存储到了相应的XML的节里面。试想,如果文件比较大,那么表单的载入将会十分的缓慢,这样做的效率比较低下。但是可以利用附件节做为媒介,运用文档库,把文件上传到Moss里面,在前台留一个简单的url,同样,在InfoPath表单载入的时候,也可以把文档库里面的文件读取到InfoPath的附件节里,提供用户下载查看。

这里提供两个类,一个用来编码,一个用来解码,下面是编码和解码的类,这里有需要注意的是,附件节对应的xml节中,如果读入了文件,要把nil属性给去掉,否则会报错。

XPathNavigator xpExcelFileNode = xpExcels.SelectSingleNode("my:AssistFileNode", ns);

                    xpExcels.AppendChild(xpExcelFileNode);

                    XPathNodeIterator xpExcelsIterator 
= xpExcels.SelectChildren(XPathNodeType.Element);

 

                    
while (xpExcelsIterator.MoveNext())

                    {

                        XPathNavigator xptempExcelFileNode 
= xpExcelsIterator.Current;

 

                        XPathNavigator xpExcel 
= xptempExcelFileNode.SelectSingleNode("my:AssistFile", ns);

 

                        
if (xpExcel.MoveToAttribute("nil""http://www.w3.org/2001/XMLSchema-instance"))

                        {

                            xpExcel.DeleteSelf();

                            InfoPathAttachmentEncoder encoder 
= new InfoPathAttachmentEncoder();

                            
string fileStream = encoder.GetTheBase64String(fo);

                            xpExcel.SetValue(fileStream);

                            
break;

                        }

                    }

public class InfoPathAttachmentEncoder

    {

        
private string base64EncodedFile = string.Empty;

        
private string fullyQualifiedFileName;

 

        
/// <summary>

        
/// Creates an encoder to create an InfoPath attachment string.

        
/// </summary>

        
/// <param name="fullyQualifiedFileName"></param>

        
public InfoPathAttachmentEncoder(string fullyQualifiedFileName)

        {

            
if (fullyQualifiedFileName == string.Empty)

                
throw new ArgumentException("Must specify file name""fullyQualifiedFileName");

 

            
if (!File.Exists(fullyQualifiedFileName))

                
throw new FileNotFoundException("File does not exist: " + fullyQualifiedFileName, fullyQualifiedFileName);

 

            
this.fullyQualifiedFileName = fullyQualifiedFileName;

        }

 

        
public InfoPathAttachmentEncoder()

        {

 

        }

 

        
public string GetTheBase64String(SPFile spFile)

        {

 

            Stream tempFileStream 
= spFile.OpenBinaryStream();

            
//BinaryReader br = new BinaryReader(tempFileStream);

 

            
// This memory stream will hold the InfoPath file attachment buffer before Base64 encoding.

            MemoryStream ms 
= new MemoryStream();

 

            
// Get the file information.

 

            
using (BinaryReader br = new BinaryReader(tempFileStream))

            {

                
string fileName = spFile.Name;

 

                
uint fileNameLength = (uint)fileName.Length + 1;

 

                
byte[] fileNameBytes = Encoding.Unicode.GetBytes(fileName);

 

                
using (BinaryWriter bw = new BinaryWriter(ms))

                {

                    
// Write the InfoPath attachment signature. 

                    bw.Write(
new byte[] { 0xC70x490x460x41 });

 

                    
// Write the default header information.

                    bw.Write((
uint)0x14);       // size

                    bw.Write((
uint)0x01);       // version

                    bw.Write((
uint)0x00);       // reserved

 

                    
// Write the file size.

                    bw.Write((
uint)br.BaseStream.Length);

 

                    
// Write the size of the file name.

                    bw.Write((
uint)fileNameLength);

 

                    
// Write the file name (Unicode encoded).

                    bw.Write(fileNameBytes);

 

                    
// Write the file name terminator. This is two nulls in Unicode.

                    bw.Write(
new byte[] { 00 });

 

                    
// Iterate through the file reading data and writing it to the outbuffer.

                    
byte[] data = new byte[64 * 1024];

                    
int bytesRead = 1;

 

                    
while (bytesRead > 0)

                    {

                        bytesRead 
= br.Read(data, 0, data.Length);

                        bw.Write(data, 
0, bytesRead);

                    }

                }

            }

 

 

            
// This memorystream will hold the Base64 encoded InfoPath attachment.

            MemoryStream msOut 
= new MemoryStream();

 

            
using (BinaryReader br = new BinaryReader(new MemoryStream(ms.ToArray())))

            {

                
// Create a Base64 transform to do the encoding.

                ToBase64Transform tf 
= new ToBase64Transform();

 

                
byte[] data = new byte[tf.InputBlockSize];

                
byte[] outData = new byte[tf.OutputBlockSize];

 

                
int bytesRead = 1;

 

                
while (bytesRead > 0)

                {

                    bytesRead 
= br.Read(data, 0, data.Length);

 

                    
if (bytesRead == data.Length)

                        tf.TransformBlock(data, 
0, bytesRead, outData, 0);

                    
else

                        outData 
= tf.TransformFinalBlock(data, 0, bytesRead);

 

                    msOut.Write(outData, 
0, outData.Length);

                }

            }

 

            msOut.Close();

 

            
return base64EncodedFile = Encoding.ASCII.GetString(msOut.ToArray());

            

 

        }

 

        
/// <summary>

        
/// Returns a Base64 encoded string.

        
/// </summary>

        
/// <returns>String</returns>

        
public string ToBase64String()

        {

            
if (base64EncodedFile != string.Empty)

                
return base64EncodedFile;

 

            
// This memory stream will hold the InfoPath file attachment buffer before Base64 encoding.

            MemoryStream ms 
= new MemoryStream();

 

            
// Get the file information.

            
using (BinaryReader br = new BinaryReader(File.Open(fullyQualifiedFileName, FileMode.Open, FileAccess.Read, FileShare.Read)))

            {

                
string fileName = Path.GetFileName(fullyQualifiedFileName);

 

                
uint fileNameLength = (uint)fileName.Length + 1;

 

                
byte[] fileNameBytes = Encoding.Unicode.GetBytes(fileName);

 

                
using (BinaryWriter bw = new BinaryWriter(ms))

                {

                    
// Write the InfoPath attachment signature. 

                    bw.Write(
new byte[] { 0xC70x490x460x41 });

 

                    
// Write the default header information.

                    bw.Write((
uint)0x14);       // size

                    bw.Write((
uint)0x01);       // version

                    bw.Write((
uint)0x00);       // reserved

 

                    
// Write the file size.

                    bw.Write((
uint)br.BaseStream.Length);

 

                    
// Write the size of the file name.

                    bw.Write((
uint)fileNameLength);

 

                    
// Write the file name (Unicode encoded).

                    bw.Write(fileNameBytes);

 

                    
// Write the file name terminator. This is two nulls in Unicode.

                    bw.Write(
new byte[] { 00 });

 

                    
// Iterate through the file reading data and writing it to the outbuffer.

                    
byte[] data = new byte[64 * 1024];

                    
int bytesRead = 1;

 

                    
while (bytesRead > 0)

                    {

                        bytesRead 
= br.Read(data, 0, data.Length);

                        bw.Write(data, 
0, bytesRead);

                    }

                }

            }

 

 

            
// This memorystream will hold the Base64 encoded InfoPath attachment.

            MemoryStream msOut 
= new MemoryStream();

 

            
using (BinaryReader br = new BinaryReader(new MemoryStream(ms.ToArray())))

            {

                
// Create a Base64 transform to do the encoding.

                ToBase64Transform tf 
= new ToBase64Transform();

 

                
byte[] data = new byte[tf.InputBlockSize];

                
byte[] outData = new byte[tf.OutputBlockSize];

 

                
int bytesRead = 1;

 

                
while (bytesRead > 0)

                {

                    bytesRead 
= br.Read(data, 0, data.Length);

 

                    
if (bytesRead == data.Length)

                        tf.TransformBlock(data, 
0, bytesRead, outData, 0);

                    
else

                        outData 
= tf.TransformFinalBlock(data, 0, bytesRead);

 

                    msOut.Write(outData, 
0, outData.Length);

                }

            }

 

            msOut.Close();

 

            
return base64EncodedFile = Encoding.ASCII.GetString(msOut.ToArray());

        }

    }

public class InfoPathAttachmentDecoder

    {

        
private const int SP1Header_Size = 20;

        
private const int FIXED_HEADER = 16;

 

        
private int fileSize;

        
private int attachmentNameLength;

        
private string attachmentName;

        
private byte[] decodedAttachment;

 

        
/// <summary>

        
/// Accepts the Base64 encoded string

        
/// that is the attachment.

        
/// </summary>

        
public InfoPathAttachmentDecoder(string theBase64EncodedString)

        {

            
byte[] theData = Convert.FromBase64String(theBase64EncodedString);

            
using (MemoryStream ms = new MemoryStream(theData))

            {

                BinaryReader theReader 
= new BinaryReader(ms);

                DecodeAttachment(theReader);

            }

        }

 

         

 

        
private void DecodeAttachment(BinaryReader theReader)

        {

            
//Position the reader to get the file size.

            
byte[] headerData = new byte[FIXED_HEADER];

            headerData 
= theReader.ReadBytes(headerData.Length);

 

            fileSize 
= (int)theReader.ReadUInt32();

            attachmentNameLength 
= (int)theReader.ReadUInt32() * 2;

 

            
byte[] fileNameBytes = theReader.ReadBytes(attachmentNameLength);

            
//InfoPath uses UTF8 encoding.

            Encoding enc 
= Encoding.Unicode;

            attachmentName 
= enc.GetString(fileNameBytes, 0, attachmentNameLength - 2);

            decodedAttachment 
= theReader.ReadBytes(fileSize);

        }

 

        
public void SaveAttachment(string saveLocation)

        {

            
string fullFileName = saveLocation;

            
if (!fullFileName.EndsWith(Path.DirectorySeparatorChar.ToString()))

            {

                fullFileName 
+= Path.DirectorySeparatorChar;

            }

 

            fullFileName 
+= attachmentName;

 

            
if (File.Exists(fullFileName))

                File.Delete(fullFileName);

 

            FileStream fs 
= new FileStream(fullFileName, FileMode.CreateNew);

            BinaryWriter bw 
= new BinaryWriter(fs);

            bw.Write(decodedAttachment);

 

            bw.Close();

            fs.Close();

        }

 

        
public string Filename

        {

            
get { return attachmentName; }

        }

 

        
public byte[] DecodedAttachment

        {

            
get { return decodedAttachment; }

        }

    }

问题三:怎么处理表单的关闭和数据的提交?

InfoPath里面,怎么向宿主环境提交数据,怎么吧表单提交到宿主里面呢?实现的方法有三种,一种是直接利用InfoPath的条件和规则以及操作无需编码,另外一个是在后台代码里面进行填写,前者局限性比较大,虽然能够完成一定的功能,但是比较局限,而用后台代码的话就不存在这个问题,可以很轻松的控制自己的逻辑和业务。第三种采用两者结合的方式,在尽量少的编码情况下,做到尽量多的东西(懒人的终极选择)。在提交的时候,采用的对象模型为,FileSubmitConnection,给段样例如下,原来还研究了一下ADO数据源的读取和提交,发现在表单的InternalStartup()以后,对其数据源的东西就无法更改了,所以和数据库操作联动的话,采用的还是自定义的assembly来进行操作,简单实用。

 //获取 提交数据源 并设置提交的参数 提交到的文件夹 文件名

                    FileSubmitConnection fileSubmit 
= (FileSubmitConnection)this.DataConnections["FileSubmit"];

                    
if (CurrentWeb.Url.EndsWith("/"))

                    {

                        fileSubmit.FolderUrl 
= HQIssueDocLib.ParentWeb.Url + HQIssueDocLib.RootFolder.Url;

                    }

                    
else

                    {

                        fileSubmit.FolderUrl 
= HQIssueDocLib.ParentWeb.Url + "/" + HQIssueDocLib.RootFolder.Url;

                    }

 

                    
//如果表单不存在

                    
if (xpIsExist.Value.Equals("0"))

                    {

                        
//提交

                        
if (xpSubmitOrNot.Value.Equals("1"))

                        {

                            
if (xpRedtape.Value.Length <= 0)

                            {

                                xpErrorMessage.SetValue(formSaveSate.EmptyRedFile);

                                xpDispatchTitle.SetValue(
string.Empty);

                                
return;

                            }

                            
//设置 CanModify为"0"为不可修改,IsSubmit为"1"已提交,IsExist为"1"为已存在,并提交

                            xpCanModify.SetValue(
"0");

                            xpIsSubmit.SetValue(
"1");

                            xpIsExist.SetValue(
"1");

                            fileSubmit.Filename.SetStringValue(filename);

                            fileSubmit.Execute();

}

}

问题四:怎么来处理表单的多视图?

一个InfoPath表单,可以采用多个视图来进行展现,并且绑定相关xml数据源的控件,可以定义新的规则和新的展现方式,这个个人认为是InfoPath良好设计的最佳体现。充分分离了数据和视图,但是目前感觉在WEB这块的INFOPATH表单,展现局限性还是很大,虽然可以自定自己的类似于AciviteX的控件,但是十分麻烦。不过后期肯定会陆续完善,因为微软内部的新Moss测试版本已经到14了。你可以在载入的时候根据读入的xml数据,来选择你所需要展现的视图,回传的时候,同样也可以更新你的视图,具体例子代码如下,思路就是通过检查传入的变量,调用SetDefaultView方法,这两个都是包含在FormLoadLoadingEventArgs里面的,具体取传入的变量的方法是看InputParameters键值对集合里面的值,和queryString类似。

public bool SetCanModify(LoadingEventArgs loadingEventArgs)

        {

            
try

            {

                XmlNamespaceManager ns 
= this.NamespaceManager;

                XPathNavigator xpRoot 
= this.MainDataSource.CreateNavigator();

                
//表单的标题

                XPathNavigator xpDispatchTitle 
= xpRoot.SelectSingleNode("my:HQContentType/my:DispatchTitleNode/my:DispatchTitle", ns);

                
//值 "0" 没有提交,值 "1"提交

                XPathNavigator xpIsSubmit 
= xpRoot.SelectSingleNode("my:HQContentType/my:IsSubmit", ns);

                
//值 "0"不允许修改 ,"1"允许修改

                XPathNavigator xpCanModify 
= xpRoot.SelectSingleNode("my:HQContentType/my:CanModify", ns);

                
//值 "0"不允许关闭,"1"允许关闭

                XPathNavigator xpIsAllowClose 
= xpRoot.SelectSingleNode("my:HQContentType/my:StateGroup/my:IsAllowClose", ns);

                
//表单是否存在

                XPathNavigator xpIsExist 
= xpRoot.SelectSingleNode("my:HQContentType/my:StateGroup/my:IsExist", ns);

 

                
//包含openfrom,表示是从表单中的链接打开的

                
bool isOpenFromForm = loadingEventArgs.InputParameters.ContainsKey("openfrom");

                
//Task5是特殊情况,链接中的参数canmodify是标志允许修改

                
bool isSpecialCanModify = loadingEventArgs.InputParameters.ContainsKey("canmodify");

 

                
//对视图和关闭按钮进行设置

                
if (xpDispatchTitle != null && xpDispatchTitle.Value.Trim().Length > 0 && xpIsExist != null && xpIsExist.Value.Equals("1"))

                {

                    
using (SPWeb web = new SPSite(SPContext.Current.Site.ID).OpenWeb(SPContext.Current.Web.ID))

                    {

                        
//获得当前项目

                        SPList HQIssueDocLib 
= web.Lists["总部发文库"];

                        SPListItem currentItem 
= FindItemByFileName(HQIssueDocLib, xpDispatchTitle.Value);

 

                        
//取Field的显示名

                        
string strAuthor = string.Empty;

                        
string strFileOrigin = GetDisplayNameByStaticName(HQIssueDocLib, "FileOrigin");

                        
string strCanModify = GetDisplayNameByStaticName(HQIssueDocLib, "CanModify");

 

                        
//如果文件存在,且CanModify域值不为空

                        
if (xpIsExist != null && xpIsExist.Value.Equals("1"&& currentItem[strCanModify] != null)

                        {

                            
//依据不同情况取得文件的创建者的Field的显示名,下属呈文的时候Drafter是我们需要的创建者

                            
if (currentItem[strFileOrigin] != null && currentItem[strFileOrigin].ToString().Equals("下属呈文"))

                            {

                                strAuthor 
= GetDisplayNameByStaticName(HQIssueDocLib, "Drafter");

                            }

                            
else

                            {

                                strAuthor 
= GetDisplayNameByStaticName(HQIssueDocLib, "Author");

                            }

                            
//取得当前用户和文件创建者的相关对象

                            SPFieldUserValue userValue 
= new SPFieldUserValue(web, currentItem[strAuthor].ToString());

                            SPUser currentUser 
= SPContext.Current.Web.CurrentUser;

                            
string currentUserLogName = currentUser.LoginName;

 

                            
//通过项的CanModify域值设置不同的视图和参数

                            
string strVaue = currentItem[strCanModify].ToString();

                            
switch (strVaue)

                            {

                                
case "":

                                    
//允许修改,当前用户就是创建者,则采取默认视图

                                    
if (currentUserLogName.Equals(userValue.User.LoginName))

                                    {

                                        
//并且是从表单里打开,则不允许关闭表单

                                        
if (isOpenFromForm)

                                        {

                                            xpIsAllowClose.SetValue(
"0");

                                        }

                                        
else//从文档库打开,允许关闭表单

                                        {

                                            xpIsAllowClose.SetValue(
"1");

                                        }

                                        xpCanModify.SetValue(
"1");

                                        loadingEventArgs.SetDefaultView(
"DefaultView");

                                    }

                                    
else//允许修改,但当前用户不是创建者,设置只读视图,并且不允许关闭表单

                                    {

                                        
//if (isOpenFromForm)

                                        
//{

                                        
//    xpIsAllowClose.SetValue("0");

                                        
//}

                                        
//else

                                        
//{

                                        
//    xpIsAllowClose.SetValue("1");

                                        
//}

                                        xpIsAllowClose.SetValue(
"0");

                                        xpCanModify.SetValue(
"0");

                                        loadingEventArgs.SetDefaultView(
"ReadOnlyView");

                                    }

                                    
break;

                                
default:

                                    
//这个是特殊的标志为,强制进行修改,是从表单链接打开,不允许关闭

                                    
if (isSpecialCanModify)

                                    {

                                        xpCanModify.SetValue(
"1");

                                        xpIsAllowClose.SetValue(
"0");

                                        loadingEventArgs.SetDefaultView(
"DefaultView");

                                        
return true;

                                    }

 

                                    
if (isOpenFromForm)//不允许修改,从表单链接打开,不允许关闭

                                    {

                                        xpIsAllowClose.SetValue(
"0");

                                    }

                                    
else//不允许修改,从文档库链接打开,允许关闭

                                    {

                                        xpIsAllowClose.SetValue(
"1");

                                    }

                                    xpCanModify.SetValue(
"0");

                                    loadingEventArgs.SetDefaultView(
"ReadOnlyView");

                                    
break;

                            };

                        }

                    }

                }

 

            }

            
catch (SPException spex)

            {

                
return false;

            }

            
return true;

        }

原创粉丝点击