SharePoint Designer 2007 设计工作流三个问题

先看 Kaneboy 的《SharePoint Designer 2007, 强大的工作流设计器》。然后,我们针对利用 SPD 2007进行扩展、设计工作流三个常见问题做一些回答。由于开始步入大忙期,时间和精力有限,每个问题我都只点到为止,具体更细节就不累赘。

1、“从用户处收集数据”这个Action中输出的输出变量是什么,做什么用?或者换个问法,我怎么获取用户填写在收集表单里的数据?

A:输出的变量实际上是任务列表中的ItemID。“从用户处收集数据”这个Action是一个强迫工作流暂停以等待用户进行操作的过程。该Action实际是以任务形式在任务列表中新建一条任务向用户派发,然后用户上来编辑任务进行录入数据,以完成任务。这个输出变量ItemID就是用来定位获取用户填写的数据项的。在Conditions中放入“比较任意数据”,选择“任务”列表要查找用户输入值的哪个字段,然后下面把任务ID和输出变量关联即可。

2、初始变量Initiations和变量Variables有何区别?

A:初始变量赋值于工作流启动前,而Variables则赋值于工作流启动后。二者均在整个工作流生命周期内可用。初始变量工作流务必设置默认值,用于当新建就自动触发工作流的情景。无法通过查找获取到的数据,或者多次反复使用的数据都可以存入变量Variables中,以便在Workflow周期里方便使用。

3、自定义扩展Activity中,如何获取当前SPWeb、SPList、SPListItem、SPFile等?比如,最简单的,如何获取当前工作流运作的ListItem数据?

A:完全可以在自定义Activity中通过WorkflowContext上下文获取到当前项或当前SPWeb等信息。除非你要获取其他Site的数据,否则就没必要用去把Site Url、List Guid或ListItem ID等这些数据信息作为输入参数让工作流定制人员来手工输入以获取相关对象。

下面为一个代码模板(LiveWriter不支持代码色彩,就看黑白的代码吧):

public class MyActivity: Activity
{

#region Properties

public static DependencyProperty __ContextProperty = DependencyProperty.Register(“__Context”, typeof(WorkflowContext), typeof(MyActivity));
[ValidationOption(ValidationOption.Required)]
public WorkflowContext __Context
{
    get
    {
        return (WorkflowContext)base.GetValue(__ContextProperty);
    }
    set
    {
        base.SetValue(__ContextProperty, value);
    }
}

public static DependencyProperty ListItemProperty = DependencyProperty.Register(“ListItem”, typeof(int), typeof(MyActivity));
[ValidationOption(ValidationOption.Required)]
public int ListItem
{
    get
    {
        return (int)base.GetValue(ListItemProperty);
    }
    set
    {
        base.SetValue(ListItemProperty, value);
    }
}

public static DependencyProperty ListIdProperty = DependencyProperty.Register(“ListId”, typeof(string), typeof(MyActivity));
[ValidationOption(ValidationOption.Required)]
public string ListId
{
    get
    {
        return (string)base.GetValue(ListIdProperty);
    }
    set
    {
        base.SetValue(ListIdProperty, value);
    }
}

#endregion

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
    Guid listGuid = Helper.GetListGuid(__Context, ListId);
    SPList list = __Context.Web.Lists[listGuid];
    SPListItem item = __Context.GetListItem(list, ListItem);

    // 做自己的事 :)

    return ActivityExecutionStatus.Closed;
}

 其对应在 WSS.ACTIONS 中的节点配置如下:

<Action Name=”测试Activity”
ClassName=”TestActivities.MyActivity”
Assembly=”TestActivities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=dd27a7cb343a4cac”
AppliesTo=”list”
Category=”我的自定义操作”>
    <RuleDesigner Sentence=”输出 %1 到数据库”>
         <FieldBind Field=”ListId,ListItem” Text=”此列表” Id=”1″ DesignerType=”ChooseDoclibItem” />
    </RuleDesigner>
    <Parameters>
        <Parameter Name=”__Context” Type=”Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions” Direction=”In”/>
        <Parameter Name=”ListId” Type=”System.String, mscorlib” Direction=”In” />
        <Parameter Name=”ListItem” Type=”System.Int32, mscorlib” Direction=”In” />
    </Parameters>
</Action>

几点说明:

  • AppliesTo=”list” 表示应用到列表,还可以有 AppliesTo=”all” 表示应用到包括列表在内的所有sharepoint库表。
  • Category=”我的自定义操作”,就表示你在 SPD 2007中看到的 Actions 的分类。
  • Action 内属性还有 ListModeration=”true” 表示是否要显示未经审核的项,UsesCurrentItem=”true” 表示直接使用当前项。
  • DesignerType=”ChooseDoclibItem” 表示点“此列表”后弹出选择对话框(ChooseDoclibItem限文档库,列表可用ChooseListItem),以选择参数 ListId 和 ListItem。DesignerType表示SPD 2007支持的各种类型,如整数Integer、字符串String,电子邮件Email(含Email关键参数,类似Field=”To,CC,Subject,Body”),单个人员SinglePerson,多个人员Person,参数类型ParameterNames,待更新项类型UpdateListItem(类似Field=”ListId,ListItem,ItemProperties”),还有一个我们第一个问题用的“向用户收集数据”用到的类型Survey。SPD 2007设计器都支持这些类型的直接设计,因为也才体现出其强大之处(当然也有不足,如不支持InfoPath,不支持调试,不支持StateMachine等)。
  • Id=”1″ 表示对应 Sentence 中的 %1。比如上面效果,你在SPD 2007将看到显示为“输出 此列表 到数据库”,点此列表后,将弹出让你选择你要的项。
  • 下面Parameters节点里就比较好理解了,只要注意 Direction 有 In 和 Out 作为输入参数和输出参数即可。

 SPD 2007 设计工作流基本也就这些东西,主要要扩展的还是需要你用 VS2005 来自定义 Activity 配合,才能达到最大灵活性。

剩下的就是发挥想象力动手吧。

Posted in 未分类 | 3 Comments

MOSS Links & Workflow Foundation

Sahil Malik 最近一连发表了10篇关于 MOSS BDC 的文章,真是让人振奋:

  1. SharePoint 2007: BDC – The Business Data Catalog
  2. SharePoint 2007: BDC – The Hello World Example
  3. SharePoint 2007: BDC – Extending the Hello World Example
  4. SharePoint 2007: BDC – Extending the “Hello World” example even further – adding specific finders
  5. SharePoint 2007: BDC – Using BDC Data in Lists
  6. SharePoint 2007: BDC – Custom Actions on your Entities
  7. SharePoint 2007: BDC – Introducing associations between entities
  8. SharePoint 2007: BDC – Enabling Search on business data
  9. SharePoint 2007: BDC – User Profiles
  10. SharePoint 2007: BDC – Writing Custom Code against the runtime object model

如果涉及到表单中托管代码(VSTA or VSTO 2005 SE)的编写使用,《InfoPath 2007 Document: Developing InfoPath 2007 Managed-Code Solutions》这篇文档你不得不看。

MOSS Workflow中,这里有一篇文章《A WorkFlow that Uploads a Document via a Task using an InfoPath Form》介绍了如何在InfoPath Task Form中上传处理附件。

谈到Workflow,上次在《Workflow 问题与互联网三化》中提到一本Workflow Foundation的书籍《Essential Windows Workflow Foundation》(简称EWF),前些日子,看了ccboy写的一篇对该书的书评,实在很精彩。同时,他还推荐了另外四本相关书,其中一本〈Programming Windows Workflow Foundation〉我也翻了一遍,感觉确实这本书挺适合作为初学者入门的好书,通俗易懂且覆盖面广。其他两本没看过,不过ccboy推荐,应该必属精品。

对于《Programming Windows Workflow Foundation》一书,相对深入浅出的EWF一书来说,这书的叙述顺序倒是中规中矩很适合入门,从Hello Workflow示例到说明XAML Workflow和Code Workflow的关系,从Workflow Runtime Services、Workflow Instance生命周期到Workflow Hosting,从每个具体的Activity讲述到如何自定义Activity,从Sequential Workflow到StateMachine Workflow……一步一步引导你到Workflow Foundation的大门里。

但是窃以为,在看完这些书后,你只是学到了如何使用Workflow Foundation这门技术,或者说开发平台来进行工作流的开发而已。而现实中的工作流往往需要理解业务相关的知识,需要站在用户的角度或者业务人员的角度来分析工作流,这才能真正达到工作流的要求。这点也是 Workflow Foundation的一大优势——DSL!关于这点,强烈建议看看Architecture Journal的一本期刊,了解工作流许多原理和业务场景。在学习Workflow Foundation的同时,在使用每个Activity的同时,想想这些Actitivy可以应用到哪些具体业务场景达到什么样的功能。否则,空有一身蛮力却无法应用自如,也是很痛苦的一件事。

Posted in 未分类 | Tagged , | 4 Comments

对SPS 2003 URL进行访问授权控制

对于普通ASP.NET站点来说,要对该站点的URL进行访问授权控制,可以通过创建一个HttpModule来监控每个Request,如果Request Url为受控URL(即要访问该URL地址需要经过一种特定验证授权)时,则跳转到验证授权页面进行身份验证授权,完成后再返回即可正常访问Request Url。我想,这个实现并不难,网络上也可以找到诸多用HttpHandler和HttpModule来做这块处理的示例。

那么,SharePoint Portal Server 2003中,访问受控URL和普通ASP.NET站点有何不同吗?带着这个疑问,我们可以一开始也用HttpModule来做尝试。假设此时我们访问一个SharePoint 文档库的某个内容,其URL地址应该是 http://localhost/DocLib1/Test.doc,而在我们的受控URL数据库记录中发现 http://localhost/DocLib1 为受控URL,那么要访问 http://localhost/DocLib1/Test.doc,就不能让未经过验证授权的用户直接访问,而应该跳转到我们验证授权页面进行身份验证授权后方能访问。结果很让人遗憾,我们的访问畅通无阻。于是做了调试跟踪,发现在 HttpModule 中 Request.Url 不是我们想要的 http://localhost/DocLib1/Test.doc,而是一个对我们未知的 http://localhost/_vti_bin/owssvr.dll,正因为这个地址不是受控URL,所以HttpModule不做处理直接让用户继续访问了。

姑且不论owssvr.dll到底为何物,现在要解决的关键问题有两个:

  1. 为什么我们点的是 http://localhost/DocLib1/Test.doc 这个请求,而到HttpModule时,却变成了http://localhost/_vti_bin/owssvr.dll,谁干的好事?
  2. 我们能否在这家伙做这件事之前把执行权抢过来做我们自己的处理?

对于第一个问题:谁动了我的URL?在了解这个问题答案之前可以先参考以下文章:

看完上面两篇文章,或许你已经清晰知道是谁动了我们的URL。是stsfltr.dll(可以在IIS管理器–>Web站点属性窗口–>ISAPI 筛选器找到)这个ISAPI Filter在HttpModule之前抢先做了处理

第一个问题找到了,第二个问题:怎么解决这个问题,把执行权抢过来?只能自己再写个 ISAPI Filter,并把该 ISAPI Filter排在stsfltr.dll之前了。于是,我们创建了一个C++ Win32 项目,定义了下面这个一个ISAPI Filter class:

class CRedirectorFilter : public CHttpFilter

{

public:

     CRedirectorFilter();

     ~CRedirectorFilter();

 

     BOOL IsSecureDocument(LPCTSTR docUrl, LPCTSTR agent, LPCTSTR cookie);

     BOOL GetCookie(CHttpFilterContext* pCtxt,CString strName, CString & strValue);

     BOOL GetAgent(CHttpFilterContext* pCtxt,CString strName, CString & strValue);

 

// Overrides

     // ClassWizard generated virtual function overrides

         // NOTE – the ClassWizard will add and remove member functions here.

         //    DO NOT EDIT what you see in these blocks of generated code !

     //{{AFX_VIRTUAL(CRedirectorFilter)

     public:

     virtual BOOL GetFilterVersion(PHTTP_FILTER_VERSION pVer);

     virtual DWORD OnPreprocHeaders(CHttpFilterContext* pCtxt, PHTTP_FILTER_PREPROC_HEADERS pHeaderInfo);

     virtual DWORD OnEndOfNetSession(CHttpFilterContext* pCtxt);

     //}}AFX_VIRTUAL

 

     //{{AFX_MSG(CRedirectorFilter)

     //}}AFX_MSG

};

通过OnPreprocHeaders来处理判断我们的受控 URL 逻辑,如果是受控URL且尚未经过验证,则绕过stsfltr.dll直接跳转到验证授权页面进行身份验证授权;如果不是受控URL或已经经过验证,则直接交给IIS继续处理。

对于MOSS 2007,没有了stsfiltr.dll的困扰,实现类似方案就相对方便许多了,有兴趣者可以利用HttpHandler或HttpModule进行类似实现。

BTW:对于对受控URL的判断逻辑,如果感觉C++实现比较吃力费时,可以考虑用.NET Assembly来写这块逻辑,然后利用C++调用托管DLL来实现这块逻辑。具体可以参考KB:

Posted in 未分类 | Tagged | 10 Comments

在MOSS中直接嵌入ASP.NET Page

在MOSS Document Library中的Page,有Basic Page和WebPart Page两种,前者更多的体现WCM特性,后者则更侧重体现Portal特性。

不管是Basic Page还是WebPart Page,都是直接和MOSS本身结合非常密切,都直接采用Site中的MasterPage。如果我们想把一个普通的ASP.NET Page也加到MOSS站点里运行,比如也放在Document Library里,或者放在Site某个文件夹下面,然后通过MOSS站点URL直接访问运行该ASP.NET Page,就需要做一些工作了。这里就准备介绍这么一个Tip来完成这件事。我想,这种应用方式主要是针对一些需要特殊页面效果,或者追求短平快的小量自定义的场景的,也不失为各类复杂应用场景提供一个可以方便Work Around的通道。正常应用场景中一般比较少用到。

1、首先从最简单的Hello World Page开始。点击此处查看 HelloWorld.aspx 源代码

<%@ Page Language=”C#” %>

 

<html>

<head runat=”server”>

    <meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″>

    <title>Hello World Page</title>

 

    <script runat=”server”>

        protected void Page_Load(object sender, EventArgs e)

        {

            Response.Write(“Hello World!”);

        }

    </script>

 

</head>

<body>

    <form id=”form1″ runat=”server”>

    </form>

</body>

</html> 

2、将HelloWorld.aspx上传到Documents文档库中。此时,你直接点击文档库中的HelloWorld.aspx进行浏览,其URL类似http://localhost/Documents/HelloWorld.aspx,将出现“An error occurred during the processing of /Documents/helloworld.aspx. Code blocks are not allowed in this file.”的错误异常。通过这个异常信息也就知道,其实我们要做的就是允许在Page Server Code能被顺利编译执行

3、修改MOSS Site的web.config,比如C:\Inetpub\wwwroot\wss\VirtualDirectories\80\web.config。找到PageParserPaths这么一个节点,修改如下:

<PageParserPaths>
    <PageParserPath VirtualPath=”/Documents/helloworld.aspx” CompilationMode=”Always” AllowServerSideScript=”true” />
</PageParserPaths>

4、重新浏览http://localhost/Documents/helloworld.aspx,页面正常被编译执行。

至此已经验证普通的ASP.NET Page是可以顺利集成在MOSS中被执行的。可以看到CompilationMode=”Always”就意味着这个操作是属于“不得已而为之”的了,但这类不得已的场景现实应用中毕竟还是存在的。下面发散思维,扩展下思路。

5、我们把SharePoint的一些接口也写在这个普通ASP.NET Page里,从这个ASP.NET Page去操作SharePoint的库表等相关信息。点击此处查看 HelloSharePoint.aspx源代码

<%@ Page Language=”C#” %>

<%@ Import Namespace=”Microsoft.SharePoint” %>

<%@ Import Namespace=”Microsoft.SharePoint.WebControls” %>

 

<html>

<head runat=”server”>

    <meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″>

    <title>Hello SharePoint</title>

 

    <script runat=”server”>

        protected void Page_Load(object sender, EventArgs e)

        {

            Response.Write(“Hello SharePoint!<br/>”);

        }

 

        protected void Button1_Click(object sender, EventArgs e)

        {

            try

            {

                string url = http://localhost;

                string listName = “Documents”;

 

                SPWeb web = new SPSite(url).OpenWeb();

                SPListItemCollection listItems = web.Lists[listName].Items;

 

                foreach (SPListItem item in listItems)

                {

                    Response.Write(item["Name"].ToString() + “<br/>”);

                }

            }

            catch (Exception ex)

            {

                Response.Write(ex.Message);

            }

        }

    </script>

 

</head>

<body>

    <form id=”form1″ runat=”server”>

        <asp:Button runat=”server” Text=”GetItems” ID=”Button1″ OnClick=”Button1_Click” />

    </form>

</body>

</html>

6、将 HelloSharePoint.aspx上传到Documents文档库中。修改Web.config的PageParserPaths节点如下。表示Documents文档下所有页面都允许服务器端脚本并进行编译运行。

<PageParserPaths>
<PageParserPath VirtualPath=”/Documents/*” CompilationMode=”Always” AllowServerSideScript=”true” IncludeSubFolders=”false” />
</PageParserPaths>

 7、浏览http://localhost/Documents/HelloSharePoint.aspx,点击GetItems按钮,正常获取到Documents文档库下所有Item的Name列表。

至此说明在被嵌入的ASP.NET Page可以利用SharePoint API操作SharePoint各类库表数据。可以看到多了一个IncludeSubFolders=”false”的配置,如果为true则表示包括子文件夹在内所有页面每次都要编译运行。通过这个例子说明,我们甚至可以把这个普通的ASP.NET Page页做得和所在Site的页面风格一致,这样就达到瞒天过海了,在该ASP.NET Page内的操作感觉就如同在同一个MOSS Site中操作一般。

8、再延伸下思路。可以尝试把一些复杂业务逻辑操作封装在Assembly中,然后再在ASP.NET Page里调用即可。有兴趣者自己可以尝试这块操作。主要在ASP.NET Page中加入类似声明:

<%@ Register Tagprefix=”MyAssembly” Namespace=”ClassLibrary1″ Assembly=”ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=671d8e00903f3760″ %>

最后,我在说上述异常错误时说“我们要做的就是允许在Page Server Code能被顺利编译执行”,那么对于Basic Page,WebPart Page等MOSS自己的Page,是否也可以在内部增加Server Code,然后配置PageParserPath来运行这些Server Code呢?答案是肯定的。

希望你的项目设计中尽量少考虑这个特性,但如果必须用,希望本文对你有所帮助。

Posted in 未分类 | Tagged | 7 Comments

枚举Exchange Server、SotreGroups和MailStore

通过以下代码枚举列出所有的Exchange Server、StoreGroups和MailStore,并获取每个MailStore中Mailbox的数量。本段C#代码为http://www.ureader.com/message/513012.aspx一文中的VBNET代码改写而成,在Exchange 2003环境中测试通过。

通过这段代码,结合创建 Mailbox 的代码,可以实现获取Exchange环境的Server、StoreGroup、MailStore和Mailbox信息,或在指定Store(比如在所有Store中最少Mailbox的那个,或者人为指定目标Store)中创建Mailbox。

        protected void Page_Load(object sender, EventArgs e)

        {

            DirectoryEntry RootDSE = new DirectoryEntry(“LDAP://RootDSE”);

            string rootPath = “LDAP://” + RootDSE.Properties["configurationNamingContext"].Value.ToString();

            DirectoryEntry configContainer = new DirectoryEntry(rootPath);

 

            DirectorySearcher configSearcher = new DirectorySearcher(configContainer);

            configSearcher.SearchRoot = configContainer;

            configSearcher.Filter = “(objectCategory=msExchExchangeServer)”;

 

            // Enumerate all Exchange Servers

            SearchResultCollection serverResults = configSearcher.FindAll();

            foreach (SearchResult serverResult in serverResults)

            {

                Response.Write(“<br/><br/><font color=\”red\”>=== Exchange Server: “ + serverResult.GetDirectoryEntry().Properties["cn"].Value.ToString() + ” ===</font><br/><br/>”);

 

                SearchResultCollection storeGroups;

                SearchResultCollection stores;

                int mailboxCount;

 

                // Enumerate all Store Groups

                storeGroups = SearchContainer(serverResult.Properties["distinguishedName"][0].ToString(),

                    “(objectCategory=msExchStorageGroup)”);

                foreach (SearchResult storeGroup in storeGroups)

                {

                    string storeGroupName = storeGroup.GetDirectoryEntry().Properties["cn"].Value.ToString();

                    Response.Write(storeGroupName + “<br/>”);

 

                    mailboxCount = 0;

 

                    // Enumerate All Stores

                    stores = SearchContainer(storeGroup.Properties["distinguishedName"][0].ToString(),

                        “(objectCategory=msExchPrivateMDB)”);

                    foreach (SearchResult store in stores)

                    {

                        Response.Write(“&nbsp;&nbsp;” + store.GetDirectoryEntry().Properties["cn"].Value.ToString() + “<br/>”);

                        Response.Write(“&nbsp;&nbsp;&nbsp;&nbsp;Number of Mailboxes: “ +

                            store.GetDirectoryEntry().Properties["homeMDBBL"].Count.ToString() + “<br/>”);

 

                        mailboxCount += store.GetDirectoryEntry().Properties["homeMDBBL"].Count;

                    }

 

                    string reportMsg = String.Format(“Total Number of Mailboxes in Storage Group({0}): {1}<br/><br/>”, storeGroupName, mailboxCount);

                    Response.Write(reportMsg);

                }

            }

        }

 

        private SearchResultCollection SearchContainer(string srvPath, string strFilter)

        {

            string ldapPath = “LDAP://” + srvPath;

            DirectoryEntry serverContainer = new DirectoryEntry(ldapPath);

 

            DirectorySearcher serverSearcher = new DirectorySearcher(serverContainer);

            serverSearcher.Filter = strFilter;

            serverSearcher.SearchScope = SearchScope.Subtree;

 

            serverSearcher.PropertiesToLoad.Add(“cn”);

            serverSearcher.PropertiesToLoad.Add(“distinguishedName”);

            serverSearcher.PropertiesToLoad.Add(“homeMDBBL”);

 

            SearchResultCollection results = serverSearcher.FindAll();

 

            return results;

        }

关于Exchange的收藏夹几个必备站点:

Posted in 未分类 | Tagged | 9 Comments

MOSS 点滴

MOSS 搜索相关:

另外,对于MOSS自带的Logging功能应该引起开发人员重视。开发人员碰到问题时,除了可以DEBUG外,有时还需要配合察看MOSS内部运作时产生的一些日志才能发现具体问题。在 Central Administrations 的 Operations 里,可以看到如下:

打开 Diagnostic logging,可以看到有 Event Throttling 的一节。在这节里,可以选择 Category 类别,并设置该类别相应的级别到Event Log或Trace Log中。

大部分类别默认都在在 Error 级别进行记录,但有时你想跟踪分析整个过程数据,那可以改变日志级别。比如想分析 Excel Web Services 运行中的一些数据,或者想跟踪 Workflow Infrastructure 的数据,那就可以设置为 Information 级别和 Verbose 级别进行记录。这个操作会耗费资源,所以在分析完数据后最好还原成默认设置。

同个管理节中的 “Usage Analysis processing” 也是很有用的功能,可以用来统计分析整个站点的访问情况。配置见http://technet2.microsoft.com/Office/en-us/library/5233cf43-6a8f-40cb-9014-0724600e7e381033.mspx?mfr=true。大部分的统计分析用的呈现WebPart通过修改外观UI就可以满足绝大部分统计分析需求。如果想增加额外统计分析,那可以用Reflector分析参考Microsoft.SharePoint.Portal.dll里的Microsoft.SharePoint.Portal.Analytics、Microsoft.SharePoint.Portal.Analytics.Processing和Microsoft.SharePoint.Portal.Analytics.UI三个命名空间下的类。

Posted in 未分类 | Tagged | 1 Comment

Workflow 问题 & 互联网三化

    Workflow Foundation的问题确实很多,差点把我邮箱爆了,我无法解决所有问题,也无法一一回复。其实网上还有很多资料可以去查找,你可以到http://wf.netfx3.com/学习下载Sample,也可以下载最新的WIndows Vista SDK(内含.netfx3.0 SDK),看本《Essential Windows Workflow Foundation》,或者到 Architecture Journal 去下载06年7期“工作流主题”的杂志,最后有问题解决不了一定要到http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=122&SiteID=1这个Workflow官方论坛或者 SharePoint-Workflow http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=1207&SiteID=1 去问,会得到wf相关开发者的回答的。下面从那论坛摘几个比较常见的问题:

  1. 复杂的审批问题:比较简单的审批问题就是让一个人专门进行审批,如果他批准则通过,拒绝则结束。但是现实中都是复杂的流程,但再复杂的流程也都可以拆分成简单的流程,所以,当很多人都想知道Workflow Foundation是否可以满足他们的工作流场景时,我都会说99%都可以。呵呵,当然具体实施碰到的风险因素按具体项目具体分析了。言归正传,复杂的审批流程一般可以归纳成下面3类:(假设给5个人同时分配一项审批任务)
  2. State-Machine Workflow中的Parallel并行问题:
  3. Load Balanced负载均衡下的Workflow:
  4. Workflow 的版本问题:
  5. 从web.config中获取WorkflowRuntime实例:http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1200209&SiteID=1
  6. Tracking:http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1188418&SiteID=1

    97年在BBS上文豪挥笔,98年Mud边武侠风云,99年OICQ里风花雪月,看着html大普及,asp搅动动态网站……一晃10年过去了。盖茨把他的口号从“A PC on every desk and in every home”进化成“Empower people through great software any time, any place, and on any device”,似乎也预示网络时代的到来。如今,单就web应用这块来说,技术层面的flash、ajax、wpf/e曾出不穷,概念层面的blog, wiki, tag, podcast, videocast等日新月异,呵呵,看得眼花缭乱。不过,在这么一个发展的历程里,也总是能找到几条发展方向,我简称之为三化。

  • 用户体验个性化(体验):对于普通用户来说,他只care体验,包括UI和Function。
  • 技术体系标准化(标准):对于平台商来说,他们应该会更care标准,许多专利都出自并握在他们手里。
  • 应用领域普及化(随时随地任何设备上享受任何服务):对于应用服务供应商,他们应该care互连互通,这可是他们吸引最大群体用户,创造最大利润的根源。

    每个方向上都会因为各种利益而出现各种技术或理念,反过来各种技术和理念也基本都在这三个方向里进行验证决定其生死。

Posted in 未分类 | Tagged , | 3 Comments

InfoPath + Workflow + MOSS

6、ECM Starter Kits 虽然是 for Beta2TR,但完全可以正常跑在正式版里,当然前提是对里面的个别打不开或跑不起来的项目进行一点修改

  • CustomSignatures项目:把相关的 CorrelationToken 由 SignatureWorkflow 改为 SignaturesWorkflow。出现这种现象也不知道该说微软什么了,估计是赶时间推出这个 Sample。
  • ModificationSample项目:跑到 Workflow1.cs 代码里,找到 updateTask1 代码段,把相关 updateTask1.AssignedTo 等等都在中间加个  TaskProperties 属性,变成 updateTask1.TaskProperties.AssignedTo 这类的。
  • 其他如果还有问题,可以具体看,要么注释,要么做修改,不影响整体代码可读性,大部分也都在正式版里运行正常。

7、我有这么一个场景,我一进去编辑 Task Form 时,该 Task Form 前面列出这条记录的Title等基本字段值,然后下面有“同意”或“下一步”的按钮,还有“拒绝”或“取消”的按钮。我点“同意”或“下一步”不要立马提交,而是转到显示另外一个Form让我填同意后需要处理的一些表单数据。如果我按“拒绝”或“取消”也转到显示另一个Form让我填不同意时要录入的另外一些信息。

    这个应用只要你用过InfoPath的视图,就立刻知道怎么实施了。InfoPath的一个表单是允许多个视图的,所以你大可以把包含“同意”和“拒绝”的两个按钮的 Form 当作第一个默认视图,同时再增加一个同意后的视图和一个拒绝后的视图,然后制定这两个按钮的规则(Action里有switch view的这项),点同意就显示同意的视图,点不同意就显示不同意的视图,同时把同意或拒绝的状态值赋值给数据源中某个字段,这样就完全达到了上面的效果。

    扩展下思路:你可以用radiobutton排出一队的选项,因为 radiobutton 绑定着一个字段,所以可以判断当用户选定哪个项时来决定我要显示哪个视图。比如,我有“全部公开”、“部分公开”和“不予公开”三个项,我选“全部公开”然后按下一步,就可以跑到全部公开的视图里填写一份政府公文,输入xxxx年xx号和备注后,按提交就完成这个步骤了;以此类推。

    额外提下InfoPath,我们知道 InfoPath Form 不用编码本身就可以有一定规则流程的,这些动作包括常用的对输入数据的校验、根据某种条件进行某种动作、执行一些Action等。所以,你可以直接在表单里设置诸如必填字段、进行自动计算、动态增加节点数据、根据输入数据动态调整表单表现或设置数据源字段、根据一定规则执行一定操作等等,而大部分这些操作在发布到 MOSS 中进行 Web 显示时,还是可以正常使用的,使得数据收集这块变得异常方便快捷。

8、MOSS的Workflow历史记录在哪里?

    你可以通过右键菜单中的Workflow菜单进去查看该条记录的工作流情况,有工作流的已经运行过的步骤产生的Task链接,也有每步的动作列表。如果想看所有Workflow的历史列表,可以访问 http://服务器/Lists/Workflow%20History 查看,不过估计那些数据不是人能看懂的。但至少可以保证,工作流从头到尾所有的业务数据和流程数据都是存在的,都可以提供给你去查到你所要的历史痕迹的。

    如果你还熟悉Workflow Foundation的 TrackingService,你用了其Sample的 WorkflowMonitor 打算来跟踪查看 MOSS 的 Workflow 时,sorry,结果是无法运行,无法跟踪,无法查看。Why?没错,WorkflowMonitor可以利用Workflow Foundation的TrackingService跟踪监控Workflow,但是 MOSS 的 Workflow 却没有使用 Workflow Foundation 默认的 TrackingService(这个实在让人费解,不知出于什么目的),所以如果你要Monitor,那可以考虑为 MOSS 写一个 WPF Application 的 Workflow Monitor,相信这个 idea 应该还是不错的:-) 那MOSS怎么做历史跟踪,打开Reflector看看 Microsoft.SharePoint.WorkflowActions.dll里的相关类,也许就清楚了。

9、差不多了,这个 InfoPath + Workflow + MOSS 的主题就到此为止,这个主题仅仅是当作辅助参考。最后看了choral 的《如何配置Windows SharePoint Services 3.0的搜索》的文章的后面评论中,有人评论说 MOSS 不值得关注,而我则反过来想告诉大家一句话:请重视或正视MOSS!微软平台上的企业应用开发商/开发人员都必须重视这个产品,而微软的对手们也必须正视这个产品。也许答案在这张图里。

Posted in 未分类 | Tagged , | 4 Comments

InfoPath + Workflow + MOSS

5、接上贴补充下一些内容。

  1. 如何看InfoPath表单是否发布成功:打开SharePoint 3.0管理中心,在应用程序管理有 InfoPath Form Services 一节,进入“Manage Form Template”就可以看到你所有发布的表单。其中看状态 Status 栏,如果为 Ready,则表示该表单已经准备好可以使用了;如果为 Installing,那就是有可能因为前面罗列的各种原因造成表单模板还无法正常发布,这时候你就要检查了。另外,你可以通过URL访问已经发布的表单,地址为类似 http://服务器/_layouts/formserver.aspx?XsnLocation=urn:schemas-microsoft-com:office:infopath:ReviewTaskForm:-myXSD-2005-11-22T23-52-35,后面 XsnLocation=跟上表单的 ID(URN)即可。
  2. 前面提过,在MOSS中,每个Workflow都是作为一个Feature存在,然后具体使用时再关联到某个List/Library中然后创建Workflow实例运行的。在一个Workflow生命周期里,业务数据自然主要就是List/Library中的记录数据SPListItem/SPFile,再辅之任务列表数据;而Workflow过程中需要的一些流程状态数据则被WF提供的服务持久化(术语好像叫“钝化”之类的)起来,供Workflow被启用时再度激活,从而顺利继续工作流的运作。
  3. 关于 Task。一开始很多要问为什么(是/要) Task?因为在 MOSS 2007 的 Workflow 中,对于业务流程中人的部分,即工作流中人为参与的部分主要是通过“任务 Task”形式实现的。即要某人来审批或添加些数据时,就在Task列表中创建分配一个 Task 给他,然后这个人过来编辑 Task,完成人为数据的录入采集,使得工作流继续运行。
  4. 上贴有提到如果不小心用VSTA打开产生后部C#/VB.NET代码后,发布时变成了需要经过管理员审核才能使用。为什么会这样?这类表单和其他表单还有什么区别吗?答案就在《Office Forms Server 简介》。
  5. SharePoint Desginer 2007 的工作流设计不支持 InfoPath 表单形式吗?是的,很遗憾,SPD2007只支持 ASPX Form形式的 Task Form。所以如果要在 MOSS 2007 里开发 InfoPath + Workflow,还得用前面说过的方法。当然,前面的方法也支持 ASPX Form形式,详细可看 ECM Starter Kits中很经典的 ASPX Form 范例 —— CollectFeedback 示例。
  6. 为什么 Workflow.xml 中的 AssociationUrl=”_layouts/CstWrkflIP.aspx” 和 InstantiationUrl=”_layouts/IniWrkflIP.aspx” 不能换成其他页面?这些页面可以理解为 InfoPath Form 的 Host Page(实际是这些页面内部嵌入了显示InfoPath Form的WebPart),InfoPath Form Services处理 InfoPath Form后通过这些 xxxIP.aspx 的页面把 InfoPath Form表单以 Web Page形式呈现出来。所以,不能改变成其它页面,否则无法正常显示 InfoPath Form。
  7. 不太明白 ItemMetadata.xml 作用,或者说把SPListItem数据传给InfoPath Form并和InfoPath控件绑定显示出来的过程是怎样的?这个一开始确实比较费解。首先根据 SDK 操作一遍后,此时 ItemMetadata.xml 已经作为辅助数据源和资源文件保存在 InfoPath Form里了。记得,此时最好把 ItemMetadata.xml 理解为 InfoPath Form 的“数据源”!然后传递过程是这样的:
    • 把准备传递给 Task InfoPath Form 的 SPListItem 字段值取出来赋值为 SPWorkflowTaskProperties.ExtendedProperties["表单字段名"]。
    • 任务表单接受这些值,然后组织成一份 xml ,也就是你看到的 ItemMetadata.xml:<z:row xmlns:z=”#RowsetSchema” … />。
    • InfoPath Form接收这份 xml 数据,因为InfoPath Form中的控件已经绑定过相应的辅助数据源 ItemMetadata.xml 的 ows_字段名,所以可以顺利的从含有数据的 ItemMetadata.xml 数据源中抽取值显示在表单里。

6、先这样吧。总体感觉似乎人们最多问题的居然是 InfoPath!InfoPath、InfoPath还是InfoPath。其实InfoPath并不难,严格说起来个人觉得和Excel差不多(当然要玩深下去,那可深不见底,但常用功能都很简单的),可能是因为不常用原因吧,可惜了这个产品。给几个 InfoPath 站点给大伙看看:

Posted in 未分类 | Tagged , | 5 Comments

InfoPath + Workflow + MOSS

在MOSS 2007种利用InfoPath 2007结合Workflow Foundation可以高效的做出非常强大的工作流应用。因为在SDK中这部分的内容有点不流畅,读起来比较费劲。所以我想以我的一点小经验和大家Share下,希望能少走点弯路,然后再结合 SDK 、ECM Starter Kits 和 WSS Workflow StarterKits 快速掌握这个非常棒的功能。

1、我要创建用于 MOSS 的 Workflow 项目。

     请下载 WSS Workflow StarterKits,安装后将有个”SharePoint Server”的组,内有 SharePoint Sequential Workflow Library 和 SharePoint State Machine Workflow Library 两个项目模板。利用这两个模板创建的项目,对 Workflow 的开发、部署都很方便。

  • 项目用到的 Microsoft.Office.Workflow.Tasks.dll、Microsoft.SharePoint.dll 和 Microsoft.SharePoint.WorkflowActions.dll 均在装有MOSS 2007的机器的 C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\ISAPI\ 目录下。其中开发时主要注意记得在VS2005工具箱里注册 microsoft.sharepoint.WorkflowActions.dll,就可以从工具箱里拖拉出 MOSS 特有的 Workflow 控件了。
  • Feature.xml:在 MOSS 中, Workflow 是作为 MOSS 的 Feature 存在的,具体使用时再关联到具体某个 List 或 Library 中并创建实例运作的。因为 Feature.xml 文件就是安装部署时候要把生成的项目 dll 文件作为 Feature 注册到 MOSS 中的描述文件了。刚打开时是空的,只是一些提示,按提示插入 Feature 的 Snippets,就有一段 xml 代码插入。其中 GUID 可通过 VS2005 菜单 Tools 下的 Create GUID 创建一个新的 GUID 填入;中间有个 <ElementManifest Location=”workflow.xml” /> 节点用于关联工作流 xml 描述文件。
  • Workflow.xml:工作流描述文件。其中有工作流名字Name(该名字在添加 Workflow 关联具体列表或文档库时显示在工作流列表中);GUID(同样通过 Create GUID 生成填入);CodeBesideClass和CodeBesideAssembly对应工作流类全名和程序集全称(项目如进行签名,此处需明确PublickToken,同时把 install.bat 中 gacutil 部分反注释掉);TaskListContentTypeId为工作流使用到的任务列表的 ContentType ID,默认为 Workflow Tasks 的 ContentTypeID,一般不需要修改;AssociationUrl、InstantiationUrl和ModificationUrl一般也不用改,默认创建即是用于呈现 InfoPath 的页面,其中 AssociationUrl 为用来关联 Workflow 和列表或文档库,并设置工作流运行参数的页面;InstantiationUrl为用于当手动启用工作流时呈现给用户的初始化页面。Association_FormURN 和 Instantiation_FormURN 等以 _FormURN 结尾的节点都存放其相应的 InfoPath Form 的 ID(该 ID 可通过设计 InfoPath Form时,打开“文件->属性”窗口即可看到该 InfoPath Form 的 ID)。关于 TaskForm 下面解释。 
  • install.bat:用于安装部署Workflow的批处理文件。根据具体情况注释或反注释命令行,同时配置MOSS地址即可。
  • Workflow1.cs:工作流设计和代码。

2、我的 InfoPath 表单模版无法正常发布,或者发布后状态为“Installing”而不是“Ready”。

    请检查以下几个环节,一般无法正常发布都有可能是以下原因造成。

  1. 首先检查表单模版是否与浏览器兼容。设计表单模版状态下,打开 InfoPath 菜单“工具–>检查设计方案”,然后再在右边任务窗格中点“更改兼容性设置”弹出表单选项,把“浏览器兼容性”组里的“设计一个可在浏览器或 InfoPath 中打开的表单模版”勾起来,然后进行检查。
  2. 如果表单发布用于 Web Access,可同样打开“表单选项”,在“安全和信任”中把“安全级别”调整为“域”级别。
  3. 确认所有的表单最好都不曾发布过。判断依据,把.xsn表单模版文件拷贝到其他地方,打开右键“设计”打开它,如果出现警告错误框提示“此表单已经发布到一个位置,但后来被保存或移动到了另外一个位置。如果确保基于此模板的表单正常工作,请重新发布表单”,那就说明该表单已经发布过,否则则没发布过。没发布过的表单模板,每次保存表单模板时均提示是“发布”还是“保存”,一概“保存”。
  4. 如果该 InfoPath 表单有后部 C#或VB.NET代码,即不小心用 VSTA 打开过,那么发布时会要求必须经过管理员审核,也会造成无法正常发布表单。可在“表单选项”窗口中“编程”里,点击“删除代码”即可。
  5. InfoPath 2007是中文版本,但发布到英文版本的 MOSS 中时也会出现发布异常。同样在“表单选项”窗口中“浏览器”里,选择表单语言为“英语(美国)”即可。

3、我在 MOSS 列表中设置 Workflow 为新增一条记录 A 时就自动启动,同时给审批者创建一条任务,该任务用了我自己定义的任务 InfoPath 表单,但该 InfoPath 任务表单中有几个字段是要读取记录A中字段值来显示的。

    这个问题的实质就是要把某条具体 ListItem 中的字段值传递给 Task 表单(该表单为 InfoPath 表单),然后再通过Task表单显示出来。这里头有三个子问题:怎样获取和当前Workflow相关的 ListItem、通过怎样方式把值传递给 Task表单、Task 表单最后又怎么呈现。

  1. 怎样获取和当前Workflow相关的ListItem:通过SPWorkflowActivationProperties.Item来获取当前 SPListItem。
  2. 通过怎样方式传递给 Task 表单。请查看《How to:Access Workflow Task form Data》。例如 taskProperties.ExtendedProperties["ApplyNo"] = this.ApplyNo;。
  3. Task表单最后怎么呈现。察看《Creating the WOrkflow Task Edit Form》的第4、5、6点。主要是要选择绑定 ows_ 字段时,默认是没有 ows_ 字段的“主”数据源,你必须选择“ItemMetadata (辅助)”数据源才能看到ows_字段。

    具体范例可以查看 ECMStarterKits中 HelloWorld 经典示例。结合上面注意的几点。

4、我现在已经在 Workflow.xml 里配置了一个 Task Form了(有类似这么一个节点<Task0_FormURN>urn:schemas-microsoft-com:office:infopath:ReviewTaskForm:-myXSD-2005-11-22T23-52-35</Task0_FormURN>),这个 Task Form是我第一个 Task 用到的,但我还有第二步,第三步,每个步骤都会建一个任务,我的每个任务都要对应不同的 Task Form 来收集每个阶段的用户录入的数据来运作工作流的。怎么加第二个任务表单?

    根据 SDK 《Windows Task Form〉,无疑,增加第二个Task Form的第一步就是在把设计好的第2步用到的 InfoPath 表单模板的 ID(URN) 配置起来。即在<Task0_FormURN>节点下增加类似这么一个节点:<Task1_FormURN>urn:schemas-microsoft-com:office:infopath:MultiStage-Initiation:-myXSD-2006-04-28T22-44-03</Task1_FormURN>。

    真正核心的问题来了,怎么让第2个任务表单<Task1_FormURN>和实际工作流中分配的第2条任务关联起来?好,关键就在 SPWorkflowTaskProperties.TaskType 这个属性。TaskType = 0 时就表示这是第一个任务请用第一个任务表单,TaskType=1时就表示这是第二个任务请用第二个任务表单。就是这么简单。:)

    你可以打开 ECM StarterKits中的 MultiStage 示例,这个示例是比较实用的例子,务必研读。打开示例中的 WorkflowTask.cs,找个GetTaskCreationProperties()方法。修改如下:

public SPWorkflowTaskProperties GetTaskCreationProperties(int taskType)
{
    SPWorkflowTaskProperties properties = new SPWorkflowTaskProperties();
    properties.AssignedTo = this.Participants[0].AccountId;
    properties.Title = this.TaskTitle;
    properties.Description = this.TaskInstructions;
    properties.SendEmailNotification = true;
    properties.TaskType = taskType;
    properties.DueDate = (DateTime)this.dueDate;

    return properties;
}

    然后再打开 Workflow1.cs 代码,搜索到这么一行代码 activity.TaskCreationProperties = this.activeTask.GetTaskCreationProperties(); 改为:

activity.TaskCreationProperties = this.activeTask.GetTaskCreationProperties(this.completedStages);

    至此,这个 MultiStage 例子就更智能化了。你在增加工作流步骤外,还可以方便控制每个步骤任务用到的 InfoPath 表单了,而不是都用同一个表单。

5、先到这里吧。最后推荐几篇经典文章:

Posted in 未分类 | Tagged , | 9 Comments