You are on page 1of 231

Open Text Corporation provides certain warranties and limitations in connection with the software that this document

describes. For information about these warranties and limitations, refer to pages iii to vi.

Livelink SDK

Developers Guide for Extending Livelink Workflow


This guide describes how to extend the functionality of Livelink Workflow using the Livelink Builder. Livelink Workflow is a preinstalled, optional Livelink module that lets you create and manage workflows in Livelink. You can extend the functionality of the Livelink Workflow module by creating a custom module that incorporates new task types, data types, workflow types, and event trigger scripts with existing workflow functionality.
3201810.1

Copyright 1999 by Open Text Corporation. The copyright to these materials and any accompanying software is owned, without reservation, by Open Text. These materials and any accompanying software may not be copied in whole or part without the express, written permission of Open Text. Open Text Corporation is the owner of the trademarks BASIS, Change Agents, Livelink, Livelink Change Agents, Livelink Channels, Livelink Collaboration, Livelink Discussions, Livelink Enterprise Activator, Livelink Explorer, Livelink Forms, Livelink Intranet, Livelink Library, Livelink LiveReports, Livelink OnTime, Livelink Project Collaboration, Livelink Prospectors, Livelink SDK, Livelink Search, Livelink Spider, Livelink Tasks, Livelink Workflow, OnTime, and Open Text. Other trademarks and trade names in the documentation are owned by other companies and are used for product and company identification and information only. Written by Stephanie Wunder. Code samples supplied by Jeff Lang.

Contacting Us
Open Text Corporation, 185 Columbia Street West, Waterloo, Ontario N2L 5Z5 Canada (519) 888-7111 If you subscribe to our Customer Assistance Program or would like more information about the support program, contact Open Text Customer Support at support@opentext.com or (800) 540-7292 or (519) 888-9933. Our support hours are Monday through Friday, 8:30 a.m. to 8:00 p.m., EST. If you have suggestions for this publication, contact the Open Text Publications Group at pubs@opentext.com. Visit our home page at http://www.opentext.com.

ii

Warranty and Special Provisions for Australia, New Zealand, or Papua New Guinea
Express Limited Warranty
LICENSEE RIGHTS. Licensee may have the benefit of certain rights or remedies pursuant to the Trade Practices Act and similar state and territory laws in Australia or the Consumer Guarantees Act in New Zealand, in respect of which certain liability may not be excluded. LIMITED EXPRESS WARRANTY. Licensor warrants that (i) the media on which the SOFTWARE is distributed will be free from defects in materials and workmanship under normal use for a period of thirty (30) days from the date of delivery of the SOFTWARE; and (ii) unless altered or changed by someone other than Licensor, the SOFTWARE will perform substantially in accordance with the Licensors accompanying documentation for a period of thirty (30) days from the date of delivery of the SOFTWARE. LICENSEE REMEDIES. To the maximum extent permitted under applicable law, Licensors entire liability and your sole and exclusive remedy under the express limited warranty for media shall be the replacement of any defective media without charge. To the maximum extent permitted under applicable law, Licensors entire liability and your sole and exclusive remedy under the express limited warranty for performance of the SOFTWARE shall be, at Licensors option, either to (i) correct the error, (ii) help you work around or avoid the error, or (iii) refund the purchase price for the SOFTWARE which is returned to Licensor with a copy of your receipt. LIMITATION OF LIABILITY. To the maximum extent permitted by applicable law, any conditions or warranties imposed or implied by law are hereby excluded. Licensee may nevertheless have the benefit of certain rights or remedies pursuant to the Trade Practices Act and similar state and territory laws in Australia or the Consumer Guarantees Act in New Zealand in respect of which liability may not be excluded. Insofar as such liability may not be excluded then to the maximum extent permitted by law, such liability is limited, at the exclusive option of the Licensor, to either (a) replacement of the SOFTWARE; or (b) correction of defects in the SOFTWARE; or (c) payment of the cost of having defects in SOFTWARE repaired. EXCLUSION OF LIABILITY/DAMAGES. The following is without prejudice to any rights you may have at law which cannot legally be excluded or restricted. You acknowledge that no promise, representation, warranty or undertaking has been made or given by Licensor (or related company of Licensor) to any person or company on its behalf in relation to the profitability of or any other consequences or benefits to be obtained from the delivery or use of the SOFTWARE and any accompanying manuals or written materials. You have relied upon your own skill and judgment in deciding to acquire the SOFTWARE and any accompanying manuals and written materials for use by you. Except as and to the extent provided in this Agreement, Licensor (or related company of Licensor) will not in any circumstances be liable for any other damages whosoever (including, without limitation, damages for loss of business, business interruption, loss of business information, or other indirect or consequential loss) arising out of the use or inability to use or supply or non-supply of the SOFTWARE and any accompanying written materials. Licensors (or related companys) total liability under any provisions of this Agreement is in any case limited to the amount actually paid by you for the SOFTWARE. This Agreement is governed by the laws of New South Wales, Australia or, where supplies are made in New Zealand, by the laws of New Zealand.

iii

11/97

Warranty and Special Provisions for Canada


Limited Warranty
LIMITED WARRANTY. Licensor warrants that (i) the media on which the SOFTWARE is distributed will be free from defects in materials and workmanship under normal use for a period of thirty (30) days from the date of delivery of the SOFTWARE; and (ii) unless altered or changed by someone other than Licensor, the SOFTWARE will perform substantially in accordance with the Licensors accompanying documentation for a period of thirty (30) days from the date of delivery of the SOFTWARE. Any implied warranties or conditions on the SOFTWARE are limited to thirty (30) days. Some states/jurisdictions do not allow limitations on duration of an implied warranty, so the above limitation may not apply to you. LICENSEE REMEDIES. Licensors entire liability and your sole and exclusive remedy under the express limited warranty for media shall be the replacement of any defective media without charge. Licensors entire liability and your sole and exclusive remedy under the express limited warranty for performance of the SOFTWARE shall be, at the Licensors option, either to (i) correct the error, (ii) help you work around or avoid the error, or (iii) refund the purchase price for the SOFTWARE which is returned to Licensor with a copy of your receipt. This Limited Warranty is void if failure of the SOFTWARE has resulted from accident, abuse, or misapplication. Any replacement SOFTWARE will be warranted for the remainder of the original warranty period. NO OTHER WARRANTIES. To the maximum extent permitted by applicable law, Licensor and its suppliers disclaim all other representations, warranties, either express or implied, including, but not limited to implied warranties of merchantability and fitness for a particular purpose, with regard to the SOFTWARE and the accompanying written materials. This limited warranty gives you specific legal rights. You may have others which vary from state/jurisdiction to state/jurisdiction. NO LIABILITY FOR CONSEQUENTIAL DAMAGES. To the maximum extent permitted by applicable law, in no event shall Licensors (or related companies) be liable for any damages whatsoever (including without limitation, direct or indirect damages for personal injury, loss of business profits, business interruption, loss of business information, or any other pecuniary loss) arising out of the use of or inability to use this product, even if Licensor has been advised of the possibility of such damages. In any case, the Licensors (or related companys) entire liability under any provision of this Agreement shall be limited to the amount actually paid by you for the SOFTWARE. Because some states/jurisdictions do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you. EXPORT LAWS. Licensee shall comply fully with all laws and regulations of Canada (Export Laws) to assure that neither the SOFTWARE nor any direct products thereof (1) are exported, directly or indirectly, in violation of Export Laws, or (2) are used for any purpose prohibited by Export Laws, including without limitation, nuclear, chemical, or biological weapons proliferation. Licensor may audit Licensees use of the SOFTWARE. All terms of any Licensee purchase order or other Licensee ordering document shall be superseded by this License. The EULA is governed by the laws of the Province of Ontario, Canada. Each of the parties hereto irrevocably attorns to the jurisdiction of the courts of the Province of Ontario and further agrees to commence any litigation which may arise hereunder in the courts located in the Judicial District of York, Province of Ontario, Canada.

11/97

iv

Warranty and Special Provisions for Europe


Limited Warranty
LIMITED WARRANTY. Licensor warrants that (i) the media on which the SOFTWARE is distributed will be free from defects in materials and workmanship under normal use for a period of thirty (30) days from the date of delivery of the SOFTWARE; and (ii) unless altered or changed by someone other than Licensor, the SOFTWARE will perform substantially in accordance with the Licensors accompanying documentation for a period of thirty (30) days from the date of delivery of the SOFTWARE. Any implied warranties on the SOFTWARE are limited to thirty (30) days. Some states/jurisdictions do not allow limitations on duration of an implied warranty, so the above limitation may not apply to you. LICENSEE REMEDIES. Licensors entire liability and your sole and exclusive remedy under the express limited warranty for media shall be the replacement of any defective media without charge. Licensors entire liability and your sole and exclusive remedy under the express limited warranty for performance of the SOFTWARE shall be, at the Licensors option, either to (i) correct the error, (ii) help you work around or avoid the error, or (iii) refund the purchase price for the SOFTWARE which is returned to Licensor with a copy of your receipt. This Limited Warranty is void if failure of the SOFTWARE has resulted from accident, abuse, or misapplication. Any replacement SOFTWARE will be warranted for the remainder of the original warranty period. NO OTHER WARRANTIES. To the maximum extent permitted by applicable law, Licensor and its suppliers disclaim all other representations, warranties, conditions or other terms, either express or implied, including, but not limited to implied warranties and/or conditions of merchantability and fitness for a particular purpose, with regard to the SOFTWARE and the accompanying written materials. This limited warranty gives you specific legal rights. You may have others which very from state/jurisdiction to state/jurisdiction. NO LIABILITY FOR CONSEQUENTIAL DAMAGES. To the maximum extent permitted by applicable law, in no event shall Licensors (or related companies) be liable for any damages whatsoever (including without limitation, direct or indirect damages for personal injury, loss of business profits, business interruption, loss of business information, or any other pecuniary loss) arising out of the use of or inability to use this product, even if Licensor has been advised of the possibility of such damages. In any case, the Licensors (or related companys) entire liability under any provision of this Agreement shall be limited to the amount actually paid by you for the SOFTWARE. Because some states/jurisdictions do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you.

Special Provisions
Reverse Engineering: If you acquired the SOFTWARE in the European Community, you may not reverse engineer, decompile, or disassemble the SOFTWARE except to the extent and for the express purposes authorized by applicable law. This EULA is governed by the laws of England and Wales.

11/97

Warranty and Special Provisions for the United States of America or Any Other Country
Limited Warranty
LIMITED WARRANTY. Licensor warrants that (i) the media on which the SOFTWARE is distributed will be free from defects in materials and workmanship under normal use for a period of thirty (30) days from the date of delivery of the SOFTWARE; and (ii) unless altered or changed by someone other than Licensor, the SOFTWARE will perform substantially in accordance with the Licensors accompanying documentation for a period of thirty (30) days from the date of delivery of the SOFTWARE. Any implied warranties on the SOFTWARE are limited to thirty (30) days. Some states/jurisdictions do not allow limitations on duration of an implied warranty, so the above limitation may not apply to you. LICENSEE REMEDIES. Licensors entire liability and your sole and exclusive remedy for media warranty shall be the replacement of any defective media without charge. Licensors entire liability and your sole and exclusive remedy for performance of the SOFTWARE shall be, at the Licensors option, either to (i) correct the error, (ii) help you work around or avoid the error, or (iii) refund the purchase price for the SOFTWARE which is returned to Licensor with a copy of your receipt. This Limited Warranty is void if failure of the SOFTWARE has resulted from accident, abuse, or misapplication. Any replacement SOFTWARE will be warranted for the remainder of the original warranty period. NO OTHER WARRANTIES. To the maximum extent permitted by applicable law, Licensor and its suppliers disclaim all other warranties, either express or implied, including, but not limited to implied warranties of merchantability and fitness for a particular purpose, with regard to the SOFTWARE and the accompanying written materials. This limited warranty gives you specific legal rights. You may have others which vary from state/jurisdiction to state/jurisdiction. NO LIABILITY FOR CONSEQUENTIAL DAMAGES. To the maximum extent permitted by applicable law, in no event shall Licensors (or related companies) be liable for any damages whatsoever (including without limitation, special, incidental, consequential, or indirect damages for personal injury, loss of business profits, business interruption, loss of business information, or any other pecuniary loss) arising out of the use of or inability to use this product, even if Licensor has been advised of the possibility of such damages. In any case, the Licensors (or related companys) entire liability under any provision of this Agreement shall be limited to the amount actually paid by you for the SOFTWARE. Because some states/jurisdictions do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you.

Special Provisions
Export Restrictions
The Export Administration Regulations of the United States prohibit the export from the United States or Canada of technical data relating to certain commodities. Licensee hereby agrees to comply with the Export Administration Regulations of the United States as and hereby gives to Licensor the assurances required in the Export Administration Regulations.

U.S. Government Rights


Department of Defense End Users. Pursuant to DFARS Section 227.7202 and its successors, the Governments right to use, reproduce, or disclose the SOFTWARE and any accompanying documentation acquired under this contract is subject to the restrictions of Open Texts standard commercial SOFTWARE license agreement. Other Government Agency End Users. Pursuant to FARS Section 12.212 and its successors, the Governments right to use, reproduce, or disclose the SOFTWARE and any accompanying documentation acquired under this contract is subject to the restrictions of Open Texts commercial SOFTWARE license agreement. Manufacturer is Open Text Corporation, 185 Columbia Street West, Waterloo, Ontario N2L 5Z5.

11/97

vi

Typographical Conventions Used in This Guide


All information in the following table is case-sensitive unless otherwise noted.
Items Convention These items appear in regular (normal) typeface. Some elements may File names, directory be shown in italic to indicate placeholders. names, folder names, path names, window Examples: names, dialog box names, Run setup.exe to start the installation program. Web page names, URLs, Open the Livelink_home/config/opentext.ini file in a text editor. and e-mail addresses Note The placeholder Livelink_home represents the Livelink root directory (directory where Livelink was installed). Contact Open Text Customer Support at support@opentext.com. In Windows NT Control Panel, double-click the Services icon to open the Services dialog box. In your Web browser, go to the Open Text Web site at http://www.opentext.com. Names of user interface These items appear in bold typeface. elements, such as Examples: buttons, links, menus, On the Go to menu, click Personal Workspace. check boxes, radio In the Services list, click Livelink Intranet : service_name, and then buttons, lists, fields, and click the Startup button. so on To store your documents outside the database, select the External Document Storage check box, and then click the Create Tables button. Click the items Info icon to open its General Info page. Click the Admin Home link to go to the Livelink Intranet Administration page. These items appear in italic typeface. Variable placeholders, references to other Examples: documents, new or For more information, see the Livelink Administrators Guide. special terminology, and After you modify this parameter, you must restart the Livelink emphasis Intranet server for the changes to take effect. You can scan new documents for content of interest by saving your search criteria in a special type of query called a prospector. In your Web browser, go to the default Livelink start page at protocol://host:port/URL_prefix/livelink.exe, where protocol is http or https, host is the DNS name of the HTTP server host, port is the port number on which the HTTP server is listening, and URL_prefix is the prefix mapped to the Livelink_home/cgi directory in the HTTP server.

vii

Items

Convention

These items appear in quotation marks. References to chapters and sections of Examples: documents, and citations For more information, see Chapter Three, Projects, in the of messages displayed to Livelink QuickStart guide. users For more information, see Item Types in Chapter Five, Livelink Items. For more information, see Item Types, page 150. If the import completes successfully, Oracle displays the message Database import completed without errors. These items appear in monospaced font. Examples: In the User Name field, type Admin. At the operating system prompt, type start-llserver, and then press ENTER. When searching for users, you can set the maximum number of users displayed per page by setting the value (default is 30) of the MaxUsersToListPerPage parameter in the [general] section of the opentext.ini file. Key names appear in ALL CAPS. Examples: Press ENTER to start a new line when typing in this field. To select multiple items, hold down the CTRL key while you click the items that you want to select.

Text typed by users, operating system commands, code examples, feature names, method names, and object names

Key names

viii

Table of Contents
Chapter One .................................................................................................................................................................................... ....................................................................................................................................................1 Introduction .................................................................................................................................................................................... 1 Livelink Workflow Terminology ......................................................................................................................................2 Workflows, Workflow Routes, and Work Packages .........................................................................................2 Workflow Relationships.............................................................................................................................................3 The Workflow Painter.................................................................................................................................................4 Workflow Status ...........................................................................................................................................................7 Extending Livelink Workflow ...........................................................................................................................................9

Chapter Two Module.............................................................................................................................................. ................................................................................................................................ The Livelink Workflow Module .............................................................................................................................................. 11 Livelink Workflow Architecture .................................................................................................................................... 12 WAPI .............................................................................................................................................................................. 12 OScript .......................................................................................................................................................................... 12 Using WAPI and OScript for Workflow Management and Status............................................................ 13 Understanding Task Types in Livelink Workflow ................................................................................................... 16 Understanding Data Types in Livelink Workflow................................................................................................... 18 Understanding Workflow Types in Livelink Workflow......................................................................................... 19 Understanding Callback Events in Livelink Workflow.......................................................................................... 21 Event Trigger Scripts................................................................................................................................................ 23

Chapter Three Module................................................................................................................................................. ................................................................................................................................ Setting Up a Custom Module................................................................................................................................................. 25 Setting up the Custmod Module ................................................................................................................................. 26 Creating the Module's Directory Structure ..................................................................................................... 26 Creating an OSpace ................................................................................................................................................. 26 Configuring the Module......................................................................................................................................... 27 Orphaning the Configure Request Handler Object...................................................................................... 28 Setting Up the Query String and the module.ini File .................................................................................. 28 Orphaning a RequestHandlerGroup Object ................................................................................................... 28 Completing the Custmod Module .............................................................................................................................. 30

ix

Chapter Four Types........................................................................................................................................................... ................................................................................................................................ Adding New Task Types ........................................................................................................................................................... 31 Defining the Task Types API Object........................................................................................................................... 32 Defining the Workflow Painter Information ............................................................................................................ 34 Defining the Status and Display of the Task............................................................................................................ 36 Example 1: Adding the Custom Display Task Type ............................................................................................... 39 The CustomDisplayAPI Object ............................................................................................................................. 39 CreateReviewMapRec().................................................................................................................................. 40 GetPainterInfo() ................................................................................................................................................ 43 ReadyTaskForInitiation()................................................................................................................................ 43 SetTaskDefaults().............................................................................................................................................. 45 SetTaskRecFromMapTask()........................................................................................................................... 46 The CustomDisplayPaint Object ......................................................................................................................... 48 GetMapData() .................................................................................................................................................... 48 PutMapData() .................................................................................................................................................... 52 t_user.html ......................................................................................................................................................... 56 The CustomDisplayWork object.......................................................................................................................... 61 GetDisplayPerformerInfo()............................................................................................................................ 62 GetPainterInfo() ................................................................................................................................................ 63 GetPainterMenu()............................................................................................................................................. 64 GetStatusDisplay() ........................................................................................................................................... 66 GetTaskEditData() ............................................................................................................................................ 69 GetTaskGif()........................................................................................................................................................ 72 NewPerformer() ................................................................................................................................................ 73 PutReviewData()............................................................................................................................................... 74 ReassignStep()................................................................................................................................................... 75 redirect.html ...................................................................................................................................................... 78 The WFCustomScriptPkg Object......................................................................................................................... 79 CBExecute() ........................................................................................................................................................ 80 ExecuteCustTaskScript() ................................................................................................................................ 80 ExecuteScript() .................................................................................................................................................. 81 ListScripts() ......................................................................................................................................................... 82 ListTemplates().................................................................................................................................................. 82 Adding Custom Scripts and Templates ............................................................................................................ 83

Chapter Five ................................................................................................................................ .......................................................................................................................... Adding New Data Types .......................................................................................................................................................... 85 Defining the Data Types API Object .......................................................................................................................... 86 Defining the Data Types Workflow Painter Information.................................................................................... 89 Defining the Data Types Web Object........................................................................................................................ 91 Example 2: Adding a New Data Type ......................................................................................................................... 94 Creating the Database Tables .............................................................................................................................. 95 cust_sql() ............................................................................................................................................................. 97 cust_drop() ......................................................................................................................................................... 98 Creating a Utility Script........................................................................................................................................... 98 SetSubPaneIndexArg...................................................................................................................................... 99 Defining the Data Types API Object.................................................................................................................. 99 CheckTaskDone() ...........................................................................................................................................101 CreateNewInstance() ....................................................................................................................................102 CreateWorkData() ..........................................................................................................................................103 DeleteWorkData() ..........................................................................................................................................103 LoadStartTaskWorkData() ...........................................................................................................................104 LoadTableValues() .........................................................................................................................................105 LoadTaskWorkData().....................................................................................................................................106 LoadWorkData()..............................................................................................................................................107 RemoveWorkData() .......................................................................................................................................107 SaveTableValues() ..........................................................................................................................................108 SaveWorkData() ..............................................................................................................................................109 SetReviewData() .............................................................................................................................................109 SetSubWorkData() .........................................................................................................................................110 SetSubWorkReturnData()............................................................................................................................110 UpdateSubWorkData().................................................................................................................................111 UpdateTableValues().....................................................................................................................................112 Defining the Data Types Workflow Painter Information .........................................................................113 GetMapData() ..................................................................................................................................................114 PutMapData() ..................................................................................................................................................114 t_tablevalues.html.........................................................................................................................................115 Defining the Data Types Web Object.............................................................................................................117 GetData() ...........................................................................................................................................................118 GetSubMapData() ..........................................................................................................................................119 GetTabInfo() .....................................................................................................................................................120 PutSubMapData()...........................................................................................................................................121 SaveData().........................................................................................................................................................122

xi

submap_tablevalues.html ..........................................................................................................................123 tablevalues.html.............................................................................................................................................126 projectpane.html ...........................................................................................................................................126 customerpane.html.......................................................................................................................................129

Chapter Six ..............................................................................................................................................133 .............................................................................................................. Adding New Workflow Types .............................................................................................................................................. 133 Creating Workflow Types .............................................................................................................................................134 Example 3: Adding a Custom Workflow Type to the custmod Module ......................................................135 CBExecute() ...............................................................................................................................................................136 GetWFTypeName().................................................................................................................................................137 GetWFTypes() ...........................................................................................................................................................138 StartWF() ....................................................................................................................................................................138

Chapter Seven ...............................................................................................................................................141 ............................................................................................................... Adding Event Trigger Scripts ............................................................................................................................................... 141 Choosing Event Trigger Scripts ..................................................................................................................................142 Creating General Event Trigger Scripts ...................................................................................................................144 Example 4: Updating Workflow Attributes ............................................................................................................146 ChangeAttribute() ..................................................................................................................................................146 Creating Performer Event Trigger Scripts...............................................................................................................149 Example 5: Assigning Steps to Workflow Participants.......................................................................................151 ChooseUser() ............................................................................................................................................................151 Creating Submap Event Trigger Scripts ..................................................................................................................154 Example 6: Defining Sub-Workflows On-The-Fly..................................................................................................156 subworkflow() ..........................................................................................................................................................156 Restricting Access to Event Trigger Scripts............................................................................................................163

Appendix A ...............................................................................................................................................165 ............................................................................................................... Creating the Form Task Type ............................................................................................................................................... 165 The FormTaskAPI Object...............................................................................................................................................166 GetPainterInfo().......................................................................................................................................................166 ReadyTaskForInitiation() ......................................................................................................................................167 SetTaskDefaults() ....................................................................................................................................................168 SetTaskRecFromMapTask() .................................................................................................................................169 The FormTaskPaint Object...........................................................................................................................................171 GetMapData()...........................................................................................................................................................171 PutMapData() ...........................................................................................................................................................175

xii

formtask.html...........................................................................................................................................................177 The FormTaskWork Object...........................................................................................................................................181 GetDisplayPerformerInfo() ..................................................................................................................................182 GetPainterInfo().......................................................................................................................................................182 GetPainterMenu() ...................................................................................................................................................184 GetStatusDisplay()..................................................................................................................................................186 GetTaskEditData()...................................................................................................................................................189 NewPerformer().......................................................................................................................................................192 ReassignStep() .........................................................................................................................................................192 Request Handlers ............................................................................................................................................................195 FormEditSetPrototype()...................................................................................................................................197 FormEditExecute() .............................................................................................................................................197 FormEditStartSetPrototype() .........................................................................................................................201 FormEditStartExecute()....................................................................................................................................201 SaveFormSetPrototype().................................................................................................................................204 SaveFormExecute()............................................................................................................................................204 SaveFormStartExecute() ..................................................................................................................................207 ............................................................................................................................................................................................. .............................................................................................................................................................211 Index ............................................................................................................................................................................................. 211

xiii

xiv

Chapter One

Introduction
Livelink Workflow is a preinstalled, optional Livelink module that lets you create and manage workflows in Livelink. Workflows are business processes that follow sequential pathsfrom start to completion. You create workflows to automate procedures that have a series of defined steps. For example, you can create a workflow that runs each time you submit an expense report. In this case, Livelink can route the document to a manager or supervisor for approval, and then to your companys Accounting department for payment. The workflows you create can be simple or complex, depending on the tasks that you want to automate. You can extend Livelinks workflow functionality by incorporating new task types, data types, workflow types, and event trigger scripts with the existing Livelink Workflow module. When you extend Livelink's workflow functionality in this way, the new task types, data types, workflow types, and event trigger scripts are stored in a separate, custom module. This simplifies installation and reduces the possibility of conflict when you upgrade Livelink or the Livelink Workflow module. For information about creating a separate, custom module, see Setting Up a Custom Module, page 25. Note Do not extend Livelink's workflow functionality by altering the original Livelink Workflow module. If you do, future upgrades to Livelink or the Livelink Workflow module will overwrite your changes. In this chapter, you will learn about: Workflows, workflow maps, and work packages Workflow relationships, tasks, and responsibilities The Workflow Painter

Livelink Workflow Terminology

Livelink Workflow Terminology


Before you can extend Livelink's workflow functionality, you must be familiar with the terminology and components involved in creating workflows. Understanding the significance of workflows, workflow routes, work packages, workflow relationships, and the Workflow Painter from a Livelink users perspective, gives you the base knowledge required to participate in a high-level extension of the Livelink Workflow module.

Workflows, Workflow Routes, and Work Packages


Workflows are business processes that follow sequential pathsfrom start to completion. You create workflows to automate procedures that have a series of defined steps or tasks that must be performed in sequence. The sequential paths of defined steps, along which workflows travel are called workflow routes. Some steps in a workflow route are tasks that particular Livelink users or groups are responsible for completing. Other steps do not involve direct user-interactionthese include Milestone steps which specify a date by which a certain number of workflow tasks should be complete, and Sub-workflow steps which initiate other workflow processes within the main workflow. When a workflow is initiated, Livelink analyzes the first step and routes the corresponding task to the Tasks page of the Livelink user or group to which it is assigned. When that Livelink user or group completes the task, Livelink analyzes the second step in the workflow and routes the corresponding task to the Tasks page of the Livelink user or group to which it is assigned (if necessary). Livelink continues analyzing and routing information in this way, until the workflow is complete. Tip You can view the workflow tasks for which you are responsible by clicking the Tasks tab in your Personal Workspace. The data that is associated with each Livelink workflow and that travels along the workflow route is stored in a work package. Work packages contain the attachments (for example, documents and other Livelink items) with which workflow participants work. Work packages can also contain attributes and comments. Attributes are fields which store information that you want to track throughout the workflow. Workflow participants can examine the attributes and specify new values at each workflow step. Comments are special instructions or notes that can be added by workflow participants as they complete their task assignments. Note For more information about workflow attributes, see the Livelink online help. In Livelink, workflows are originally represented by workflow maps. Workflow maps are Livelink items that let you define a specific business process and provide a graphical representation of the workflow route and work package.

Developers Guide for Extending Livelink Workflow

Livelink Workflow Terminology

Workflow Relationships
Workflow relationships define the responsibilities and capabilities that particular Livelink users and groups have in a workflow. There are four workflow relationships that Livelink users have: creator, initiator, participant, and manager.
Table 1-1: Workflow Relationships Relationship Creator Initiator Participant Manager Description Adds the workflow map to Livelink, paints it, and defines its work package Initiates the workflow map, starting a workflow process Works on the components of the work package and completes tasks Monitors the status of a workflow and usually has the authority to modify, suspend, resume, or stop a workflow after it is initiated

The creator of a workflow map assigns tasks to other participants by defining the work package and work route. Workflow tasks are displayed on the Tasks page in a workflow participants Personal Workspace. If you are responsible for completing the first task in a workflow, the task is displayed on your Tasks page when the workflow is initiated. If you are responsible for completing a task that occurs later in the workflow, the task is displayed on your Tasks page when the task that precedes it in the workflow route is complete. The initiator of a workflow is the Livelink user that starts the workflow process (usually by clicking the workflow name in Livelink). If the creator of the workflow map selects the Display at Initiation check box on the Start Step Definition page when creating the workflow map, the workflows work package is routed to the initiator's task list before the first task is displayed on the first workflow participants Tasks page. This lets the initiator of the workflow modify the work package (for example, add attachments or edit attributes) before routing the first task to the assigned participant. If the creator of the workflow map does not select the Display at Initiation check box on the Start Step Definition page when creating the workflow map, the first task in the workflow is routed to the assigned participant immediately. Workflow participants are responsible for reviewing the work package and completing the tasks that are listed on the Tasks page in their Personal Workspaces. When a workflow participant completes a task, Livelink routes the work package to the participant who is responsible for completing the next task in the sequence. The manager of a workflow monitors the workflows progress, confirms that milestones are met, and makes adjustments to the work package when necessary. If you are the manager of an active workflow, you can click Workflow Status on the Go to menu to monitor the workflows status. The Workflow Status page displays the titles of all the active workflows for which you have responsibility, the status of the active workflows (OK, Step Late, Workflow Late, Suspended, Stopped, or Completed), the name of the

Introduction

Livelink Workflow Terminology

workflow participant that is assigned to the current task, the workflows due date and time, and the relationship you have in the workflow. You can only specify one Livelink user or group as the manager of a workflow; however, you can establish different permission levels within a single management group. This means that the creator of a workflow map can specify a group of Livelink users as the manager of the workflow, and can then expand that group and assign different permission levels to the base group members and the workflow initiator. This means that some members of the workflow management group may view the detailed workflow status while others may not. Other members may have permissions to suspend, stop, or delete workflows but may not have permission to change the data in the work package. Note For more information about the Workflow Status page, see Workflow Status, page 7.

The Workflow Painter


You create workflow maps using Livelinks Java-based Workflow Painter. The Workflow Painter lets you paint a graphical representation of a workflow process and is located on the Map Editor page in Livelink. The Workflow Painter provides a drag-and-drop interface where you define the workflow route and work package using icons. The Workflow Painter contains the Step Icon Palette, the Function window, and the Map Overview window.

Figure 1-1: The Workflow Painter

Developers Guide for Extending Livelink Workflow

Livelink Workflow Terminology

Step Icon Palette


The Step Icon Palette contains icons which represent the different types of tasks that you can add to the workflow map that you are designing in the Workflow Painter.
Table 1-2: Step Icon Palette Icon Description Defines a preliminary step which lets the initiator of the workflow add data to the work package or cancel the workflow before the first task is routed to the workflow participant. The Start step is the first step in every workflow map and is displayed in the Workflow Painter by default. Defines a User step in the workflow, which lets you assign work to a Livelink user or group. You can use the User step to define the task that you want a Livelink user or group to perform and to provide guidelines for completing that task. Defines an Evaluate step in the workflow, which lets you specify different workflow routes based on the current status of the workflow. You can use the Evaluate step to specify workflow conditions and to design the workflow routes that are associated with those conditions. If the condition you specify is true, the workflow is routed along one path. If the condition you specify is false, the workflow is routed along a different path. You can also base conditions on the values of data contained in the work package or on the disposition of specific tasks. Defines a Milestone step in the workflow, which specifies an important date that occurs during the execution of the workflow. You can use Milestone steps to set target dates at which time certain workflow tasks must be complete. You can then tell Livelink to inform the initiator and manager of the workflow if milestone dates have been exceeded by sending them an e-mail notification (using Change Agents). Milestone steps execute automatically when the previous task is completed and have no duration. Defines an Initiator step, which lets you assign tasks to the initiator of the workflow Defines a sub-workflow step in the main workflow, which lets you initiate a separate workflow map as a sub-process within the main workflow

Note For more information about the icons in the Step Icon Palette and the tasks that are associated with those icons, see the Livelink online help.

Introduction

Livelink Workflow Terminology

Function Window
By default, the Function window lets you edit the properties of a workflow map, save the workflow map, or initiate the workflow map.

Figure 1-2: Function Overview Window

When you create a workflow map in the Workflow Painter, you define a work package that contains the data that the workflow participants require to complete their task assignments. The work package can include attachments, attributes, comments, or forms. You can define the work package by setting the properties of the workflow map. You can: Define general information associated with the workflow map, such as its title, description, and due date. Define the manager or management group responsible for monitoring and updating the status and execution of the workflow map. Select documents and other Livelink items (for example, queries, URLs, forms, and spreadsheets) to include as part of the work package. Define attributes that store information as a means of tracking and managing the work process and that prompt for user input.

The Function Overview window also lets you save and initiate workflows. If you want to reuse a workflow map that you create in the Workflow Painter, you can save it to a Livelink location for which you have the Modify permission. If you do not want to reuse a workflow map, you can initiate it and then discard it by closing the Workflow Painter (that is, leaving the Map Editor page).

Map Overview window


The Map Overview window displays an overview of the workflow map that you paint in the Workflow Painter.

Figure 1-3: Map Overview Window

Developers Guide for Extending Livelink Workflow

Livelink Workflow Terminology

The modifications that you make to a workflow map in the Workflow Painter are dynamically updated in the Map Overview window. This window is especially useful for large maps that cannot be displayed on a single screen in the Workflow Painter. Note For more information about creating and using workflows in Livelink, see the Livelink online help.

Workflow Status
If you are the manager or initiator of a workflow, you can monitor the status of the workflow after it is initiated in Livelink. The Workflow Status page displays the title, status, step information, due date, and relationship associated with the active workflows in Livelink. The Workflow Status page is displayed when you click Workflow Status in the Go to menu.
Table 1-3: The Workflow Status Page Status Title Info/Status Step Due Date Relationship Description The name assigned to the workflow at initiation. You can click this title to display detailed workflow status. Status information about the workflow. Valid values include OK, Step Late, Workflow Late, Suspended, Stopped, or Completed. The name of the Livelink user or group that is assigned to the active task in the workflow The date and time by which the active task should be complete. If a due date is not specified for a workflow, this column is set to No Due Date. Your relationship to the active workflow. This value is set to Manage or Initiated, depending on the option you click in the Show list.

You can adjust the display of workflows on the Workflow Status page by clicking All, Initiated, or Managed in the Show list. If you click All, all of the active workflows in Livelink are displayed. If you click Initiated, only the workflows that you have initiated are displayed on the Workflow Status page. If you click Managed, only the active workflows that you manage are displayed on the Workflow Status page. You can also choose to display workflows that are archived, not archived, or currently executing. You can view more detailed workflow status by clicking the workflow title on the Workflow Status page. The Detailed Status page contains five tabs, each describing the status of the active workflow in more detail.
Table 1-4: The Detailed Status Page Status Tab General Description Displays the workflow title, the date on which the workflow was initiated, the Livelink user name of the initiator, the due date, the status, and the date on which the workflow was completed. The General tab also displays the Stop and Suspend buttons, which let you stop or suspend the execution of the workflow.

Introduction

Livelink Workflow Terminology

Table 1-4: The Detailed Status Page Status Tab Map View Step List Description Displays a non-editable, graphical representation of the workflow map. For more information about the Map View tab, see the Livelink online help. Displays the names of the steps in the workflow, the name of the performer of each step, the due date for each step, and the date on which the step is completed. If a step has not yet been completed, it is set to <Awaiting Completion>. Note You can view detailed information about each step by clicking the step name. This displays the Step Detail page which lets you reassign a task to another Livelink user or group, if appropriate. Lists all events that occurred during the execution of the workflow, including the date, time, and name of the Livelink user or group that performed the events Displays links to all of the items that are attached to the workflow. You can also add attachments to the workflow from the Attachments page. Displays all of the attributes that are defined for the workflow. If you have not defined attributes for the workflow, this tab is not displayed on the Detailed Status page.

Audit

Attachments Attributes

Developers Guide for Extending Livelink Workflow

Extending Livelink Workflow

Extending Livelink Workflow


If you understand how the Livelink Workflow module operates within Livelink to let you create and manage workflows, you can begin to explore the possibilities for extending Livelink's existing workflow functionality, such as: Adding new task types Adding new data types Adding new workflow types Adding event trigger scripts

One way to extend the functionality of Livelink workflows is to add new task types to the Step Icon Palette. New task types let Livelink users add custom tasks to the workflow maps that they create in Livelink. You add a new task type to Livelink when you want to customize the interface and operations presented to workflow participants for particular types of tasks in a workflow. When you add a new task type, you design the interface that is presented to the performer of a workflow task and you define the custom functionality that the new interface provides. You can create task types that require workflow participants to perform automated tasks, such as filling out a form or sending a file to the printer or fax machine. Then, when the new task becomes ready in an active workflow, a custom task is displayed on the corresponding workflow participants Tasks page. For more information about adding new task types, see Adding New Task Types, page 31. You add new data types to Livelink if you want to include new types of information in the work packages that accompany your workflows. By default, work packages can contain three data types: attachments, attributes, and comments; however, you can create a data type for any new type of information that you want to add to a work package. Adding new data types to Livelink also lets you customize the Livelink interface. For example, if the work packages that you include with your workflows usually contain many attributes, you can create a data type that creates the attributes and groups them on tabs in the Livelink interface. This makes it easy for workflow participants to locate and use the attributes in the work package. For more information about data types, see Adding New Data Types, page 85. You can also extend the functionality of Livelink workflows by adding new workflow types to Livelink. You add new workflow types when you want to apply a particular set of custom operations to different workflow map definitions. Adding new workflow types lets you define the set of custom operations that are applied to a workflow map definition when the creator of a workflow map chooses to create that particular type of workflow in Livelink. These custom operations can include adding workflow tasks, modifying the duration or name of existing workflow tasks, adding callback scripts to workflow events, or any other modification that you want to make to a workflow map definition. For more information about adding new types of workflows, see Adding New Workflow Types, page 133.

Introduction

Extending Livelink Workflow

Another way to extend the functionality of Livelink workflows is to create custom event trigger scripts. Event trigger scripts automatically perform an operation when a specific workflow event occurs. For example, if you want to save a workflow attachment as a new version of an existing Livelink document when the workflow is complete, you can create an event trigger script that automatically saves the item for you. You can also create event trigger scripts that monitor the work load of all workflow participants and assign a particular workflow task to the participant with the least amount of tasks. In fact, you can create event trigger scripts that perform any operation when a specific workflow event occurs. For more information about adding event trigger scripts, see Adding Event Trigger Scripts, page 141.

10

Developers Guide for Extending Livelink Workflow

Chapter Two

The Livelink Workflow Module


A high-level extension of Livelink's workflow functionality, requires an advanced understanding of the architecture and operation of the existing Livelink Workflow module. The functionality of the Livelink Workflow module is controlled by the Workflow Application Programming Interface (WAPI) and the OScript programming language. WAPI and OScript work together to create a robust workflow management system that provides advanced management tools and status control. The Livelink Workflow module is based on a combination of task types, data types, workflow types, and event trigger scripts. After you understand how these features work in the main Livelink Workflow module, you can create your own module that extends their capabilities. In this chapter, you will learn about: WAPI and OScript Workflow management and status Task types, data types, workflow types, and event trigger scripts

11

Livelink Workflow Architecture

Livelink Workflow Architecture


The Livelink Workflow module lets you create and manage workflows in Livelink using the Workflow Application Programming Interface (WAPI) and the OScript programming language.

WAPI
WAPI is an application programming interface that maintains a database of workflow maps (maps) and work packages (work). Workflow maps define specific tasks and the links that join those tasks in a workflow. Work is created when a workflow map is initiated in Livelink and defines the work package that is routed through the workflow. WAPI also provides functions that you can use to query the database of workflow maps and work packages. WAPI is responsible for most of the database interaction involved in the creation and execution of Livelink workflows, including the maintenance of basic workflow information such as titles, due dates, status, and to-do lists. WAPI is also responsible for maintaining a complete audit trail.

OScript
The OScript programming language is an object-oriented scripting language that lets you develop and customize applications using the Livelink Builder. In Livelink, OScript is used to control the graphical user interface (GUI) for painting workflow maps and displaying to-do lists and task information to workflow participants. OScript is used along with Livelinks document management functionality to store workflow maps. It stores workflow maps as document nodes that can manipulate Livelinks extensive document management functionality (including permissions, version control, and attributes). Additionally, OScript almost completely handles the flow of data as it is routed through a workflow. Note For information about the Workflow API (WAPI) built-in package which is available in OScript, see the Livelink Developers Documentation.

12

Developers Guide for Extending Livelink Workflow

Livelink Workflow Architecture

Using WAPI and OScript for Workflow Management and Status


WAPI and OScript work together to manage the permissions that you can assign to workflow managers and the status of workflows in Livelink. The manager of a workflow monitors a workflows status and has the authority to modify, suspend, resume, or stop a workflow after it is initiated. WAPI lets the creator of a workflow map specify only one Livelink user or group as the workflow manager; however, OScript lets the creator of a workflow map establish different permission levels within a single management group. Because WAPI and OScript work together in this way, the creator of a workflow map can specify a group of Livelink users as the manager of the workflow, and can then expand that group and assign different permission levels to the base group members and the workflow initiator. This means that some members of the workflow management group may view the detailed workflow status while others may not. Other members may have permission to suspend, stop, or delete workflows but may not have permission to change the data in the work package. In the following image, the Publications group is the manager of the workflow and the group members and initiator have different permission levels.

The Livelink Workflow Module

13

Livelink Workflow Architecture

Figure 2-1: Workflow Management Group Permissions

Whether the manager of a workflow is an individual Livelink user or a group of Livelink users, they are concerned with the due dates and status of the workflows that they manage. In Livelink, WAPI is responsible for calculating and maintaining all of the due dates and completed dates for a workflow; however, WAPI is only aware of workflow status in terms of Suspended, Stopped, Archived, and so on. OScript is responsible for defining the more detailed workflow status that is presented to workflow managers. Detailed status levels are determined by analyzing the due dates for a workflow and the tasks it contains. Tip Because detailed status levels are determined by OScript, OScript programmers can change the definition of the status levels or add new levels just by overriding the script that defines them.

Storing Workflow Management and Status Information


Workflow management and status information is stored in a table named WWork, in the Livelink database.

Figure 2-2: The WWork Table

The rows in the WWork table contain specific workflow management information, such as due dates, milestone dates, permission settings, and status. For example, the WORK_DATECOMPLETED row, contains the date on which the workflow was completed.

14

Developers Guide for Extending Livelink Workflow

Livelink Workflow Architecture

Workflow management and status information for sub-workflows is stored in a table named WSubWork in the Livelink database.

Figure 2-3: The WSubWork Table

The rows in the WSubWork table contain specific workflow management information, such as due dates, milestone dates, permission settings, and status for sub-workflows. For example, the SUBWORK_DATECOMPLETED row, contains the date on which the subworkflow was completed.

The Livelink Workflow Module

15

Understanding Task Types in Livelink Workflow

Understanding Task Types in Livelink Workflow


To a Livelink user, workflows can have many different task types, including Start, Initiator, User, Evaluate, Sub-workflow, and Milestone. To WAPI, all of the task types in a workflow are essentially the samethe difference is primarily determined by the values stored in the pFlags attribute that is associated with each task. Each workflow task is associated with an attribute named pFlags which controls the behavior of the task. There are two optional values for the pFlags attribute: WAPI.MAPTASK_FLAG_AUTODONE and WAPI.MAPTASK_FLAG_MILESTONE. These values are stored in the MapTask_Flags column in the WMapTask table.

Figure 2-4: The WMapTask Table

The WAPI.MAPTASK_FLAG_AUTODONE flag is a constant value that tells WAPI that the current task does not require user interaction and can be marked finished as soon as it becomes ready. The WAPI.MAPTASK_FLAG_MILESTONE flag is a constant value that indicates to WAPI that the task should be marked as a Milestone step and that milestone dates should be calculated and stored. This flag also stores the milestone date (that is, the date by which the task is scheduled to be complete).
Table 2-1: Standard Workflow Task Types Task Type Start Description A preliminary task type which lets the initiator of a workflow add data to the work package or cancel the workflow before it is routed to the first workflow participant. The Start task type is not included in the WAPIMAP database table as a step in the workflow route. Note For more information about the WAPIMAP database table, see Understanding Workflow Types in Livelink Workflow, page 19.

16

Developers Guide for Extending Livelink Workflow

Understanding Task Types in Livelink Workflow

Table 2-1: Standard Workflow Task Types Task Type User and Initiator Description Workflow task types which provide the main user interface into the data in the workflow. The only difference between the User and Initiator task types is that Initiator tasks use Performer event trigger scripts to assign the task to the Livelink user that initiated the workflow when the step becomes ready. The creator of a workflow map determines which Livelink user is assigned to a User task. Note The User and Initiator task types do not set the WAPI.MAPTASK_FLAG_AUTODONE or WAPI.MAPTASK_FLAG_MILESTONE flags. A workflow task type which sets the WAPI.MAPTASK_FLAG_AUTODONE flag and enables a special callback script. This callback script tells WAPI whether Livelink will take the TRUE route, the FALSE route, or the LOOPBACK route to continue the workflow. The Evaluate task type asks the workflow's data types to return their data, and then compares the data with the evaluation criteria stored for the task type to determine which route to take. A workflow task type which sets the WAPI.MAPTASK_FLAG_AUTODONE and WAPI.MAPTASK_FLAG_MILESTONE flags. When the workflow reaches a Milestone task type, the task is marked finished as soon as it becomes ready. This task tells WAPI to store and calculate a milestone date, by which the task is scheduled to be complete. A workflow task type which uses a Submap callback script to start a subworkflow. The task type lets the creator of a workflow map choose a second workflow map from the Livelink database. This workflow map is initiated as a sub-workflow of the main workflow map. When a Submap task is reached, a Submap callback script locates the selected sub-workflow map node in Livelink, reads the sub-workflow map definition, stores the definition in WAPI, and initiates it as a subworkflow process. Callback scripts are also used to set up the data that is passed into the sub-workflow from the main workflow and the data that is returned to the main workflow when the sub-workflow finishes. Note This task type does not set the WAPI.MAPTASK_FLAG_AUTODONE or the WAPI.MAPTASK_FLAG_MILESTONE flags.

Evaluate

Milestone

Submap

Many of the task types that you define in a workflow map use callback scripts to perform their operations. For example, the One Level Expand and Full Expand options in the Group Options list (General tab) for the User or Initiator task type, use callback scripts to determine which workflow participants must work on the corresponding User or Initiator task. These options use a callback script to launch a sub-workflow that assigns the task to the appropriate members of the group (depending on the option that you choose). The Member Accept option does not use a callback script to perform its function. It leaves the task assigned to a group. Livelink knows that when a task is assigned to an entire group, one member of the group must accept the task before work on the task can begin.

The Livelink Workflow Module

17

Understanding Data Types in Livelink Workflow

Understanding Data Types in Livelink Workflow


In Livelink, workflows route data to workflow participants and instruct those participants to perform a certain type of task. Because many different types of data can be routed in workflows, the workflow data is packaged in self-sufficient objects. This means that Livelink is not aware of the data types that flow through the workflow system and makes no assumptions about the format or storage methods for the data. In fact, workflows can even route data that is not stored in the Livelink database. For example, a workflow can route legacy data that is stored outside of the Livelink databaseas long as the data can be accessed. In this case, the data itself does not move through the workflow; instead, Livelink routes pointers to the data. The self-sufficient objects which control the data that is routed in workflows know how to save, retrieve, and display their own data. In fact, a workflow only knows two things about the data it routes: Identifier information (type and subtype integers), which allows the workflow to call scripts contained in the self-sufficient object Information specified by the data type, which allows the object to access the correct data for the workflow

18

Developers Guide for Extending Livelink Workflow

Understanding Workflow Types in Livelink Workflow

Understanding Workflow Types in Livelink Workflow


Livelink uses two different types of maps to create and process workflows: the Livelink Workflow Map, which is used for painting the workflow process and the WAPIMAP, which is the route definition used by WAPI.

The Livelink Workflow Map


The Livelink Workflow Map is a definition of the workflow map that is recognized by the Workflow Painter and stored as a Record data type for easy manipulation within OScript. It contains fields that correspond to the WAPIMAP database table columns and other fields that are used for temporary storage when the workflow map is being painted and before it is initiated. When you create a workflow map in Livelink, Livelink actually creates an empty document item. This empty document item is represented by a workflow map node in Livelink. When you paint the workflow map, you define the Livelink Workflow Map, which is stored as a Record data type. If you save the painted workflow map, the Record that represents the Livelink Workflow Map definition is added (in BLOB format) as a new version to the document item in Livelink. Each time you modify and save the workflow map, a new version of the workflow map definition is added to the document item. If the workflow map that you paint in Livelink contains attachments (specified in the Attachments work package data type), an attachment folder is stored as a child of the workflow map node in Livelink. Note Because workflow maps are stored as document items in the Livelink database, they can take advantage of Livelinks document management features, such as permissions and version control. When a workflow map is opened in the Workflow Painter, a copy of the workflow map node is temporarily stored in a hidden folder. The changes that you make to a workflow map in the Workflow Painter are stored in the temporary copy of the workflow map nodeuntil you save them. When you save the changes you made in the Workflow Painter, the temporary copy of the workflow map node is added to the original node as a new version. The temporary version of the workflow map node is stored in the Livelink database for seven days, which allows you to return to a map that is in the process of being painted (but not yet saved) within seven days.

The Livelink Workflow Module

19

Understanding Workflow Types in Livelink Workflow

The WAPIMAP
When a workflow map is initiated, the Livelink Workflow Map definition is used to create the WAPIMAP. The WAPIMAP stores the routing information that is used by WAPI to control the workflow route. This map definition is stored in the WMap and WMapTask database tables and is used throughout the life of the workflow. At the time of initiation, the Livelink Workflow Map and the WAPIMAP are completely separateany changes made to the painted Livelink Workflow Map do not affect the active workflows that were initiated from the Livelink Workflow Map definition. This means that if you change the route defined in a workflow map definition, the changes do not affect the route of those workflows that have already been initiated. Similarly, any changes made to a specific workflows WAPIMAP after initiation do not affect the Livelink Workflow Map.

20

Developers Guide for Extending Livelink Workflow

Understanding Callback Events in Livelink Workflow

Understanding Callback Events in Livelink Workflow


Callback events are workflow events or actions that tell WAPI to run certain scripts at specific times in the execution of a workflow map. When you add a callback event to a Livelink workflow, you instruct the workflow to perform an operation when a specific workflow event occurs. Callback event data is stored in special columns in the WAPI database tables. Each of these columns is also associated with a specific workflow event. For example, one column is associated with initiating a workflow, another is associated with deleting a workflow, another is associated with completing a workflow, and so on. You store the callback event data in the WAPI database column that is associated with the workflow event that you want to initiate the callback event during the execution of the workflow. Then, when the workflow is initiated in Livelink, Livelink checks the WAPI database columns to determine if and when callback events should occur. Tip You can identify the columns in the WAPI database table that store callback event data by their nameswhich all end in CB. For example, if you want a callback event to occur when a specific workflow step becomes ready, you store the callback event data in the WAPI database table column that is associated with a workflow step becoming ready. Then, when Livelink reaches that step in the workflow, the callback event occurs.

The Livelink Workflow Module

21

Understanding Callback Events in Livelink Workflow

Figure 2-5: Executing Callback Scripts

When Livelink encounters callback event data in a WAPI database column, it calls a script that is stored in the WAPISESSION object. This script examines the workflow event that triggered the callback event (for example, the step becoming ready), and then calls another script named GenericCB(). Note The GenericCB() script is not called if the callback event was triggered when a workflow participant added a row to an audit trail.

22

Developers Guide for Extending Livelink Workflow

Understanding Callback Events in Livelink Workflow

The GenericCB() script examines the callback event data that is stored in the corresponding column of the WAPI database table. This data must be formatted as a list of lists, where each sub-list contains two elements: an integer and the additional data that is required to execute the callback event. The GenericCB() script examines the integer element and does one of two things (based on its value). If the integer is a value between 1 and 99, a standard workflow operation is performed. If the integer is a value outside of this range, the GenericCB() script calls another script named CustomCB(), indicating that a custom callback event is present in the Livelink workflow. The CustomCB() script begins loading all of the custom objects that are registered in WAPI and calling their CBExecute() scripts. When the CustomCB() script calls a CBExecute() script that matches the value of the integer associated with the callback event, it runs that CBExecute() script and returns an Assoc. The CBExecute() script performs the custom operations associated with the workflow step. Note One of the fields in the Assoc is called Handled. If Handled is set to TRUE, it indicates that the operation is being handled or performed. If Handled is set to FALSE, the process continues until the operation can be handled or performed.

Event Trigger Scripts


If you want to use callback events to add functionality to Livelink, but you do not want to create a supporting interfaceor if you want to add functionality that does not require an interfaceyou can use special classes of callback events called event trigger scripts. Event trigger scripts provide an interface to callback events in Livelink. When you add event trigger scripts to Livelink, this interface (the Event Scripts tab) becomes available in the Livelink interfaceallowing the creator of a workflow map to determine which operations to associate with the particular workflow events that occur in the execution of a workflow map. There are three types of event trigger scripts: general event trigger scripts, performer event trigger scripts, and submap event trigger scripts. For more information about event trigger scripts, see Adding Event Trigger Scripts, page 141. Note All event trigger scripts are contained in a transaction, along with the workflow event that triggered them. This means that if you create an event trigger script that is supposed to run at workflow initiation time and the script returns an error, the workflow initiation also fails. If an event trigger script is attached to a step-ready event and fails, the stepready event also fails.

The Livelink Workflow Module

23

Understanding Callback Events in Livelink Workflow

24

Developers Guide for Extending Livelink Workflow

Chapter Three

Setting Up a Custom Module


You can extend the functionality of the Livelink Workflow module by adding task types, data types, workflow types, or event trigger scripts that perform customized operations. To extend the functionality of the Livelink Workflow module in this way, you must create a custom module that stores the new functionality you are adding. After it is installed, this custom module works with the Livelink Workflow module to extend the workflow capabilities of Livelink. Always store the custom functionality that you create in a module that can be installed and uninstalled separately from all other Livelink modules. If you modify an original Livelink module (for example, the Livelink Workflow module), future updates to the module will automatically overwrite your changes. Note For more information about creating a Livelink module, see the Livelink Module Development Guide. For specific information about using the Livelink Builder, see the Livelink Builder Developers Guide. In this chapter, you will learn how to: Create the modules directory structure Create an OSpace Create a Configure request handler and request handler group for the module

25

Setting up the Custmod Module

Setting up the Custmod Module


Before you can add a new task type, data type, workflow type, or callback event trigger script to a Livelink workflow, you must create a module which stores the new functionality. This module acts as a shell for the new functionality and can be installed or uninstalled independently of other Livelink modules. The module that you create in this chapter is named custmod.

Creating the Module's Directory Structure


You begin setting up a module by creating the modules directory structure in the /staging directory of your primary Livelink installation (for example, c:/opentext/staging). To create the module directory structure: 1. Create a new directory in the /staging directory of your primary Livelink installation, and name it custmod_1_0_0. The module name (custmod) is followed by the major version number, the minor version number, and the revision number, respectively. 2. In the /custmod_1_0_0 directory, create three new directories and name them html, ospace, and support.

Note You can also create an optional directory named help in your /custmod_1_0_0 directory. The /help directory is used to store the HTML help files you may want to include with your custom module.

Creating an OSpace
After you create the directory structure for your custom module, you can create the OSpace that will contain the functionality of the module. All OSpaces have the .oll file extension. To create the custmod OSpace: 1. In the Livelink Builder, click New OSpace on the OSpace menu. 2. Name the OSpace custmod.oll, and save it in your custom module's /ospace directory (for example, c:/opentext/staging/custmod_1_0_0/ospace).

26

Developers Guide for Extending Livelink Workflow

Setting up the Custmod Module

Configuring the Module


Each Livelink module contains a WebModule object which stores the configuration information for the module. The WebModule object defines the name of the module as it appears on the Livelink administration pages, the internal name of the module, the OSpaces contained in the module, the major version number, minor version number, build level, and revision number of the module, the modules dependencies, and the script that is necessary to create a .ini file for the module. To configure the module: 1. Orphan WebDsp:WebModule in the custmod OSpace of your custom module, and name it CustWebModule. 2. In the CustWebModule object, set the value of the fEnabled feature to TRUE. When set to TRUE, this feature allows Livelink to register the CustWebModule object in Kernel:KernelRoot:SubSystem:ModuleSubSystem. 3. Set the value of the fName feature to "Custom Module". This feature specifies the name that is displayed on the Livelink administration pages when you install, upgrade, or uninstall your custom module. 4. Set the value of the fModuleName feature to custmod. This feature stores the internal name for your module. The value must be in lowercase letters and must match the name you used when you created your module directory structure. 5. Set the value of the fOSpaces feature to {'custmod'}. This feature lists the OSpaces that are contained in your custom module. 6. Set the value of the fVersion feature to {'1','0','d','0'}. This feature specifies the major version number, minor version number, build level (b for beta, r for revision, and d for development), and revision number of your custom module. The major version number, minor version number, and revision number must match those you used when you created the modules directory structure. The build level is for your personal reference only. 7. In the Custmod Globals object, run the BuildOSpace() script.

Setting Up a Custom Module

27

Setting up the Custmod Module

Orphaning the Configure Request Handler Object


The Configure request handler object is used to install, uninstall, and upgrade your custom module. This object defines the name of your module, the schema modifications required by your module, and the custom setup steps that your module needs to perform. To orphan the Configure request handler object and modify its features: 1. Orphan WebAdmin:AdminRequestHandler:Configure in the custmod OSpace of your custom module, and name it Configure. 2. In the Configure object that you just orphaned, set the fEnabled feature to TRUE. 3. Set the value of the fFuncPrefix feature to custmod.

Setting Up the Query String and the module.ini File


After you orphan the Configure request handler, you can set up the query string that calls the request handler during installation. When this is complete, you can create the module.ini file, which is required to install, upgrade, and uninstall the custom module. To set up the query string and the module.ini file: 1. In the CustWebModule object, set the value of the fSetUpQueryString feature to func=custmod.configure&module=custmod&nextUrl=%1. 2. In the Custmod Globals object, run the BuildOSpace() script. 3. In the CustWebModule object, run the 0DumpModuleConfigToFile() script and save the custmod.ini file in the /custmod_1_0_0 directory (the root of your modules directory structure).

Orphaning a RequestHandlerGroup Object


The RequestHandlerGroup object keeps track of all the request handlers in your custmod OSpace. This allows the Configure request handler object that you just created to be registered in the RequestHandlerSubsystem. To create a RequestHandlerGroup object: 1. Orphan WebDSP:WebDspRoot:RequestHandlerGroup in the custmod OSpace of your custom module, and name it custmod RequestHandlerGroup. 2. In the custmod RequestHandlerGroup object, set the fEnabled feature to TRUE. 3. Run the SetRequestHandlers() script. 4. In the Custmod Globals object, run the BuildOSpace() script. 5. Save and export the custmod OSpace, and then exit Livelink Builder.

28

Developers Guide for Extending Livelink Workflow

Setting up the Custmod Module

You can now start your Livelink Intranet service and install your custom module in Livelink. For more information about installing modules, see the Livelink Installation Guide. Tip You can set the value of the changeStateOspaces variable in the WebBuilder.lxe file to CUSTMOD (all uppercase letters), to open the custmod OSpace in an unlocked state in the Livelink Builder. The WebBuilder.lxe file is stored in the main directory of your primary Livelink installation (for example, c:/opentext).

Setting Up a Custom Module

29

Completing the Custmod Module

Completing the Custmod Module


Now that you have set up the custmod module, installed it, and prepared it for use in Livelink, you can begin to create the functionality required to add a new task type, data type, workflow type, or callback event trigger script to a Livelink workflow. The following chapters describe how to complete the custmod module: Chapter Four, Adding New Task Types, describes how to add a new type of task to your module. In this chapter, you will add a task type that lets the creators of workflow maps customize the interface that is presented to workflow participants and the operations that occur when the workflow participant works on the task in Livelink. Chapter Five, Adding New Data Types, describes how to add new types of data to your module. In this chapter, you will add a data type that creates a series of workflow attributes and arranges them on custom tabs that become available to workflow participants when they work on the corresponding tasks. Chapter Six, Adding New Workflow Types, describes how to add new types of workflows to your module. In this chapter, you will create a custom workflow that appends the date stamp to the title of the workflow when it is initiated in Livelink. This workflow type also runs a particular callback script when it is initiated. Chapter Seven, Adding Event Trigger Scripts, describes how to add event trigger scripts to your module. In this chapter, you will create three event trigger scripts that perform different functions when different workflow events occur.

Each of these chapters provides you with general information for incorporating new workflow functionality. This general information is supported by a specific example of how to apply the functionalitycomplete with code samples. Tip Because the examples in each chapter can become quite long and complex, consider cutting and pasting the code samples into the scripts you create in Livelink Builder, when working on the examples. Typing the code samples manually is very time-consuming.

30

Developers Guide for Extending Livelink Workflow

Chapter Four

Adding New Task Types


New task types let Livelink users add custom steps to the workflow maps that they create in Livelink. You add a new task type to Livelink when you want to customize the interface and operations presented to workflow participants for particular types of tasks in a workflow. When you add a new task type, you design the interface that is presented to the performer of a workflow task and define the functionality that the new interface provides. For example, you can create a task type that lets the creator of a workflow map customize the interface and operations that are presented to workflow participants when they work on taskswithout using the Livelink Builder. This task type could allow the creators of workflow maps to attach HTML templates that define an interface to the tasks. The creators of workflow maps could also attach callback scripts that perform custom operations. Then, different templates and scripts could be associated with different tasks of this task type in a workflow. To add a new task type, you must orphan the following objects:
WFMain:WFRoot:WFObjectTypes:WFTaskTypes:StandardTasks, WebFP:WebWfpRoot:WFTask, and WebWork:WebWorkRoot:WFTask. The WFMain:WFRoot:WFObjectTypes:WFTaskTypes:StandardTasks object is the API object for the task type. The WebFP:WebWfpRoot:WFTask object controls the information

required by the Workflow Painter to add the task to a workflow map. The WebWork:WebWorkRoot:WFTask object controls the task when a workflow participant is working on it and when it is displayed on the Detailed Status page in Livelink. In this chapter, you will learn how to: Define a task types API object, Workflow Painter information, and status information Add a new task type to your custom module

31

Defining the Task Types API Object

Defining the Task Types API Object


You begin creating a task type by defining its API object. This object contains features and scripts that are required for the operation of a workflow task type and is named StandardTasks. You can find the StandardTasks object in WFMain:WFRoot:WFObjectTypes:WFTaskTypes. Notes The StandardTasks object acts as a class object that you can use to create many different task types. You can also access the StandardTasks object using the Livelink API. For more information, see the Livelink API Developers Reference. For each task type that you create, you can modify the following features.
Table 4-1: Features Associated With the StandardTasks Object Feature
fSubType

Description Stores a unique integer value that works with the fType feature to identify the object. The following objects also contain fSubType features: The WFTask object, which is orphaned when you define the Workflow Painter information The WFTask object, which is orphaned when you define the status and display of the task The values of the fSubType features for the WFTask objects must be the same as the value of this fSubType feature. Stores the name of the task. By default, this value is displayed when you position your cursor over the tasks icon in the Workflow Painter. Note For more information about the Step Icon Palette, see The Workflow Painter, in Chapter One, Introduction. Stores a unique integer value that works with the fSubType feature to identify the object. The following objects also contain fType features: The WFTask object, which is orphaned when you define the Workflow Painter information The WFTask object, which is orphaned when you define the status and display of the task The values of the fType features for the WFTask objects must be the same as the value of this fType feature.

fTaskName

fType

Note When adding task types, Open Text recommends setting the fSubType values to 1 and the fType values to an integer higher than 10.

32

Developers Guide for Extending Livelink Workflow

Defining the Task Types API Object

For each task type that you create, you can modify the following scripts.
Table 4-2: Scripts Associated With the StandardTasks Object Script
GetPainterInfo()

Description Retrieves the text that is displayed as a title below the task icon in the Workflow Painter. The default value is the title of the task, as specified in the fTaskName feature. If you do not want to display the title of the task in the Workflow Painter, you can modify this script. Note In most cases, you will not modify the GetPainterInfo() script. Stores callback scripts and any additional data that is required by the task type throughout the execution of a workflow. This script is called when the workflow is initiated. You can use this script to store the type, subtype, and permission information for the task type in the MAPTASK_USERDATA column of the WMapTask table. The type and subtype information must be present in the WMapTask table so that Livelink can distinguish between different types of tasks. Stores the default data that must be present before Livelink can recognize the task type (that is, the type and subtype). This script also stores the title, performer ID, and any other information that you want to appear as default values on the Step Definition page when a task of this type is edited in Livelink. You can also use this script to store task information that must be present if the creator of a workflow map does not edit the task on the Step Definition page before the workflow is initiated. Converts a workflow task that has been prepared for initiation to a workflow map definition task. This script transfers data from the WAPIMAPTASK to the correct fields in the map definition task to convert the workflow task back to a format that the Workflow Painter recognizes. Note The WAPIMAPTASK data type is used to define the object handle of a workflow map task that is stored in the WAPI database. It is used in WAPI functions for all task manipulation operations. This script is called when a workflow manager attempts to modify a workflow that has already been initiated. It undoes the changes that were made by the ReadyTaskForInitiation() script (because the ReadyTaskForInitiation() script is called again when the workflow manager saves their changes and the workflow process is resumed).

ReadyTaskForInitiation()

SetTaskDefaults()

SetTaskRecFromMap()

Adding New Task Types

33

Defining the Workflow Painter Information

Defining the Workflow Painter Information


After you define the task type's API object, you must provide the Workflow Painter with the information it requires to display the task and manipulate the data provided by the creator of the workflow map. You define the Workflow Painter information using the WFTask object. You can find this object in WebWFP:WebWFPRoot. For each task type that you create, you can modify the following features.
Table 4-3: Features Associated With the WFTask Object Feature
fSubType

Description Stores a unique integer that works with the fType feature to identify the task type. The following objects also contain fSubType features: The StandardTasks object, which is orphaned when you define the API object for the task type The WFTask object, which is orphaned when you define the status and display of the task The values of the fSubType features for the StandardTasks object and the WFTask object must be the same as the value of this fSubType feature. Stores a unique integer that works with the fSubType feature to identify the task type. The following objects also contain fType features: The StandardTasks object, which is orphaned when you define the API object for the task The WFTask object, which is orphaned when you define the status and display of the task The values of the fType features for the StandardTasks object and the WFTask object must be the same as the value of this fType feature.

fType

Note When adding task types, Open Text recommends setting the fSubType values to 1 and the fType values to an integer higher than 10. For each task type that you create, you can modify the following scripts.
Table 4-4: Scripts Associated With the WFTask Object Script
GetMapData()

Description Tells Livelink what to display when the creator of a workflow map edits this type of task. This script identifies the location and name of the HTML file to display for the Step Definition page for this task type. Note You can maintain consistency with other Livelink task pages, by using the commonedittask.html file. This file is located in the /webwfp_8_1_x/html directory.

34

Developers Guide for Extending Livelink Workflow

Defining the Workflow Painter Information

Table 4-4: Scripts Associated With the WFTask Object Script


PutMapData()

Description Saves the information that the creator of a workflow map enters on the Step Definition page when editing the task in the Workflow Painter. This data is saved in the workflow map definition.

In addition to the features and scripts that you modify to define the Workflow Painter information for a task type, you must create the HTML file for this tasks Step Definition page. You access the Step Definition page in Livelink by right-clicking a task in the Workflow Painter, and then clicking Edit or by double-clicking the task in the Workflow Painter.

Adding New Task Types

35

Defining the Status and Display of the Task

Defining the Status and Display of the Task


After you define the API object and Workflow Painter information for the task type, you must define the information that handles the task when a workflow participant is working on it and when it is displayed on the Detailed Status page in Livelink. You define this information using the WFTask object. You can find this object in WebWork:WebWorkRoot. For each task type that you create, you can modify the following features.
Table 4-5: Features Associated With the WFTask Object Feature
fPaletteTask

Description Stores a Boolean value. If the value of this feature is set to TRUE, an icon representing the task type can be displayed on the Step Icon Palette in the Workflow Painter. If the value of this feature is set to FALSE, an icon representing the task type cannot be displayed on the Step Icon Palette in the Workflow Painter. Note For more information about the Step Icon Palette, see The Workflow Painter, page 4. Stores a unique integer that works with the fType feature to identify the object. The following objects also contain fSubType features: The StandardTasks object, which is orphaned when you define the API object for the task type The WFTask object, which is orphaned when you define the Workflow Painter information for the task type The values of the fSubType features for the StandardTasks object and the WFTask object must be the same as the value of this fSubType feature. Stores the name of the image that you want to represent the task on the Step Icon Palette in the Workflow Painter. This image must be stored in your modules /support directory (for example, c:/opentext/module/custmod_1_0_0/support) and in the Livelink /support directory (for example, c:/opentext/support). Stores a unique integer that works with the fSubType feature to identify the object. The following objects also contain fType features: The StandardTasks object, which is orphaned when you define the API object for the task type The WFTask object, which is orphaned when you define the Workflow Painter information for the task type The values of the fType features for the StandardTasks object and the WFTask object must be the same as the value of this fType feature.

fSubType

fTaskGif

fType

Note When adding task types, Open Text recommends setting the fSubType values to 1 and the fType values to an integer higher than 10.

36

Developers Guide for Extending Livelink Workflow

Defining the Status and Display of the Task

For each task type that you create, you can modify the following scripts.
Table 4-6: Scripts Associated With the WFTask Object Script
GetDisplayPerformerInfo()

Description Examines the workflow and determines if the task has been assigned to a workflow participant. If it has, this script retrieves the name and ID of the workflow participant to whom the task has been assigned. This information is displayed when the workflow participant attempts to reassign the task in Livelink. Defines the information (for example, the name, type, subtype, icon, or module) that the Workflow Painter needs to know about the task type. This includes all of the information that the Workflow Painter requires to perform operations on the task. Returns a list of Assocs that define the menu commands that appear when you right-click this task types icon in the Workflow Painter. You can set the Boolean value of the viewonly variable to TRUE if you want to display a standard set of menu commands that do not let the creator of a workflow map edit the task type in the Workflow Painter. You can set the Boolean value of the viewonly variable to FALSE if you want to display a standard set of menu commands that let the creator of a workflow map edit the task type in the Workflow Painter. Whether you set the Boolean value of the viewonly variable to TRUE or FALSE, you can use this script to override the menu commands that are displayed in the Livelink interface as needed. Retrieves the information that is displayed when a workflow participant clicks the task name on the Step List tab or doubleclicks the task icon on the Map View tab of the Detailed Status page. This script returns Undefined (indicating that there is no information of this type relevant to the active workflow) or it returns an Assoc. If the script returns an Assoc, the fields in the Assoc must contain the information that the HTML file (specified by retval.HTMLFile) needs to be displayed in Livelink. The Assoc contains OK if the data needed for display is retrieved; otherwise, it contains a string named ErrMsg. Retrieves the information that is displayed when a workflow participant clicks the task name on the Tasks page in their Personal Workspace. This script returns Undefined (indicating that there is no information of this type relevant to the active workflow) or it returns an Assoc. If the function returns an Assoc, the fields in the Assoc must contain the information that the HTML file (specified by retval.HTMLFile) needs to be displayed in Livelink. The Assoc contains OK if the data needed for display is retrieved; otherwise, it contains a string named ErrMsg.

GetPainterInfo()

GetPainterMenu()

GetStatusDisplay()

GetTaskEditData()

Adding New Task Types

37

Defining the Status and Display of the Task

Table 4-6: Scripts Associated With the WFTask Object Script


NewPerformer()

Description Updates task information if the task is reassigned in Livelink. For example, if the performers name is also the name of the task in the Workflow Painter, this script updates the names. This script is called when this type of task is reassigned in Livelink so that all performer-dependent information is updated.

Depending on the complexity of the task types that you add to Livelink, you may have to provide additional functionality. For example, if you create a task type that allows the creators of workflow maps to attach custom callback scripts to particular workflow events that are associated with that task type, you may need to define the objects responsible for handling those callback scripts in Livelink. For more information, see The WFCustomScriptPkg Object, page 79. If you add a complex task type to the custom module that you are creating, you may need to create custom request handlers that perform the operations associated with the task type. For example, the form task type (installed with the Livelink Forms module) uses request handlers to display the data in the form, to retrieve data from the form, and to pass the data to the workflow. In this case, the request handlers set up and manipulate all of the forms data correctly. For information about creating the request handlers used by the form task type, see Request Handlers, page 195.

38

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

Example 1: Adding the Custom Display Task Type


The following example describes how to add a new type of task to Livelink. When the creator of a workflow adds this type of task to their workflow, they can do the following: Attach callback scripts to particular workflow events that are associated with the task. These callback scripts define the custom operations that can occur when a task of this type becomes ready or is complete. Attach HTML templates to the task. These HTML templates define the interface that is presented to workflow participants when they work on tasks of this type.

The Step Definition page for this task looks just like the User Step Definition page, but contains two new fields: the Script to run field and the Template to use field. The creator of a workflow map can choose a callback script to run for the task from the Script to run field. This callback script can perform any custom operation and can run when the task becomes ready or when the task is complete. The creator of a workflow map can also choose an HTML template file to display for the task from the Template to use field. The HTML template defines the interface that is displayed when a workflow participant works on that particular task in Livelink. The creator of a workflow map can create many different tasks of this type, and can choose different callback scripts and HTML templates for each, so that each task looks and behaves differently. This lets the creators of workflow maps customize the workflow interface and operationswithout actually changing any code using the Livelink Builder. The HTML templates and callback scripts used for this task type are stored in two new folders in your custom module directory structure: the /templates folder and the /scripts folder. The HTML templates that you store in the /templates folder can be selected from the Template to use field on a Step Definition page for this type of task. The callback scripts that you store in the /scripts folder can be selected from the Script to run field on a Step Definition page for this type of task.

The CustomDisplayAPI Object


You begin adding the custom display task type to Livelink by defining the API object. To define the API object for this task: 1. Orphan WFMain:WFRoot:WFObjectTypes:WFTaskTypes:StandardTasks in the custmod OSpace in your custom module, and name it CustomDisplayAPI. 2. In the CustomDisplayAPI object, change the fSubType feature to an Integer/Real type, and set its value to 1. The fSubType feature stores a unique integer that works with the fType feature to identify the object. 3. Ensure that the fType feature is an Integer/Real type, and set its value to 11.

Adding New Task Types

39

Example 1: Adding the Custom Display Task Type

The fType feature stores a unique integer that works with the fSubType feature to identify the object. 4. Ensure that the fTaskName feature is a String type, and set its value to Custom
Display Task.

The fTaskName feature stores the name of the task type as it is displayed in the Workflow Painter. 5. Create a script, and name it CreateReviewMapRec(). For more information about the CreateReviewMapRec() script, see CreateReviewMapRec(), on this page. 6. Override the following scripts:
GetPainterInfo() ReadyTaskForInitiation() SetTaskDefaults() SetTaskRecFromMapTask()

For more information about these scripts, see the code samples that follow. You have created the API object. Now you must customize it for the custom display task type.

CreateReviewMapRec()
The following code sample describes how to create a script that returns the workflow map definition that is used when a task is sent to a Livelink user or group for review. This workflow map definition is created on-the-fly and is initiated as a sub-workflow task.
Function Record CreateReviewMapRec( \ Object prgCtx, \ Record user, \ Integer groupFlags, \ Assoc taskData, \ RecArray packages ) Boolean expandFlag Dynamic userRecords List fields Integer i Record r Record newTask String fieldName Integer taskID = 1 Point pos = Point( 100, 50 ) //Create a generic map record that contains all of the workflow //map information. Record mapRec = $WFMain.WFMapPkg.CreateMapRec()

40

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

//Add a Start task and a User task to the workflow map //definition. AddNewTask( \ prgCtx, \ mapRec, \ $WFMain.WFTaskSubsystem.GetItemByName( 'WFStartTask' ), \ Undefined, \ Point( 20, 50 ) ) //Determine whether this task is assigned to a group. If it is, //determine whether the task should be assigned to each member of //the group or to the group as a whole. If the task is assigned //to the group as a whole, the first group member to accept the //task is responsible for completing it. if ( ( user.TYPE != UAPI.USER ) && ( IsSet( groupFlags ) ) && \ ( IsDefined( groupFlags ) ) && ( groupFlags != \ $WFMain.WFConst.kWFGroupStandard ) ) expandFlag = ( groupFlags == \ $WFMain.WFConst.kWFGroupExpandFull ) userRecords = $LLIAPI.UsersPkg.ExpandGroup( prgCtx, user, \ expandFlag ) if ( userRecords.OK ) userRecords = userRecords.Members else userRecords = { user } end else userRecords = { user } end if ( Length( userRecords ) < 1 ) newTask = user else newTask = userRecords[ 1 ] end //Assign a custom display task to each workflow participant to //which the information must be sent for review. newTask = AddNewTask( \ prgCtx, \ mapRec, \ $WFMain.WFTaskSubsystem.GetItemByname( \ 'CustomDisplay' ), \ newTask, \ pos ) //Set the Group Options value to Member Accept (standard). taskData.EXATTS.GroupFlags = $WFMain.WFConst.kWFGroupStandard //Copy the data fields from the original task to the new sub//workflow task. The allows the sub-workflow task to be able to //modify the data in the same way that the original task could //modify the data. fields = { 'PAINTER', 'USERDATA' }

Adding New Task Types

41

Example 1: Adding the Custom Display Task Type

for fieldName in Assoc.Keys( taskData ) if ( RecArray.IsColumn( newTask, fieldName ) && !( Str.Upper( \ fieldName ) in fields ) ) newTask.( fieldName ) = taskData.( fieldName ) end end newTask.PERFORMERID = userRecords[ 1 ].ID for i = 2 to Length( userRecords ) newTask = $LLIAPI.RecArrayPkg.CopyRec( mapRec.TASKS[ 2 ] ) //Generate a new position for the next task. pos += Point( 0, 75 ) newTask.PAINTER = { pos, newTask.PAINTER[ 2 ] } newTask.PERFORMERID = userRecords[ i ].ID RecArray.AddRecord( mapRec.TASKS, \ RecArray.GetRecord( newTask ) ) end //Add a link between the two tasks. for r in mapRec.TASKS if ( taskID > 1 ) $WFMain.WFMapPkg.AddLinkRecord( \ mapRec.TASKS, \ mapRec.LINKS, \ mapRec.TASKS[ 1 ], \ mapRec.TASKS[ taskID ], \ 0 ) end taskID += 1 end //Add the work package from the main workflow to the sub//workflow. for r in packages RecArray.AddRecord( mapRec.WORK_PACKAGES, \ RecArray.GetRecord( r ) ) end return( mapRec ) end Function Record AddNewTask( \ Object prgCtx, \ Record mapRec , \ Object taskType, \ Dynamic context, \ Point iconPos ) Dynamic val List data Record taskRec //Add a new Record to the tasks RecArray.

42

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

taskRec = $LLIAPI.RecArrayPkg.NewRecord( mapRec.TASKS ) taskType.SetTaskDefaults( prgCtx, taskRec, context ) val = taskType.GetPainterInfo( prgCtx, taskRec, context ) taskRec.PAINTER = { iconPos, val } return( taskRec ) end

GetPainterInfo()
The following code sample describes the default values of the GetPainterInfo() script. This is the information that the Workflow Painter needs to know about the task type.
Function Dynamic GetPainterInfo( \ Object prgCtx, \ Record task, \ Dynamic context = Undefined ) Dynamic retVal if ( IsDefined( context ) ) retVal = context.ID end return( retVal ) end

ReadyTaskForInitiation()
The following code sample describes how to prepare a custom display task for initiation in Livelink. It stores callback scripts and any other additional data that is required by the custom display task type throughout the execution of a workflow.
Function Boolean ReadyTaskForInitiation( \ Object prgCtx, \ Record r, \ RecArray workPkg ) Assoc expandInfo List cbInfo List prevCBs RecArray user Boolean initiatorStep = False Boolean success = True Object const = $WFMain.WFConst Object uSession = prgCtx.USession() Record userData = $WFMain.WFMapPkg.CreateTaskUserDataRec() userData.TYPE = r.TYPE userData.SUBTYPE = r.SUBTYPE userData.PERMFLAGS = r.USERFLAGS //Add a standard Done callback script. This callback script is //called when the task is completed and the workflow is routed to //the next workflow participant. The Done callback script //instructs all of the data types in the workflow to make a copy //of the information that was entered for the task. The data type //stores the information in a separate table so that each version //is available at the end of a workflow.

Adding New Task Types

43

Example 1: Adding the Custom Display Task Type

if ( IsDefined( r.DONECB ) ) cbInfo = { @r.DONECB, { const.kCBSetTaskDoneData, Undefined } } else cbInfo = { { const.kCBSetTaskDoneData, Undefined } } end //If the creator of the workflow map has selected a callback //script from the Script to run field on the Custom Display Step //Definition page for this task, add the callback script to the //appropriate workflow event (Step Becomes Ready or Step Is Done). if ( IsDefined( r.EXATTS.CustTaskScript ) ) if ( r.EXATTS.RunScript == 'DoneCB' ) cbInfo = { @r.DONECB, { 500, r.EXATTS.CustTaskScript } } else prevCBs = r.READYCB prevCBs = ( IsDefined( prevCBs ) ) ? prevCBs : {} prevCBs = { @prevCBs, { 500, r.EXATTS.CustTaskScript } } r.READYCB = prevCBs end end r.DONECB = cbInfo r.USERDATA = userData //If the performer of the task is a group, add a callback script //that identifies the group name. Then, if the group task is //part of a loopback, the task is reassigned to the whole group //when the route loops back (and not to the individual group //member who initially accepted the task). if ( IsDefined( r.PERFORMERID ) && ( r.PERFORMERID > 0 ) ) user = UAPI.GetByID( uSession.fSession, r.PERFORMERID ) if ( !IsError( user ) ) if ( user[ 1 ].TYPE != UAPI.USER ) prevCBs = r.PERFORMERCB prevCBs = ( IsDefined( prevCBs ) ) ? prevCBs : {} r.PERFORMERCB = { { const.kCBSetGrpStepPerformer, \ r.PERFORMERID }, @prevCBs } end end //If the performer is Undefined, add a callback script that //assigns the task to the initiator of the workflow. elseif ( r.PERFORMERID == 0 ) r.PERFORMERCB = { { const.kCBGetInitiator, Undefined } } initiatorStep = True end //If the performer of the task is a group, and that group should //be expanded so that the task is assigned to all members of the //group, add a Submap callback script. This Submap callback script //creates a sub-workflow that expands the members of the group. if ( !initiatorStep ) if ( IsSet( r.EXATTS.GroupFlags ) && \ ( r.EXATTS.GroupFlags != const.kWFGroupStandard ) ) expandInfo.Type = r.TYPE expandInfo.SubType = r.SUBTYPE expandInfo.Flag = r.EXATTS.GroupFlags

44

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

r.SUBMAPIDCB = { { const.kCBExpandGroup, expandInfo } } end end return( success ) end

SetTaskDefaults()
The following code sample describes how to specify the default information required by Livelink to recognize the custom display task type. This is also where you specify data that must be present if the creator of the workflow map does not edit the task before the workflow is initiated.
Function Void SetTaskDefaults( \ Object prgCtx, \ Record taskRec, \ Dynamic context = Undefined ) //Set values for the default information required by Livelink to //recognize the custom display task type. The fType and fSubType //values store unique integers that identify the task type. taskRec.TYPE = .fType taskRec.SUBTYPE = .fSubType taskRec.EXATTS = Assoc.CreateAssoc( Assoc.NotSetValue() ) taskRec.CUSTOMDATA = Assoc.CreateAssoc( Assoc.NotSetValue() ) taskRec.FLAGS = 0 //Specify the name of the task and the performer ID associated //with the task. These values are displayed as default values on //the Custom Display Step Definition page when a custom display //task is edited for the first time. if ( !IsDefined( context ) ) taskRec.TITLE = Str.String( .fTaskName ) taskRec.PERFORMERID = Undefined else taskRec.TITLE = context.NAME taskRec.PERFORMERID = context.ID end taskRec.EXATTS.GroupFlags = $WFMain.WFConst.kWFGroupStandard //If the creator of the workflow map chose a script from the //Script to run field on the Custom Display Step Definition page //for this task, set the script to run when the task is ready, by //default. taskRec.EXATTS.RunScript = 'ReadyCB' end

Adding New Task Types

45

Example 1: Adding the Custom Display Task Type

SetTaskRecFromMapTask()
The following code sample describes how to convert a custom display task that has been prepared for initiation to a workflow map definition.
Function Void SetTaskRecFromMapTask( \ WAPIMAPTASK task, \ Record r, \ Record taskData ) List Dynamic Object Integer cbInfo data = task.pUserData const = $WFMain.WFConst groupFlag = const.kWFGroupStandard

//Convert the values stored in the WAPIMAPTASK to the //corresponding fields in the map definition task. r.TYPE = data.TYPE r.SUBTYPE = data.SUBTYPE r.USERFLAGS = data.PERMFLAGS r.SUBMAPID = task.pSubMapID r.PERFORMERID = taskData.WORK.SUBWORKTASK_PERFORMERID r.READYCB = task.pReadyCB //Remove the Done callback script. r.DONECB = task.pDoneCB r.DONECB = $WFMain.WFMapPkg.RemoveCBInfoType( r.DONECB, \ const.kCBSetTaskDoneData ) r.KILLCB = task.pKillCB //Remove the Performer callback script. r.PERFORMERCB = task.pPerformerCB r.PERFORMERCB = $WFMain.WFMapPkg.RemoveCBInfoType( \ r.PERFORMERCB, const.kCBSetGrpStepPerformer ) //If present, remove the callback script that assigns the //custom display task to the initiator of the workflow. r.PERFORMERCB = $WFMain.WFMapPkg.RemoveCBInfoType( \ r.PERFORMERCB, const.kCBGetInitiator ) r.CONDITIONCB = task.pConditionCB r.FORM = task.pForm r.PAINTER = task.pPainter r.STARTDATE = task.pStartDate r.DUEDURATION = task.pDueDuration r.DUEDATE = task.pDueDate r.DUETIME = task.pDueTime r.FLAGS = task.pFlags r.TITLE = taskData.WORK.SUBWORKTASK_TITLE r.DESCRIPTION = task.pDescription r.INSTRUCTIONS = task.pInstructions r.PRIORITY = task.pPriority

46

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

//Walk through the Submap callback scripts and search for a //callback script that expands group members. If this type of //callback script is found, determine whether the callback script //expands the group and its subgroups or whether the callback //script expands only the first level of group members. r.SUBMAPIDCB = task.pSubMapIdCB for cbInfo in r.SUBMAPIDCB if ( cbInfo[ 1 ] == const.kCBExpandGroup ) groupFlag = cbInfo[ 2 ].Flag break end end //Remove the callback script that expands group members and store //the expand group flag value in the appropriate field of the //r. EXATTS Assoc. r.SUBMAPIDCB = $WFMain.WFMapPkg.RemoveCBInfoType( \ r.SUBMAPIDCB, const.kCBExpandGroup ) r.EXATTS = Assoc.CreateAssoc( Assoc.NotSetValue() ) r.EXATTS.GroupFlags = groupFlag //Remove the callback scripts that were added to the Step Becomes //Ready or Step Is Done workflow events. for cbInfo in r.DONECB if ( cbInfo[ 1 ] == 500 ) r.EXATTS.CustTaskScript = cbInfo[ 2 ] r.DONECB = $WFMain.WFMapPkg.RemoveCBInfoType( r.DONECB, \ 500 ) break end end for cbInfo in r.READYCB if ( cbInfo[ 1 ] == 500 ) r.EXATTS.CustTaskScript = cbInfo[ 2 ] r.READYCB = $WFMain.WFMapPkg.RemoveCBInfoType( \ r.READYCB, 500 ) break end end end

Adding New Task Types

47

Example 1: Adding the Custom Display Task Type

The CustomDisplayPaint Object


After you define the API object for the task type that you are creating, you must provide the Workflow Painter with the information it requires to display the task and handle the data that the performer of the task provides. To define the Workflow Painter information: 1. Orphan WebWFP:WebWFPRoot:WFTask in the custmod OSpace in your custom module, and name it CustomDisplayPaint. 2. In the CustomDisplayPaint object, change the fSubType feature to an Integer/Real type, and set it to 1. The fSubType feature stores a unique integer that works with the fType feature to identify the object. It must match the value specified for the fSubType feature in the CustomDisplayAPI object. 3. Change the fType feature to an Integer/Real type, and set it to 11. The fType feature stores a unique integer that works with the fSubType feature to identify the object. It must match the value specified for the fType feature in the CustomDisplayAPI object. 4. Override the following scripts:
GetMapData() PutMapData()

For more information about these scripts, see the code samples that follow. 5. Create an HTML file, name it t_user.html, and store it in your modules /html directory (for example, c:/opentext/module/custmod_1_0_0/html). For more information about this HTML file, see t_user.html, page 56. You have created the object necessary to define a task types Workflow Painter information. Now you must provide the code required to customize the information for the custom display task type.

GetMapData()
The following code sample describes how to display the Step Definition page for this task type when the creator of a workflow map edits the task in the Workflow Painter.
function Assoc GetMapData( \ Object prgCtx, \ Integer mapID, \ Integer taskID, \ Record mapRec, \ Record request )

48

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

Assoc a Assoc paneData Assoc performerInfo Assoc retVal Assoc tabPaneInfo Dynamic tmp Integer whichTab List tabList Object obj Record p String label Boolean Integer Record String String String knownUser = False i = 1 taskInfo = mapRec.TASKS[ taskID ] gif = 'guy.gif' name = [WebWFP_HTMLLabel.User] objName = .OSName

whichTab = ( RecArray.IsColumn( request, 'PaneIndex' ) ) ? \ Str.StringToInteger( request.PaneIndex ) : 1 //Specify the commonedittask.html file as the HTML file to //display when the creator of a workflow map edits a custom //display task in the Workflow Painter. Specify the location of //the commonedittask.html file (that is, the webwfp module). retVal.HTMLFile = "commonedittask.html" retVal.ModuleName = 'webwfp' //Create an Assoc named retVal.Data and populate it with the task //and map information, including the ID of the workflow map, the //ID of the task, the URL of the next page to display, and the //header information for the task. retVal.Data = Assoc.CreateAssoc() retVal.Data.MapID = mapID retVal.Data.TaskID = taskID retVal.Data.NextURL = request.NextURL retVal.Data.HeaderArgs = $WebWork.WFPkg.GetHeaderTypeArgs( \ 'PAINT', $WebWork.WFPkg.GetMapName( prgCtx, mapID, \ mapRec ) ) retVal.Data.HeaderLabel = [WebWFP_HTMLLabel.StartStepDefinition] //Create an Assoc named tmp that stores all of the data required //to paint the first tab that appears when the creator of a //workflow map edits the custom display task in the Workflow //Painter (that is, the General tab). tmp = Assoc.CreateAssoc() tmp.Label = [WebWork_HTMLLabel.General] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( request ), i ) tmp.HelpKey = objName tmp.Active = FALSE //This task type uses the commonedittask.html file. This HTML //file expects to be passed a list of tab names, along with a //list of the data to be displayed on each tab. TabList is a list

Adding New Task Types

49

Example 1: Adding the Custom Display Task Type

//of Assocs that identifies each tab to be displayed. There is //another list of Assocs that lists the panes to be displayed //with each tab. This second list of Assocs contains the HTML //information and all other information that the pane needs to //draw itself. tabPaneInfo.TabList = { tmp } a = Assoc.CreateAssoc() a.TaskInfo = taskInfo a.MapID = mapID a.TaskID = taskID - 1 a.NextURL = request.NextURL //Retrieves the name of the performer of the task and a .gif file //that represents the performer type (that is, a user or a //group). If the step is assigned to the initiator of the //workflow, then <Initiator> is returned as the performer's name. if ( IsDefined( taskInfo.PERFORMERID ) ) if ( taskInfo.PERFORMERID == 0 ) name = [WebWFP_Label.LtInitiatorGt] else tmp = UAPI.GetByID( prgCtx.USession().fSession, \ taskInfo.PERFORMERID ) if ( !IsError( tmp ) ) knownUser = True name = tmp[ 1 ].NAME if ( tmp[ 1 ].TYPE != UAPI.USER ) gif = '2-guys.gif' end end end end if ( gif == '2-guys.gif' ) a.Gif = '16group.gif' else a.Gif = '16user.gif' end performerInfo.Name = name performerInfo.Gif = gif performerInfo.KnownUser = knownUser performerInfo.ID = taskInfo.PERFORMERID a.PerformerInfo = performerInfo //Create an Assoc named tmp that stores the name of your custom //module, the HTML file to display, and the data that appears on //the General tab. tmp = Assoc.CreateAssoc() tmp.ModuleName = 'custmod' tmp.HTMLFile = 't_user.html' tmp.Data = a //Set the name of the Step Definition page for this task type. //This page is displayed when the creator of a workflow map edits //a custom display task in the Workflow Painter.

50

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

retVal.Data.HeaderLabel = 'Custom Display Step Definition' tabPaneInfo.PaneList = { tmp } i += 1 //Create the Permissions tab that appears on the Custom Display //Step Definition page for this task type. tmp = Assoc.CreateAssoc() tmp.Label = [WebWFP_Label.Permissions] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( request ), i ) tmp.HelpKey = objName + "." + 'Permissions' // do not XLATE tmp.Active = FALSE tabPaneInfo.TabList = { @tabPaneInfo.TabList, tmp } a = Assoc.CreateAssoc() a.Permissions = taskInfo.USERFLAGS //Create an Assoc named tmp that stores the name of your custom //module, the HTML file to display, and the data that appears on //the Permissions tab. tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwfp' tmp.HTMLFile = 't_perms.html' tmp.Data = a tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, tmp } i += 1 //Set up any data type information that is required for this type //of task. for p in mapRec.WORK_PACKAGES obj = $WebWFP.WFPackageSubsystem.GetItem( { p.TYPE, \ p.SUBTYPE } ) if ( IsDefined( obj ) && IsDefined( p.USERDATA ) ) a = obj.GetTabInfo( prgCtx, request, p.USERDATA, i ) a.Active = FALSE if IsDefined( a.HelpKey ) a.HelpKey = objName + "." + a.HelpKey else a.HelpKey = objName end paneData = obj.GetMapData( prgCtx, taskInfo, p.USERDATA ) if ( IsDefined( paneData ) ) i += 1 tabPaneInfo.TabList = { @tabPaneInfo.TabList, a } tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, paneData } end end end i = Length( tabPaneInfo.TabList ) + 1

Adding New Task Types

51

Example 1: Adding the Custom Display Task Type

//List the callback scripts that fire, if applicable. AssoceventInfo List fields = { 'PERFORMERCB', 'READYCB', 'DONECB', 'KILLCB', \ 'RESURRECTCB' } List events = { [WebWFP_HTMLLabel.AssignStepPerformer], \ [WebWFP_HTMLLabel.StepBecomesReady], \ [WebWFP_HTMLLabel.StepIsDone], \ [WebWFP_HTMLLabel.StepIsKilled], \ [WebWFP_HTMLLabel.StepIsResurrected] } eventInfo.Events = events eventInfo.FieldNames = fields eventInfo = $WFMain.WFMapPkg.GetValidEvents( prgCtx, eventInfo ) if ( eventInfo.NumberOfEvents > 0 ) tmp = Assoc.CreateAssoc() tmp.Label = [WebWFP_HTMLLabel.EventScripts] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( request ), i ) tmp.HelpKey = objName + "." + 'EventScripts' tmp.Active = FALSE tabPaneInfo.TabList = { @tabPaneInfo.TabList, tmp } a = Assoc.CreateAssoc() a.EventInfo = eventInfo a.DataRec = taskInfo tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwfp' tmp.HTMLFile = 't_events.html' tmp.Data = a tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, tmp } end //Store the pane information for the tabs in the data Assoc. Then //set the active tab. By default, the active tab is 1 (or //whichever tab was originally passed into the script). if ( ( whichTab < 2 ) || ( whichTab > Length( \ tabPaneInfo.TabList ) ) ) whichTab = 1 end tabPaneInfo.TabList[ whichTab ].Active = True retVal.Data.TabInfo = tabPaneInfo retVal.Data.Tab = whichTab return( retVal ) end

PutMapData()
The following code sample describes how to save the data that the creator of a workflow map enters on the Custom Display Step Definition page.
function assoc PutMapData( \ Object prgCtx, \ Record mapRec, \ Record taskInfo, \ Record r )

52

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

Assoc paneData Assoc retVal Integer defaultDispo Integer flags Integer i Integer permAndDispositionFlags List dispositions List emptyDispos Object obj Real time Record p Integer count = 0 retVal.OK = TRUE if ( ( r.PaneIndex == 1 ) || ( r.PaneIndex == 0 ) ) //Save the step name. if ( RecArray.IsColumn( r, 'Title' ) ) taskInfo.Title = $LLIAPI.FormatPkg.ValToString( r.title ) end //Save the start date. if ( RecArray.IsColumn( r, 'StartDate' ) ) taskInfo.StartDate = ._CrackDate( r.StartDate ) end //Save the instructions. if ( RecArray.IsColumn( r, 'Instructions' ) ) taskInfo.Instructions = \ $LLIAPI.FormatPkg.ValToString( r.Instructions ) end //Save the callback script that the creator of the workflow map //selects from the Script to run field. if ( IsFeature( r, 'CustTaskScript' ) && ( \ r.CustTaskScript != [WebWFP_HTMLLabel._None_] ) ) taskInfo.ExAtts.CustTaskScript = r.CustTaskScript //Save the information about when to execute the callback //script (that is, the workflow event that triggers the //callback script). taskInfo.ExAtts.RunScript = r.RunScript else taskInfo.ExAtts.CustTaskScript = Undefined end //Save the template that the creator of the workflow map //selects from the Template to use field. if ( IsFeature( r, 'CustTaskTemplate' ) && ( \ r.CustTaskTemplate != [WebWFP_HTMLLabel._None_] ) ) taskInfo.CustomData.CustTaskTemplate = r.CustTaskTemplate else taskInfo.CustomData.CustTaskTemplate = Undefined end

Adding New Task Types

53

Example 1: Adding the Custom Display Task Type

//Save the duration. if ( RecArray.IsColumn( r, 'Duration' ) ) if IsDefined( r.Duration ) && Length( r.Duration ) Boolean inDays = ( r.DurationUnits == "Days" ) time = $LLIAPI.FormatPkg.StringToVal( r.Duration, \ RealType ) if ( Type( time ) != RealType ) retVal.OK = FALSE if inDays retVal.ErrMsg = \ [WebWork_ErrMsg.DurationMustBeANumberOfDays] else retVal.ErrMsg = \ [WebWork_ErrMsg.DurationMustBeANumberOfHours] end else taskInfo.DueDuration = \ $LLIAPI.FormatPkg.ConvertToSeconds( inDays, time ) end else taskInfo.DueDuration = Undefined end end //Save the group options. if RecArray.IsColumn( r, "GroupFlags" ) taskInfo.EXATTS.GroupFlags = Str.StringToInteger( \ r.GROUPFLAGS ) end elseif ( r.PaneIndex == 2 ) //Save the disposition types. for i = 1 to 5 if ( RecArray.IsColumn( r, Str.Format( "disposition_%1", \ i ) ) ) && \ Length( r.( Str.Format( "disposition_%1", i ) ) ) dispositions = { @dispositions, r.( Str.Format( \ "disposition_%1", i ) ) } else emptyDispos = { @emptyDispos, i } end end if RecArray.IsColumn( r, "disposition_selected" ) defaultDispo = $LLIAPI.FormatPkg.StringToVal( \ r.disposition_selected, integerType ) //Retrieve the disposition settings from the Permissions tab //for this task. Ignore those fields in which no values have //been specified. for i in emptyDispos if ( i < defaultDispo ) count += 1

54

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

end end defaultDispo -= count If ( Length( dispositions ) < defaultDispo ) retVal.OK = FALSE retVal.ErrMsg = \ [WebWork_ErrMsg.DefaultDispositionMustHaveAName] end else defaultDispo = 1 end if RecArray.IsColumn( r, "RequireDisposition" ) flags |= $WFPDisposition else dispositions = { 'Approve', 'Reject' } end //Save the permissions settings. if RecArray.IsColumn( r, "SeeAllComments" ) flags |= $WFPComments end if RecArray.IsColumn( r, "SendForReview" ) flags |= $WFPReview end if RecArray.IsColumn( r, "Delegate" ) flags |= $WFPDelegate end taskInfo.USERFLAGS = { flags, { { @dispositions }, \ defaultDispo } } else //Determine whether the data types that are attached to the //workflow need to display anything before setting up the data //for a particular task. For example, the custom display task //type needs to display a tab that allows the creator of a //workflow map to specify whether certain workflow attributes //are editable, required, or read-only. i = 3 for p in mapRec.WORK_PACKAGES obj = $WebWFP.WFPackageSubsystem.GetItem( { p.TYPE, \ p.SUBTYPE } ) if ( IsDefined( obj ) && IsDefined( p.USERDATA ) ) paneData = obj.GetMapData( prgCtx, taskInfo, p.USERDATA ) if ( IsDefined( paneData ) ) if ( i == r.PaneIndex ) retVal = obj.PutMapData( prgCtx, taskInfo, \ p.USERDATA, r ) break else i += 1

Adding New Task Types

55

Example 1: Adding the Custom Display Task Type

end end end end //Save any callback data. $WEBWFP.WFContentManager.StoreCallbackData( taskInfo, \ r ) end return retVal end

t_user.html
The following code sample describes how to design the Web page that is displayed when the creator of a workflow map edits a custom display task in the Workflow Painter. You edit a custom display task on the Custom Display Step Definition page, which is accessed by double-clicking the tasks icon or by right-clicking the task's icon, and then clicking Edit.
;;webscript t_user( Assoc data ) <!-- File: custmod/t_user.html --> ;;oscript{ Integer i List durationInfo String checked String dueDuration Dynamic taskInfo = data.TaskInfo //Set up the URLs that define the links on the Step Definition //page (for example, the Map Editor link which jumps back to //the Workflow Painter). String nextURL = Str.Format( \ "%1?func=wfp.TaskEdit&MapID=%2&TaskID=%3&NextURL=%4", \ .URL(), \ data.MapID, \ data.TaskID, \ Web.Escape( data.NextURL ) ) String chooseUserURL = .url() + Str.Format( \ "?func=wfp.TaskUserSet&MapID=%1&TaskID=%2&PerformerID=%3" + \ "&nextURL=%4", \ data.MapID, \ data.TaskID, \ taskInfo.PerformerID, \ Web.Escape( nextURL ) ); //Set up the contents of the Group Options list. List flags = { $WFMain.WFConst.kWFGroupStandard, \ $WFMain.WFConst.kWFGroupExpand, \ $WFMain.WFConst.kWFGroupExpandFull } List flagLabels = { [WebWFP_HTMLLabel.MemberAccept], \ [WebWFP_HTMLLabel.OneLevelExpand], \ [WebWFP_HTMLLabel.FullExpand] }

56

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

;;} //Set up the information that is displayed on the General tab of //the Custom Display Step Definition page. This includes the //title, the performer, the group options, the task instructions, //the name of the callback script to run, the workflow event that //triggers the callback script, the template to use, the //duration, and the start date. <TABLE BORDER="0" CELLPADDING="2" CELLSPACING="1"> //Set up the Step Name field. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebWFP_HTMLLabel.StepName_]`&nbsp;</FONT></TD> <TD> <IMG SRC="`.ModImg( 'custmod' )``data.Gif`" WIDTH="16" HEIGHT="16" ALIGN="BOTTOM" BORDER="0" VALIGN="TOP" HALIGN="RIGHT">&nbsp; <INPUT TYPE="TEXT" NAME="Title" SIZE="33" VALUE="`taskInfo.Title`" MAXLENGTH="255" ONCHANGE="markTaskEditDirty();"> <INPUT TYPE="HIDDEN" NAME="PerformerID" VALUE="`taskInfo.PerformerID`"> </TD> </TR> //Set up the Assigned To field. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebWFP_HTMLLabel.AssignedTo_]`&nbsp;</FONT></TD> <TD> <A HREF="`chooseUserURL`" ONCLICK="if ( leaveTaskEdit() ) taskEditGo( '`%LchooseUserURL`' ); else return false;"> <B>`[WebWFP_HTMLLabel.UserOrGroupColon]`</B> </A> <IMG SRC="`.Img()``data.PerformerInfo.Gif`" WIDTH="16" HEIGHT="16" ALIGN="BOTTOM" BORDER="0" VALIGN="TOP" HALIGN="RIGHT"> ;if ( data.PerformerInfo.KnownUser ) ;;call <.HTMLPrefix() + 'douserdialog.html'>( data.PerformerInfo.ID, data.PerformerInfo.Name ) ;else `%Ldata.PerformerInfo.Name` ;end </TD> </TR> //Set up the Group Options field. <TR>

Adding New Task Types

57

Example 1: Adding the Custom Display Task Type

<TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebWFP_HTMLLabel.GroupOptions_]`&nbsp;</FONT></TD> <TD> <SELECT NAME="GroupFlags" ONCHANGE="markTaskEditDirty();"> ;for i = 1 to Length( flags ) ;if ( taskInfo.EXATTS.GroupFlags == flags[ i ] ) <OPTION VALUE="`flags[ i ]`" SELECTED>`flagLabels[ i ]` ;else <OPTION VALUE="`flags[ i ]`">`flagLabels[ i ]` ;end ;end </SELECT> </TD> </TR> //Set up the Instructions field. <TR> <TD bgcolor="#CCCCCC" NOWRAP valign="TOP"><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebWFP_HTMLLabel.Instructions_]`&nbsp;</FONT></TD> <TD> <TEXTAREA NAME="Instructions" ROWS="6" COLS="45" WRAP="SOFT" ONCHANGE="markTaskEditDirty();">`taskInfo.Instructions`</TEXTAREA> </TD> </TR> //Set up the Script to run field. <TR> <TD bgcolor="#CCCCCC" NOWRAP valign="TOP"><FONT face="Arial, Helvetica, sans-serif" size="2">&nbsp;Script to run:&nbsp;</FONT></TD> <TD> <SELECT NAME="CustTaskScript" ONCHANGE="markTaskEditDirty();"> ;String scriptName ;List scriptsList = $Custmod.UtilityPkg.ListScripts() <OPTION>&lt;None&gt; ;for scriptName in scriptsList <OPTION `( taskInfo.ExAtts.CustTaskScript == scriptName ) ? 'selected':''`>`scriptName` ;end </SELECT> <BR> //Set up the Step Becomes Ready and Step Is Done radio //buttons.

58

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

<TABLE BORDER="0" CELLPADDING="2" CELLSPACING="1"> <TR> <TD>Script runs when:</TD> <TD> ;checked = ( taskInfo.ExAtts.RunScript == 'ReadyCB' ) ? "CHECKED" : "" <INPUT TYPE="RADIO" NAME="RunScript" `checked` VALUE="ReadyCB" ONCLICK="markTaskEditDirty();">Step Becomes Ready <BR> ;checked = ( taskInfo.ExAtts.RunScript == 'DoneCB' ) ? "CHECKED" : "" <INPUT TYPE="RADIO" NAME="RunScript" `checked` VALUE="DoneCB" ONCLICK="markTaskEditDirty();">Step Is Done </TD> </TR> </TABLE> </TD> </TR> //Set up the Template to use field. <TR> <TD bgcolor="#CCCCCC" NOWRAP valign="TOP"><FONT face="Arial, Helvetica, sans-serif" size="2">&nbsp;Template to use:&nbsp;</FONT></TD> <TD> <SELECT NAME="CustTaskTemplate" ONCHANGE="markTaskEditDirty();"> ;String templateName ;List templatesList = $Custmod.UtilityPkg.ListTemplates() <OPTION>&lt;None&gt; ;for templateName in templatesList <OPTION `( taskInfo.customData.CustTaskTemplate == templateName ) ? 'selected':''`>`templateName` ;end </SELECT> </TD> </TR> //Set up the Duration field. ;;oscript{ if IsDefined( taskInfo.DueDuration ) durationInfo = $LLIAPI.FormatPkg.ConvertFromSeconds( \ taskInfo.DueDuration ) dueDuration = $LLIAPI.FormatPkg.ValToString( \ durationInfo[2] ) else durationInfo = { TRUE, 0 } end ;;} <TR>

Adding New Task Types

59

Example 1: Adding the Custom Display Task Type

<TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebWFP_HTMLLabel.Duration_]`&nbsp;</FONT></TD> <TD> <INPUT TYPE="TEXT" NAME="Duration" VALUE="`dueDuration`" SIZE="5" ONCHANGE="markTaskEditDirty();"> ;checked = ( durationInfo[ 1 ] ) ? "CHECKED" : "" <INPUT TYPE="RADIO" NAME="DurationUnits" `checked` VALUE="Days" ONCLICK="markTaskEditDirty();">`[WebWFP_HTMLLabel.Days]` ;checked = ( !durationInfo[ 1 ] ) ? "CHECKED" : "" <INPUT TYPE="RADIO" NAME="DurationUnits" `checked` VALUE="Hours" ONCLICK="markTaskEditDirty();">`[WebWFP_HTMLLabel.Hours]` </TD> </TR> //Set up the Start Date field. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebWFP_HTMLLabel.StartDate_]`&nbsp;</FONT></TD> <TD NOWRAP> ;;call <.htmlPrefix() + 'datefield.html'>( 'StartDate', taskInfo.StartDate, TRUE, TRUE ) </TD> </TR> //Set up the Action field, which contains the Add to Workflow //Definition button. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebDoc_HTMLLabel.Action_]`&nbsp;</FONT></TD> <TD> <INPUT TYPE="Submit" VALUE="`[WebWFP_HTMLLabel.AddToWorkflowDefinitionButtonLabel]`"> </TD> </TR> </TABLE> ;;end

60

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

The CustomDisplayWork object


After you define the API object and Workflow Painter information for the custom display task type, you must define the information that handles the task when a workflow participant is working on it and when it is displayed on the Detailed Status page in Livelink. To define the information that handles the task and displays it on the Detailed Status page: 1. Orphan WebWork:WebWork Root:WFTask in the custmod OSpace in your custom module, and name it CustomDisplayWork. 2. In the CustomDisplayWork object, set the fPaletteTask feature to TRUE. 3. Change the fSubType feature to an Integer/Real type, and set it to 1. The fSubType feature stores a unique integer that works with the fType feature to identify the object. It must match the value specified for the fSubType feature in the CustomDisplayAPI object and the CustomDisplayPaint object. 4. Change the fType feature to an Integer/Real type, and set it to 11. The fType feature stores a unique integer that works with the fSubType feature to identify the object. It must match the value specified for the fType feature in the CustomDisplayAPI object and the CustomDisplayPaint object. 5. Change the fTaskGif feature to a String type, and set it to custtask.gif. The custtask.gif file identifies the image that you want to display for the custom display task in the Step Icon Palette and must be 32x32 pixels. 6. Copy the custtask.gif file to the /webwork directory of your Livelink installation (for example, c:/opentext/webwork). 7. Create a 16x16 pixel version of the custtask.gif file, name it 16user.gif, and copy the 16user.gif file to the support directory in your custom module. This 16user.gif file identifies the image that is displayed on the Custom Display Step Definition page. 8. Override the following scripts:
GetDisplayPerformerInfo() GetPainterInfo() GetPainterMenu() GetStatusDisplay() GetTaskEditData() GetTaskGif() NewPerformer()

For more information about these scripts, see the code samples that follow.

Adding New Task Types

61

Example 1: Adding the Custom Display Task Type

9. Create a script, and name it PutReviewData. For more information, see PutReviewData(), page 74. 10. Create a script, and name it ReassignStep. For more information, see ReassignStep(), page 75. 11. Create an HTML file, name it redirect.html, and store it in your modules /html directory (for example, c:/opentext/module/custmod_1_0_0/html). For more information about this HTML file, see redirect.html, on page 78. You have created the object necessary to handle the task operations. Now you must provide the code required to customize the information for the custom display task type.

GetDisplayPerformerInfo()
The following code sample describes how to retrieve the name and ID of the workflow participant to which the task is assigned.
Function Dynamic GetDisplayPerformerInfo( \ Object prgCtx, \ Record taskRec ) Dynamic Dynamic Integer Object performer retVal performerID uSession = prgCtx.USession()

//Search for the ID of the Livelink user to which the task is //assigned. if ( RecArray.IsColumn( taskRec, 'PERFORMERID' ) ) performerID = taskRec.PERFORMERID elseif ( RecArray.IsColumn( taskRec, 'WORK' ) ) performerID = taskRec.WORK.SUBWORKTASK_PERFORMERID elseif ( RecArray.IsColumn( taskRec, 'SUBWORKTASK_PERFORMERID' \ ) ) performerID = taskRec.SUBWORKTASK_PERFORMERID else performerID = Undefined end //If the Livelink user ID is defined, retrieve the Livelink user //name that is associated with it. if ( IsDefined( performerID ) ) performer = UAPI.GetByID( uSession.fSession, performerID ) //If the Livelink user name is found, create an Assoc named retVal //in which you store the Livelink user name and ID. if ( !IsError( performer ) ) retVal = Assoc.CreateAssoc() retVal.ID = performer[ 1 ].ID retVal.Name = performer[ 1 ].NAME

62

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

end else retVal = [WebWork_Label.User] end return( retVal ) end

GetPainterInfo()
The following code sample describes how to define the information that the Workflow Painter needs to know about the custom display task type.
Function Assoc GetPainterInfo( \ Object prgCtx, \ Record task = Undefined ) Assoc Assoc Assoc String String info linkData retVal gif = .fTaskGif name = .GetTaskName()

//Retrieve the title and image used to represent the custom //display task type in the Workflow Painter. if ( IsDefined( task ) ) info = .GetDisplayInfo( prgCtx, task ) name = info.Title gif = info.Gif end retVal.ID = Str.String( .fType ) + '_' + Str.String( .fSubType ) //Specify a name for the task in the Workflow Painter. retVal.Name = name //Specify the name of the image that is displayed in the //Workflow Painter to represent the custom display task. retVal.Gif = gif //Specify whether this task should be added to the Step Icon //Palette in the Workflow Painter. retVal.PaletteTask = .fPaletteTask //Specify whether this task can be duplicated. retVal.Duplicatable = .fDuplicatable //Specify the name of the Edit request handler for the custom //display task type. This request handler displays the custom //display task when you click the task name in your Tasks list. retVal.RHandler = 'wfp.TaskEdit'

Adding New Task Types

63

Example 1: Adding the Custom Display Task Type

//Specify the name of the View request handler for the custom //display task type. This request handler displays the detailed //status for the custom display task when you click a task name //on the Step List page. retVal.RHandlerWorkView = 'work.TaskDetail' //Specify the name of the Choose User request handler for the //custom display task type. This request handler is called when //you right-click the custom display task icon in the Workflow //Painter, and then click Choose Performer to specify the //performer of the task. retVal.RHandlerChoose = 'wfp.TaskUserSet' //Specify the background color of the task when it is displayed //in the Map Overview window in the Workflow Painter. retVal.Background = 'flesh' //Retrieve the link information associated with the task type. //This includes the maximum number of link types that can come //from the task type and the maximum number of link types that //can go to this task type. linkData = .GetTaskTypeObj().GetLinkInfo() //Specify the maximum number of link types that can come from //this task type. Most task types can only have a single link //type coming from them (either a standard link or a loopback //link); however, a conditional step can have two link types //coming from it. retVal.MaxLinkTypes = linkData.MaxLinkTypes //Specify the type of links that can go to this task type. retVal.LinkTypesTo = linkData.LinkTypesTo //Specify the type of links that can come from this task type. retVal.LinkTypesFrom = linkData.LinkTypesFrom return( retVal ) end

GetPainterMenu()
The following code sample describes how to define the menu commands that appear when you right-click the custom display task icon in the Workflow Painter.
Function List GetPainterMenu( Boolean viewonly ) List retval //If the menu commands are not set to viewonly, populate the //following Assocs. if ( !viewonly )

64

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

Assoca Assocb Assocc Assocd Assoce Assocf //Populate Assoca with the label, font, help, and userdata //values. The label is the name of the menu command, as it //appears in the popup menu that is displayed when you right//click the task type in the Workflow Painter. The font value //specifies the type of font used to display the menu command. //The help value is the text that is displayed on the Status //Bar when you position your cursor over the Edit command in //the popup menu. The userdata value identifies the request //handler that executes the Edit command. a.label = [WebWork_MenuLabel.Edit] a.font = "bold" a.help = [WebWork_MenuLabel.EditThisStepSAttributes] a.userdata = "rhandler" //Populate Assocb with the label, help, and userdata values. //The label is the name of the menu command, as it appears in //the popup menu that is displayed when you right-click the //task type in the Workflow Painter. The help value is the text //that is displayed on the Status Bar when you position your //cursor over the Duplicate command in the popup menu. The //userdata value identifies the request handler that executes //the Duplicate command. b.label = [WebWork_MenuLabel.Duplicate] b.help = [WebWork_MenuLabel.DuplicateTheCurrentStepSelection] b.userdata = "duplicate" //Set c.separator to TRUE to insert a separator line between //the Duplicate and Choose Performer commands in the popup menu //that appears when you right-click the task type in the //Workflow Painter. c.separator = "true" //Populate Assocd with the label, help, and userdata values. //The label is the name of the menu command, as it appears in //the popup menu that is displayed when you right-click the //task type in the Workflow Painter. The help value is the text //that is displayed on the Status Bar when you position your //cursor over the Choose Performer command in the popup menu. //The userdata value identifies the request handler that //executes the Choose Performer command. d.label = [WebWork_MenuLabel.ChoosePerformer] d.help = [WebWork_MenuLabel.ChooseAUserOrGroupForThisStep] d.userdata = "rhandlerChoose" //Set e.separator to TRUE to insert a separator line between //the Choose Performer and Delete commands in the popup menu //that appears when you right-click the task type in the //Workflow Painter.

Adding New Task Types

65

Example 1: Adding the Custom Display Task Type

e.separator = "true" //Populate Assocf with the label, help, and userdata values. //The label is the name of the menu command, as it appears in //the popup menu that is displayed when you right-click the //task type in the Workflow Painter. The help value is the text //that is displayed on the Status Bar when you position your //cursor over the Delete command in the popup menu. The //userdata value identifies the request handler that executes //the Delete command. f.label = [WebWork_MenuLabel.Delete] f.help = [WebWork_MenuLabel.DeleteTheCurrentStepSelection] f.userdata = "delete" //Create a list of the Assocs that hold the values for the menu //commands, and name the list retVal. retval = { a, b, c, d, e, f } //If the menu commands are set to viewonly, populate Assoca with //the label, font, help, and userdata values for the read-only //menu command (view). else Assoca a.label = [WebWork_MenuLabel.View] a.font = "bold" a.help = [WebWork_MenuLabel.ViewThisStep] a.userdata = "rhandlerWorkView" //Store Assoca in a list and name the list retVal. retval = { a } end return retval end

Note The GetPainterMenu() script displays the Edit, Duplicate, Choose Performer, and Delete commands on the menu that appears when you right-click the custom display task icon in the Workflow Painter. The Choose Performer command is separated from the rest of the commands in the menu by two separator lines.

GetStatusDisplay()
The following code sample describes how to retrieve the information that is displayed when a workflow participant clicks the task name on the Step List page. The Step List page is accessed by clicking the Step List tab on the Detailed Status page for a workflow.
function Assoc GetStatusDisplay( \ Object prgCtx, \ Dynamic context, \ Dynamic data = Undefined ) Assoc a

66

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

Assoc retVal Assoc tabPaneInfo Assoc tmp Integer whichTab RecArray auditInfo RecArray disposition RecArray performer String title Record Record mapRec = context.MAP_PAINTER task = context

whichTab = ( RecArray.IsColumn( data, 'PaneIndex' ) ) ? \ Str.StringToInteger( data.PaneIndex ) : 1 //Populate the tmp Assoc with Label, URL, HelpKey, and Active //values. The Label value specifies the name of the tab that you //are preparing for display (General). The URL value identifies //the page to display on the General tab. The HelpKey value //specifies the help page to display for this task type. If set //to TRUE, the Active value indicates that the tab is the active //tab (currently displayed). If set to FALSE, the Active value //specifies that the General tab is not the active tab and must //be called for display. tmp.Label = [WebWork_HTMLLabel.General] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( data ), 1 ) tmp.HelpKey = 'User' // do not XLATE tmp.Active = FALSE //Store the tmp Assoc in a list and assign it to //tabPaneInfo.TabList. tabPaneInfo.TabList = { tmp } a.Gif = '16user.gif' //Determine whether the workflow participant can reassign the //task by checking permissions. a.CanReassign = $WFMain.WAPIPkg.CheckWFPermissions( \ prgCtx, task, $WFMain.WFConst.kWFChangeWork ) //Retrieve the disposition data for the task. disposition = $WFMain.WAPIPkg.GetDispositionData( \ prgCtx, task.SUBWORKTASK_SUBWORKID, task.SUBWORKTASK_TASKID ) //If a disposition was specified for this task, add it to the //Assoc of data that is passed to the HTML file so that it can be //displayed. if ( IsDefined( disposition ) && Length( disposition ) ) a.Disposition = Str.Quote( $LLIAPI.FormatPkg.ValToString( \ disposition[ 1 ].VALUE ), '"' ) end if ( IsDefined( task.SUBWORKTASK_PERFORMERID ) ) performer = UAPI.GetByID( prgCtx.USession().fSession, \ task.SUBWORKTASK_PERFORMERID )

Adding New Task Types

67

Example 1: Adding the Custom Display Task Type

if ( !IsError( performer ) ) a.PerformerName = performer[ 1 ].NAME if ( performer[ 1 ].TYPE != UAPI.USER ) a.Gif = '16group.gif' end end end a.WorkRec = task tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwork' tmp.HTMLFile = 'wwtuser.html' tmp.Data = a tabPaneInfo.PaneList = { tmp } //Retrieve the audit trail, if necessary. if ( whichTab == 2 ) auditInfo = $WFMain.WAPIPkg.GetAuditRec( \ prgCtx, task.WORK_WORKID, task.SUBWORK_SUBWORKID, \ task.SUBWORKTASK_TASKID ) if ( IsError( auditInfo ) ) auditInfo = Undefined end end //Add the Audit tab to the Step Detail page for this type of //task. tmp = Assoc.CreateAssoc() tmp.Label = [WebWork_HTMLLabel.Audit] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( data ), 2 ) tmp.HelpKey = 'Audit' tmp.Active = FALSE tabPaneInfo.TabList = { @tabPaneInfo.TabList, tmp } tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwork' tmp.HTMLFile = 'audittrail.html' tmp.Data = auditInfo tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, tmp } tmp = .GetPackageStatusData( prgCtx, task, data, tabPaneInfo ) //Set the Active flag for the tab that is currently selected. if ( tmp.OK ) if ( ( whichTab < 2 ) || ( whichTab > Length( \ tabPaneInfo.TabList ) ) ) whichTab = 1 end tabPaneInfo.TabList[ whichTab ].Active = TRUE end

68

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

//Set up an Assoc that returns all of the data required by //Livelink to draw the Step Detail page. retVal.OK = tmp.OK retVal.ErrMsg = tmp.ErrMsg retVal.HTMLFile = "wwt.html" retVal.ModuleName = 'webwork' retVal.Tab = whichTab retVal.TabInfo = tabPaneInfo retVal.Data = task //Set the masthead information so that the correct header is //displayed at the top of the Step Detail page. retVal.HeaderArgs = $WebWork.WFPkg.GetHeaderTypeArgs( 'STATUS', \ task.SUBWORK_TITLE ) return( retVal ) end

GetTaskEditData()
The following code sample describes how to retrieve the information that is displayed when a workflow participant clicks the task name on the Tasks page in their Personal Workspace.
function Assoc GetTaskEditData( \ Object prgCtx, \ Record taskInfo, \ Record r ) Assoc a Assoc data Assoc paneData Assoc retVal Assoc tabPaneInfo Assoc tmp Dynamic status Integer whichTab List tabList Object obj RecArray packages Record p WAPIWORK work Boolean Boolean Integer Integer ok = true groupStep = False flags = WAPI.STARTTASK_FLAG_REEXECUTE i = 1

tmp = GetCustomData( taskinfo.subworktask_customdata ) if ( IsDefined( tmp ) && IsDefined( tmp.CustTaskTemplate ) ) data.HTMLFile = 'redirect.html' data.CustTaskTemplate = tmp.CustTaskTemplate data.ModuleName = 'custmod' else whichTab = ( RecArray.IsColumn( r, 'PaneIndex' ) ) ? \ Str.StringToInteger( r.PaneIndex ) : Undefined

Adding New Task Types

69

Example 1: Adding the Custom Display Task Type

//If a workflow participant has accessed the task but is just //viewing the tabs and not actually entering data, do not add //rows to the audit trail for these operations. if ( IsDefined( whichTab ) ) if ( whichTab > 0 ) flags = flags | WAPI.STARTTASK_FLAG_NOAUDIT end data.HTMLFile = 'tgeneric.html' data.ModuleName = 'webwork' data.TaskInfo = taskInfo //Set the masthead information. data.HeaderArgs = $WebWork.WFPkg.GetHeaderTypeArgs( \ 'WORK', taskInfo.SUBWORK_TITLE ) //Determine whether the task has been assigned to a Livelink //user or a group. if !( $WFMain.WAPIPkg.CheckTaskAssignedToUser( prgCtx, \ taskInfo ) ) groupStep = True end //Set up the information required to draw the first tab //(General). tmp = Assoc.CreateAssoc() tmp.Label = [WebWork_HTMLLabel.General] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( r ), i ) tmp.Active = FALSE tabPaneInfo.TabList = { tmp } a.TaskInfo = taskInfo a.GroupStep = groupStep //Use the standard General tab that lets a group member //accept the task and add it to their task list. This is the //same General tab that is used for a User task type. tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwork' tmp.HTMLFile = 'taskgeneralpane.html' tmp.Data = a tabPaneInfo.PaneList = { tmp } if ( !groupStep ) //Get a work handle. work = prgCtx.WSession().AllocWork() if ( !IsError( work ) ) //Use the StartTask() script to verify that the //performer of this task can access the data associated //with the task. This script sets up the work object so //that it can be used to access the work package. You

70

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

//can also use the StartTask() script to make sure that //this task is ready to be complete (and was not //completed already). status = WAPI.StartTask( \ work, \ r.WorkID, \ r.SubWorkID, \ r.TaskID, \ flags ) if ( !IsError( status ) ) //Retrieve the current work package. packages = $WFMain.WAPIPkg.GetWorkPackages( \ prgCtx, work, taskInfo ) if ( !IsError( packages ) ) i += 1 for p in packages obj = \ $WebWork.WFPackageSubsystem.GetItem( \ { p.TYPE, p.SUBTYPE } ) if ( IsDefined( obj ) && IsDefined( \ p.USERDATA ) ) a = obj.GetTabInfo( prgCtx, r, \ p.USERDATA, i ) a.Active = FALSE paneData = obj.GetData( \ prgCtx, taskInfo, p.USERDATA, \ r ) if ( IsDefined( paneData ) ) i += 1 paneData.IsEditable = \ True tabPaneInfo.TabList = { \ @tabPaneInfo.TabList, a } tabPaneInfo.PaneList = \ {@tabPaneInfo.PaneList, \ paneData } end end end //If the data was not retrieved correctly //(OK=FALSE), return an error message. else ok = False retVal.ApiError = work retVal.ErrMsg = \ [Web_ErrMsg2.CouldNotAccessWorkpackage] end //If the data was not retrieved correctly //(OK=FALSE), return an error message. else ok = False retVal.ApiError = work retVal.ErrMsg = [Web_ErrMsg2.CouldNotAccessWork]

Adding New Task Types

71

Example 1: Adding the Custom Display Task Type

end WAPI.FreeWork( work ) else ok = False retVal.ApiError = work retVal.ErrMsg = [Web_ErrMsg2.CouldNotAllocwork] end end if ( ok ) if ( ( whichTab < 2 ) || ( whichTab > Length( \ tabPaneInfo.TabList ) ) ) whichTab = 1 end tabPaneInfo.TabList[ whichTab ].Active = True end data.TabInfo = tabPaneInfo data.Tab = whichTab else ok = False retVal.ErrMsg = \ [WebWork_ErrMsg.TheArgumentPaneIndexIsRequired] end end retVal.OK = ok retVal.Data = data return( retVal ) end Function Dynamic GetCustomData( Dynamic val ) Dynamic retVal = val while ( Type( retVal ) == StringType ) retVal = Str.StringToValue( retVal ) end return( retVal ) end

GetTaskGif()
The following code sample describes how to define the image used to represent the task in the Workflow Painter. This image changes depending on the performer of the task.
Function String GetTaskGif( \ Object prgCtx, \ Record taskRec ) String retVal Integer id = .GetTaskTypeObj().GetTaskPerformerId( taskRec ) if ( IsDefined( id ) ) retVal = .GetDisplayInfo( prgCtx, taskRec ).Gif else retVal = .fTaskGif end return( retVal ) end

72

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

NewPerformer()
The following code sample describes how to update task information if the task is reassigned in Livelink.
Function Assoc NewPerformer( \ Object prgCtx, \ Record taskRec, \ Integer newID ) Assoc retVal Dynamic userInfo String name //Set some default variables and locate the initiator task object //in the WebWork OSpace. Boolean success = True Integer painterInfo = 0 Integer performerID = 0 Object taskType = $WebWork.WFTaskSubsystem.GetItemByName( \ 'Initiator' ) String initTitle = taskType.GetTaskName() String title = taskRec.TITLE //Determine the default title for the task based on the //performer ID. if ( !IsDefined( taskRec.PERFORMERID ) ) name = .GetTaskName() elseif ( taskRec.PERFORMERID == 0 ) name = initTitle else userInfo = UAPI.GetByID( prgCtx.USession().fSession, \ taskRec.PERFORMERID ) if ( !IsError( userInfo ) ) userInfo = userInfo[ 1 ] name = userInfo.NAME end end //Determine the default title for the task based on the //new performer ID. if ( newID == 0 ) // Initiator title = initTitle elseif ( !IsDefined( newID ) ) // Generic user title = .GetTaskName() performerID = newID else performerID = newID painterInfo = newID //Determine the user name of the performer, based on their //performer ID. userInfo = UAPI.GetByID( prgCtx.USession().fSession, newID )

Adding New Task Types

73

Example 1: Adding the Custom Display Task Type

if ( !IsError( userInfo ) && Length( userInfo ) ) userInfo = userInfo[ 1 ] title = userInfo.NAME end end //If the title of the task was set to the default title, then //update it with the new default title. If the title of the task //was not set to the default title, then dont change it. if ( success ) if ( taskRec.TITLE == name ) taskRec.TITLE = title end taskRec.PERFORMERID = performerID taskRec.PAINTER[ 2 ] = painterInfo end retVal.OK = success return( retVal ) end

PutReviewData()
The following code sample describes how to create a script that saves the instructions you provide to the reviewers of a task when you submit it for review. These instructions can also include the duration and some group options.
function assoc PutReviewData( \ Object prgCtx, \ Record mapRec, \ Record taskInfo, \ Record r ) Assoc retVal Real time retVal.OK = TRUE //Save the step name. if ( RecArray.IsColumn( r, 'Title' ) ) taskInfo.Title = $LLIAPI.FormatPkg.ValToString( r.title ) end //Save the instructions. if ( RecArray.IsColumn( r, 'Instructions' ) ) taskInfo.Instructions = $LLIAPI.FormatPkg.ValToString( r.Instructions ) end //Save the duration. if ( RecArray.IsColumn( r, 'Duration' ) ) if IsDefined( r.Duration ) && Length( r.Duration ) Boolean inDays = ( r.DurationUnits == "Days" )

74

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

time = $LLIAPI.FormatPkg.StringToVal( r.Duration, RealType ) if ( Type( time ) != RealType ) retVal.OK = FALSE if inDays retVal.ErrMsg = \ [WebWork_ErrMsg.DurationMustBeANumberOfDays] else retVal.ErrMsg = \ [WebWork_ErrMsg.DurationMustBeANumberOfHours] end else taskInfo.DueDuration = \ $LLIAPI.FormatPkg.ConvertToSeconds( inDays, time ) end else taskInfo.DueDuration = Undefined end end //Save the group options. if RecArray.IsColumn( r, "GroupFlags" ) taskInfo.EXATTS.GroupFlags = Str.StringToInteger( \ r.GROUPFLAGS ) end return retVal end

ReassignStep()
The following code sample describes how to create a script that lets workflow participants reassign a task on the Step Detail page.
Function Boolean ReassignStep( \ Object prgCtx, \ Record taskRec, \ Record user, \ Record workData, \ WAPIWORK work ) Assoc userInfo Integer where List cbData Boolean Object Object String ok = False uSession = prgCtx.USession() wSession = prgCtx.WSession() cr = Str.EOL()

//Determine whether a manager of a workflow has permission to //reassign the task. if ( $WFMain.WAPIPkg.CheckWFPermissions( prgCtx, workData, \ $WFMain.WFConst.kWFChangeWork ) )

Adding New Task Types

75

Example 1: Adding the Custom Display Task Type

ok = wSession.StartTrans() //If the title of the task was set to the default title, then //update it with the new default title. If the title of the task //was not set to the default title, then dont change it. if ( ok ) userInfo = .GetDisplayInfo( prgCtx, taskRec ) ok = UpdateStepTitle( prgCtx, work, taskRec, userInfo, \ user ) //If the task has been assigned to a group, add a performer //callback script that runs if the workflow loops back during the //reassignment. If the workflow loops back, this callback makes //sure that the task is reassigned to the entire groupnot just to //the group member that originally accepted it. if ( ok ) if ( user.TYPE != UAPI.USER ) cbData = { { $WFMain.WFConst.kCBSetGrpStepPerformer, \ user.ID } } else cbData = Undefined end //Save the callback script in the database. ok = UpdateMapTask( prgCtx, taskRec, cbData ) end //Set the performer ID to the new user ID. if ( ok ) ok = UpdatePerformer( prgCtx, work, taskRec, user ) end if ( !wSession.EndTrans( ok ) ) ok = False end end end return( ok ) end Function Boolean UpdatePerformer( \ Object prgCtx, \ WAPIWORK work, \ Record old, \ Record new ) Boolean success List info //Make the WAPI call that reassigns the task. success = prgCtx.WSession().CheckRetVal( \ WAPI.ReassignTask( \ work, \

76

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

old.WORK.SUBWORKTASK_WORKID, \ old.WORK.SUBWORKTASK_SUBWORKID, \ old.WORK.SUBWORKTASK_TASKID, \ new.ID ) ) if ( success ) info = { 1, { Str.String( [WebWork_Message.StepReassignedTo] \ ), new.ID } } $WFMain.WAPIPkg.AddAuditData( prgCtx, work, info ) old.WORK.SUBWORKTASK_PERFORMERID = new.ID end return( success ) end Function Boolean UpdateStepTitle( \ Object prgCtx, \ WAPIWORK work, \ Record task, \ Assoc info, \ Record user ) String Boolean Object name success = True wSession = prgCtx.WSession()

//Save the new task title to the database. if ( info.Title == task.WORK._SUBWORKTASK_PERFORMERID_Name ) name = user.NAME success = wSession.CheckRetVal( WAPI.UpdateTaskAttribute( \ work, \ task.WORK.SUBWORKTASK_WORKID, \ task.WORK.SUBWORKTASK_SUBWORKID, \ task.WORK.SUBWORKTASK_TASKID, \ WAPI.SUBWORKTASK_TITLE, \ name ) ) if ( success ) task.WORK.SUBWORKTASK_TITLE = name end end return( success ) end Function Boolean UpdateMapTask( \ Object prgCtx, \ Record taskData, \ List cbData = Undefined ) Boolean success WAPIMAPTASK task Object WAPIMAP session = prgCtx.WSession() map = session.AllocMap()

Adding New Task Types

77

Example 1: Adding the Custom Display Task Type

success = session.CheckRetVal( WAPI.LoadMapByID( map, \ taskData.WORKINFO.SUBWORK_MAPID ) ) if ( success ) task = WAPI.AllocNthMapTask( map, \ taskData.WORK.SUBWORKTASK_TASKID ) success = session.CheckRetVal( task ) if ( success ) task.pPerformerCB = cbData WAPI.FreeMapTask( task ) success = session.CheckRetVal( WAPI.ReplaceMap( map ) ) end end WAPI.FreeMap( map ) return( success ) end

redirect.html
This HTML file describes how to display the HTML file that the creator of a workflow map assigns to a task of this type.
;;webscript redirect( Assoc taskData ) <!-- File: custtask/redirect.html --> //Display the HTML file that the creator of the workflow assigned //to this task. ;;call <.Module( 'custmod' ).PathPrefix() + 'templates' + File.Separator() + taskData.CustTaskTemplate>() ;;end

78

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

The WFCustomScriptPkg Object


After you customize the objects necessary to create the custom display task type, you must orphan the WFCustomScriptPkg object, which is responsible for executing the callback scripts that can be used with this task type. You can find this object in WFMain:WFRoot:WFCustomScriptPkg. To orphan the WFCustomScriptPkg Object: 1. Orphan WFMain:WFRoot:WFCustomScriptPkg in the custmod OSpace in your custom module, and name it WFCustomScriptPkg. 2. In the WFCustomScriptPkg object, modify the CBExecute() script. For more information about modifying the CBExecute() script, see CBExecute(), page 80. 3. Create a child object of the Custmod Root object, and name it UtilityPkg. 4. In the UtilityPkg object, create a script, and name it ExecuteCustTaskScript. For more information about the ExecuteCustTaskScript() script, see ExecuteCustTaskScript(), page 80. 5. Create a Script feature, and name it ExecuteScript. For more information about the ExecuteScript() script, see ExecuteScript(), page 81. 6. Create a Script feature, and name it ListScripts. For more information about the ListScripts() script, see ListScripts(), page 82. 7. Create a Script feature, and name it ListTemplates. For more information about the ListTemplates() script, see ListTemplates(), page 82. 8. In the Custmod Globals object, create a Dynamic feature, name it custtaskmodule, and set its value to the object reference number of the CustWebModule object. 9. In the Custmod Globals object, create a Dynamic feature, name it UtilityPkg, and set its value to the object reference number of the UtilityPkg object that you created in step 3. 10. In the Custmod Globals object, run the BuildOSpace() script. 11. Save and export your OSpace, then exit and restart the Livelink Builder. You have created the object responsible for handling callback scripts. Now you must customize this object for the custom display task type.

Adding New Task Types

79

Example 1: Adding the Custom Display Task Type

CBExecute()
The following code sample describes how to handle the callback scripts that can be used with the custom display task type.
Function Assoc CBExecute( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer subWorkID, \ Integer taskID, \ Integer returnSubWorkID, \ Integer returnTaskID, \ List info, \ Dynamic extraData = Undefined ) Assoc data Dynamic retVal Boolean handled = True //If the callback script that is being executed is one of the //scripts that is added by the custom display task type, then run //the script. switch( info[ 1 ] ) case 500 retVal = $Custmod.UtilityPkg.ExecuteCustTaskScript( \ prgCtx, work, workID, subWorkID, taskID, info[ 2 ] ) end default handled = False end end data.Handled = handled data.retVal = retVal return( data ) end

ExecuteCustTaskScript()
The following code sample describes how to create a script that loads information about the custom display task and uses that information to locate and run the appropriate callback script.
Function Boolean ExecuteCustTaskScript( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer subWorkID, \ Integer taskID, \ String scriptName ) Dynamic taskInfo Boolean success = False Object uapiCtx = prgCtx.Usession() //Load information about the custom display task. taskInfo = prgCtx.WSession().LoadTaskStatus( workID, subWorkID, \

80

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

taskID ) if ( !IsError( taskInfo ) ) UAPI.ExpandList( \ uapiCtx.fSession, \ taskInfo, \ { \ 'WORK_OWNERID', \ 'WORK_MANAGERID', \ 'SUBWORKTASK_PERFORMERID' } ) //Call the ExecuteScript() script which uses the information //about the task to locate and run the correct callback script. success = $Custmod.UtilityPkg.ExecuteScript( prgCtx, work, \ taskInfo, scriptName ) end return( success ) end

ExecuteScript()
The following code sample describes how to create a script that locates and runs the callback script that is associated with this custom display task.
Function Boolean ExecuteScript( \ Object prgCtx, \ WAPIWORK work, \ Dynamic taskInfo, \ String scriptName ) Boolean Dynamic String String String success = True err = Undefined moduleDir = $Custmod.custtaskmodule.PathPrefix() scriptsDir = moduleDir + "scripts" + File.Separator() scriptFile = scriptsDir + scriptName

//Convert the HTML files that have OScript in them to pure HTML. result = $WebLingo.WebScript.RunFileWithArglist( \ scriptFile, \ Undefined, \ Undefined, \ { prgCtx, work, taskInfo }, \ true ) if ( Type( result ) == ListType ) echo( "Error during $WebLingo.WebScript.RunFile =", result ) success = false elseif IsNotError( result ) success = true else echo( "Error during $WebLingo.WebScript.RunFile =", result ) success = false end return( success ) end Dynamic

Adding New Task Types

81

Example 1: Adding the Custom Display Task Type

ListScripts()
The following code sample describes how to create a script that returns a list of scripts that are stored in the /scripts directory of the custmod module (for example, c:/opentext/module/custmod_8_1_x/scripts).
function List ListScripts() String scriptPath String moduleDir = $Custmod.custtaskmodule.PathPrefix() String scriptsDir = moduleDir + "scripts" + File.Separator() List vFileListList = File.FileList( scriptsDir ) List retList = {} //Retrieve the list of scripts that are stored in your //module's /script directory. These are the scripts that the //creators of workflow maps can attach to the custom display task //type in the Workflow Painter. if ( !IsError( vFileListList ) && \ ( Length( vFileListList ) > 0 ) ) for scriptPath in vFileListList retList = { @retList, File.GetName( scriptPath ) } end end return( retList ) end

ListTemplates()
The following code sample describes how to create a script that returns a list of HTML templates that are stored in the /templates directory of the custmod module (for example, c:/opentext/module/custmod_1_0_0/templates).
Function List ListTemplates() String templatePath String moduleDir = $Custmod.custtaskmodule.PathPrefix() String templatesDir = moduleDir + "templates" + File.Separator() List vFileListList = File.FileList( templatesDir ) List retList = {} //Retrieve the list of HTML templates that are stored in your //module's /template directory. These are the HTML templates that //the creators of workflow maps can attach to the custom display //task type in the Workflow Painter. if ( !IsError( vFileListList ) && \ ( Length( vFileListList ) > 0 ) ) for templatePath in vFileListList retList = { @retList, File.GetName( templatePath ) } end end return( retList ) end

82

Developers Guide for Extending Livelink Workflow

Example 1: Adding the Custom Display Task Type

Adding Custom Scripts and Templates


After you implement the functionality required to create the custom display task type, you must create and store the custom scripts and templates for the tasks. To add custom scripts and templates: 1. Create a folder in your custom module's directory structure, and name it scripts (for example, c:/opentext/module/custmod_1_0_0/scripts). 2. Create a folder in your custom module's directory structure and name it templates (for example, c:/opentext/module/custmod_1_0_0/templates). 3. Add the scripts that you want to make available to the custom display task type to the /scripts folder that you created in step 1. The scripts appear in the Script to run list on the Custom Display Step Definition page in the Workflow Painter. 4. Add the templates that you want to make available to the Custom Display task type to the /templates folder that you created in step 2. The templates appear in the Template to use list on the Custom Display Step Definition page in the Workflow Painter.

Adding New Task Types

83

Example 1: Adding the Custom Display Task Type

84

Developers Guide for Extending Livelink Workflow

Chapter Five

Adding New Data Types


You add new data types to Livelink if you want to include new types of information in the work packages that accompany your workflows. By default, work packages can contain three data types: attachments, attributes, and comments; however, you can create a data type for any new type of information that you want to add to a work package. Note Data types are also called package types and are displayed in the Packages section of a workflow maps Properties page. Adding new data types to Livelink also lets you customize the Livelink interface. For example, if the work packages that you include with your workflows usually contain many attributes, you can create a data type that creates the attributes and groups them on tabs in the Livelink interface. This makes it easy for workflow participants to locate and use the attributes in the work package. You add new data types to Livelink by orphaning three objects: WFMain:WFRoot:WFObjectTypes:WFDataTypes, WebWFP:WebWFPRoot:WFPackage, and WebWork:WebWorkRoot:WFPackage. The WFMain:WFRoot:WFObjectTypes:WFDataTypes object is the API object for the task type. The WebWFP:WebWFPRoot:WFPackage object controls the information required by the Workflow Painter to add the data type to a workflow map. The WebWork:WebWorkRoot:WFPackage object controls the operation of the data type in executing workflows. In this chapter, you will learn how to: Define the data types API object Define the data types Workflow Painter information Define the information required to route the data type through a workflow Add a new data type to your custom module

85

Defining the Data Types API Object

Defining the Data Types API Object


You begin creating a data type by defining its API object using the WFDataTypes object. This object contains the features and scripts that are required for the operation of the data type. You can find the WFDataTypes object in WFMain:WFRoot:WFObjectTypes. Note The WFDataTypes object acts as a class object that you can use to create many different data types. Create a child of the WFDataTypes object for each data type that you want to add to Livelink. For each data type that you create, you can modify the following features.
Table 5-1: Features Associated With the WFDataTypes Object Feature
fDataName

Description Stores the name of the custom tab that is displayed in the Livelink interface when you create a new data type. If a data type is attached to a workflow, its custom tab is displayed when: The creator of the workflow map edits a task on a Step Definition page in the Workflow Painter. Workflow participants access the work package of the executing workflow from their Personal Workspaces. Stores a unique integer that works with the fType feature to identify the object. The following objects also contain fSubType features: The WFPackage object, which is orphaned when you define the Workflow Painter information The WFPackage object, which is orphaned when you define the information that controls the operation of the data type in executing workflows The values of the fSubType features for the WFPackage objects must match the value of this fSubType feature. Stores a unique integer that works with the fSubType feature to identify the object. The following objects also contain fType features: The WFPackage object, which is orphaned when you define the Workflow Painter information The WFPackage object, which is orphaned when you define the information that controls the operation of the data type in executing workflows The values of the fType features for the WFPackage objects must match the value of this fType feature.

fSubType

fType

86

Developers Guide for Extending Livelink Workflow

Defining the Data Types API Object

Note When adding data types, Open Text recommends setting the fSubType values to 1 and the fType values to an integer higher than 10. For each data type that you create, you can modify the following scripts.
Table 5-2: Scripts Associated With the WFDataTypes Object Script
CheckTaskDone()

Description Verifies that a workflow participant has provided all of the data types required information before their input is saved and the workflow is routed to the next workflow participant. For example, if you create a data type that groups workflow attributes and you indicate that entries are required for some of those attributes, this script verifies that a workflow participant has provided information for those required attributes before the workflow is routed to the next workflow participant. This script returns a list that can contain two items: a Boolean value and an error message. The Boolean value specifies whether the required data has been provided. If the required data has not been provided, the Boolean value is set to FALSE and an error message identifying the required values that must be specified before Livelink can route the work package to the next workflow participant is returned. Creates a new instance of the data type when the data type is attached to a workflow map in the Workflow Painter. This script is called when the creator of a workflow map selects the custom data type check box in the Packages section of a workflow maps Properties page, and then clicks the Add to Workflow Definition button. Adds the data type to the work package of the specified workflow. This script is called when the initiator of a workflow clicks the workflow maps name in Livelink to begin the initiation process. Deletes any previous data that has been stored for the data type. This script is called when new values are specified for the data type. Returns the information about the data type that must be available to the initiator of a workflow when they work on the Start task. This script is called when the initiator of a workflow clicks the workflow maps name in Livelink to begin the initiation process. The initiator of a workflow to which a custom data type has been attached can click the custom data type tab and provide information for the data type before starting the workflow process. Because the initiator is the first workflow participant to provide data type information, this script does not have to retrieve any previous values that may have been stored for the data type. When the initiator clicks the Initiate button to start the workflow process, their input is saved and routed with the work package to the next workflow participant.

CreateNewInstance()

CreateWorkData()

DeleteWorkData()

LoadStartTaskWorkData()

Adding New Data Types

87

Defining the Data Types API Object

Table 5-2: Scripts Associated With the WFDataTypes Object Script


LoadTaskWorkData()

LoadWorkData()

RemoveWorkData()

SaveWorkData()

SetReviewData()

SetSubWorkData()

SetSubWorkReturnData()

Description Returns the information about the data type that must be available to workflow participants when they work on their tasks. This script is called when a workflow participant clicks the custom data type tab in the work package of an active workflow task. When a workflow participant clicks the custom data type tab, the information that was stored for the data type when the previous task was completed is retrieved and displayed. This script populates the data types values with the input of the previous workflow participant. It is called for all workflow tasksexcept the Start task. Note For information about displaying data type values for the Start task, see LoadStartTaskWorkData(). Returns information about a data type for the specified workflow. This script is called when a workflow manager views the detailed status of a workflow that contains this data type. It is also called by event trigger scripts throughout the execution of a workflow. Removes the data type from the specified workflow. This script is called when a workflow that contains the data type is deleted from Livelink. It is also called when a workflow manager modifies an executing workflow by removing the data type. Saves the data type information throughout the execution of a workflow. This script is called each time that a workflow participant updates the data type information. Stores the information about a data type that must be passed to the sub-workflow that is created when a workflow participant sends a task for review. This information is used by the SetSubWorkData() and SetSubWorkReturnData() scripts. Note For more information about sending a task for review, see the Livelink online help. Passes data type information from a workflow to a sub-workflow. This script is called when a sub-workflow starts or when a task is sent for review. Passes data type information from a sub-workflow back to a main workflow. This script is called when a sub-workflow finishes or when a task that has been sent for review finishes. This is the companion script to the SetSubWorkData() script.

88

Developers Guide for Extending Livelink Workflow

Defining the Data Types Workflow Painter Information

Defining the Data Types Workflow Painter Information


After you define the API object for the data type, you must provide the Workflow Painter with the information it requires to add the data type to the workflow map. You define the Workflow Painter information using the WFPackage object. You can find this object in
WebWFP:WebWFPRoot.

For each data type that you create, you can modify the following features.
Table 5-3: Features Associated With the WFPackage Object Feature
fSubType

Description Stores a unique integer that works with the fType feature to identify the object. The following objects also contain fSubType features: The WFDataTypes object, which is orphaned when you define the API object for the data type The WFPackage object, which is orphaned when you define the information that controls the operation of the data type in executing workflows The values of the fSubType features for the WFDataTypes object and the other WFPackage object must match the value of this fSubType feature. Stores a unique integer that works with the fSubType feature to identify the object. The following objects also contain fType features: The WFDataTypes object, which is orphaned when you define the API object for the data type The WFPackage object, which is orphaned when you define the information that controls the operation of the data type in executing workflows The values of the fType features for the WFDataTypes object and the other WFPackage object must match the value of this fType feature.

fType

Note When adding data types, Open Text recommends setting the fSubType values to 1 and the fType values to an integer higher than 10. For each data type that you create, you can modify the following scripts.
Table 5-4: Scripts Associated With the WFPackage Object Script
GetMapData()

Description Specifies the name and location of the HTML file that is displayed to the creator of the workflow map when they define data type information for tasks in the Workflow Painter. The HTML file called by this script is displayed when the creator of a workflow map clicks the custom data type tab on a Step Definition page when defining tasks in the Workflow Painter.

Adding New Data Types

89

Defining the Data Types Workflow Painter Information

Table 5-4: Scripts Associated With the WFPackage Object Script


PutMapData()

Description Saves the information specified on the HTML page that is referenced in the GetMapData() script. This script saves the settings that the creator of a workflow map specifies when they define the data type information for tasks in the Workflow Painter. This script is called when the creator of a workflow map adds the data type information that they specify on the custom data type page to the workflow definition (by clicking the Add to Workflow Definition button).

In addition to the features and scripts that you modify to define the Workflow Painter information for a data type, you must create the HTML file that is displayed to the creator of the workflow map when they define the data type information for each workflow task in the Workflow Painter. This HTML page is displayed when the creator of a workflow map clicks the custom data type tab on the Step Definition page when defining tasks in the Workflow Painter.

90

Developers Guide for Extending Livelink Workflow

Defining the Data Types Web Object

Defining the Data Types Web Object


After you define the API object and the Workflow Painter information for the data type, you must define the data types Web object. The Web object provides the information necessary to handle the data type when workflow participants manipulate its data in executing workflows. You define this information using the WFPackage object. You can find this object in WebWork:WebWorkRoot. Note The WFPackage object acts as a class object that you can use to create many data types. Create a child of the WFPackage object for each data type that you want to add to Livelink. For each data type that you create, you can modify the following features.
Table 5-5: Features Associated With the WFPackage Object Feature
fSubType

Description Stores a unique integer that works with the fType feature to identify the object. The following objects also contain fSubType features: The WFDataTypes object, which is orphaned when you define the API object for the data type The WFPackage object, which is orphaned when you define the Workflow Painter information for the data type The values of the fSubType features for the WFDataTypes object and the other WFPackage object must match the value of this fSubType feature. Stores a unique integer that works with the fSubType feature to identify the object. The following objects also contain fType features: The WFDataTypes object, which is orphaned when you define the API object for the data type The WFPackage object, which is orphaned when you define the Workflow Painter information for the data type The values of the fType features for the WFDataTypes object and the other WFPackage object must match the value of this fType feature.

fType

Note When adding data types, Open Text recommends setting the fSubType values to 1 and the fType values to an integer higher than 10.

Adding New Data Types

91

Defining the Data Types Web Object

For each data type that you create, you can modify the following scripts.
Table 5-6: Scripts Associated With the WFPackage Object Script
GetData()

Description Sets up the display of the HTML page that is loaded when the initiator of a workflow provides data type information for the Start task, when a workflow participant opens a workflow task in their Tasks list, and when a workflow manager views the detailed status for this type of task. This script sets up the tabs on the HTML page that is called when a Livelink user is working with the data stored by the data type for the workflow. Specifies the name and location of the HTML file that is displayed to the creator of a workflow map when they define a sub-workflow task that contains the custom data type in a workflow that also contains the custom data type. This HTML file is displayed when the creator of a workflow map clicks the custom data type tab on the Sub-Map Step Definition page. The Sub-Map Step Definition page lets the creator of a workflow map specify the information about the data type that the main workflow passes to a sub-workflow. This page is also displayed when a workflow participant sends a task for review. Generates the names of the custom data type tabs that are displayed when workflow participants work on their tasks. This script also generates the URLs that determine which HTML files are displayed when each tab is active. This script ensures that the correct page is displayed when a workflow participant clicks a custom data type tab in the work package of an active workflow task. Note You can access the work package by clicking a task name on the Tasks page in your Personal Workspace. Saves the data type information that the creator of a workflow map specifies on the Sub-Map Step Definition page, when defining data type information for a sub-workflow task. This script is called when the creator of the workflow map clicks the Add to Workflow Definition button on the custom data type page (accessed by clicking the custom data type tab on the Sub-Map Step Definition page in the Workflow Painter). Saves the information that is entered on the HTML page referenced by the GetData() script. The SaveData() script saves the information that the initiator of a workflow specifies for the Start task, the information that a workflow participant enters when working on this type of task in their Task list, and the information specified by the workflow manager when viewing the detailed status for this type of task.

GetSubmapData()

GetTabInfo()

PutSubmapData()

SaveData()

92

Developers Guide for Extending Livelink Workflow

Defining the Data Types Web Object

In addition to the features and scripts that you modify to define the information that controls the operation of a data type, you must create the HTML files that are displayed when: The creator of a workflow map defines the data type information for a sub-workflow task on the Sub-Map Step Definition page. A workflow participant accesses the custom data type tab in the work package of an active workflow task.

Adding New Data Types

93

Example 2: Adding a New Data Type

Example 2: Adding a New Data Type


This example describes how to create a new data type called Table Values. The Table Values data type organizes many workflow attributes into logical groups and displays them on a series of custom tabs. The Project tab contains workflow attributes that define the name, code, due date, and priority of a project. The Customer tab contains workflow attributes that define the name and contact information of a customer that is related to the project. The Project and Customer tabs are contained within the Table Values tab, which is created for the Table Values data type and contained in the work packages of workflows to which the Table Values data type has been added.

Figure 5-1: The Project Tab

The organization that the Table Values data type provides makes it easy for workflow participants to locate and use the workflow attributes for which they must provide values throughout the execution of a workflow. To use the Table Values data type, the creator of a workflow map must first attach the Table Values data type to the workflow map definition, and then define whether each workflow attribute is editable, required, or read-only for each task in the workflow map. The Table Values data type is added to a workflow map definition by selecting the Table Values check box on the workflow maps Properties page. The workflow attributes are defined by clicking the Table Values tab on the Step Definition page for each task in the workflow map, and then clicking Editable, Entry Required, or Read-Only in the corresponding workflow attribute fields.

94

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

Figure 5-2: The Table Values Data Type Tab in the Workflow Painter

If the creator of a workflow map clicks Editable in a workflow attribute field, the workflow participant responsible for completing that task may input new values for the attribute. If the creator of a workflow map clicks Entry Required in a workflow attribute field, the workflow participant responsible for completing that task must input new values for the attribute. If the creator of a workflow map clicks Read-Only in a workflow attribute field, the workflow participant responsible for completing that task cannot input new values for the attribute. When a workflow map that contains the Table Values data type is initiated in Livelink, the Table Values tab (containing the Project and Customer tabs) is added to the work package and routed to each workflow participant when their tasks become ready. As workflow participants work on tasks, they specify values for the workflow attribute fields on the Project and Customer pages. The information that each workflow participant provides is saved in external database tables and updated as the work package is routed from one participant to another. Each workflow participant can view the information provided by the workflow participant who completed the preceding taskexcept the workflow initiator (because there is no preceding task).

Creating the Database Tables


The values that workflow participants provide for the workflow attribute fields that are listed on the Project and Customer pages are stored in database tables that reside outside of the Livelink database. Before you create the Table Values data type, you must create these external tables. To create the Cust_Project and Cust_Customers database tables: 1. Orphan DBWizAPI:DBWizAPIRoot:ExecPkg in the custmod OSpace in your custom module, and name it CustModExecPkg. 2. Orphan DBWizAPI:DBWizAPIRoot:ModuleDBObject in the custmod OSpace in your custom module, and name it ModuleDBObject. 3. Create a child of the Custmod Root object (your custom modules root object), and name it CustModDBScripts.

Adding New Data Types

95

Example 2: Adding a New Data Type

4. Create three children of the CustModDBScripts object, and name them MSSQL, Oracle, and Sybase, respectively. The names of these child objects are case-sensitivetype them exactly as they appear in step 4. 5. In the CustModDBScripts object, create three Dynamic features, and name them MSSQL, Oracle, and Sybase, respectively. 6. In the CustModDBScripts object, set the value of the MSSQL feature to the object reference number of the MSSQL object that you created in step 4. 7. Set the value of the Oracle feature to the object reference number of the Oracle object that you created in step 4. 8. Set the value of the Sybase feature to the object reference number of the Sybase object that you created in step 4. 9. In the ModuleDBObject, do the following: Ensure that the fDBVersion feature is a list, and set its value to {8, 0, 1}. This list represents the version of the database in which you are creating the tables. Set the value of the fEnabled feature to TRUE. Set the value of the fExecPkg feature to the object reference number of the CustModExecPkg object that you created in step 1. Ensure that the fInstallScripts feature is a List type, and set its value to
{'cust_sql'}.

Set the value of the fModuleName feature to custmod. Set the value of the fName feature to Custom Module. Set the value of the fScripts feature to the object reference number of the CustModDBScripts object that you created in step 3. Ensure that the fUninstallScripts feature is a List type, and set its value to {'cust_drop'}.

10. In the Custmod Globals object, run the BuildOSpace() script. 11. In the CustModDBScripts object, create a script and name it cust_sql. This script is used to create the tables when you install your custom module. For more information about the cust_sql() script, see cust_sql(), page 97. 12. In the CustModDBScripts object, create a script and name it cust_drop. This script is used to delete the tables from the database when you uninstall your custom module. For more information about the cust_drop() script see cust_drop(), page 98.

96

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

13. In the Configure object, set the value of the fHasDBSchema feature to TRUE. 14. Uninstall and reinstall the custmod module. Tip You may have to manually uninstall the custmod module and then reinstall it before your changes are registered with the Livelink database.

cust_sql()
The following code sample can be used as a prototype for the script that creates the tables in your database when you install your custom module.
#ifdef COMPILEME create table %Prefix%Cust_Project ( WorkflowID %type_int% Project_Name %type_char_64% Priority %type_int% ID_Code %type_char_32% DueDate %type_date% ) / create index Cust_Project_Primary on %Prefix%Cust_Project ( WorkflowID ) / create table %Prefix%Cust_Customers ( WorkflowID %type_int% Name %type_char_64% Addr1 %type_char_32% Addr2 %type_char_32% City %type_char_32% State %type_char_32% Zip %type_char_32% Phone %type_char_32% Fax %type_char_32% ) /

%not_null%, %null%, %null%, %null%, %null%

%not_null%, %null%, %null%, %null%, %null%, %null%, %null%, %null%, %null%

create index Cust_Customers_Primary on %Prefix%Cust_Customers ( WorkflowID ) / insert into %Prefix%KIni values( '%IniSection%', '%IniKeyword%', '{8,0,1}' ) / %commit_command% / #endif

Adding New Data Types

97

Example 2: Adding a New Data Type

cust_drop()
The following code sample can be used as a prototype for the script that removes the tables from the database when you uninstall your custom module.
#ifdef COMPILEME drop table %Prefix%Cust_Project / drop table %Prefix%Cust_Customers / delete from KIni where IniSection = '%IniSection%' and IniKeyword = '%IniKeyword%' / %commit_command% / #endif

Creating a Utility Script


The Table Values data type requires a utility script that you must create before you can implement the new data type. This script can be created at any point throughout the implementation of the data type. To create the utility script: 1. Create a child object of the Custmod Root object, and name it CustmodPkg. 2. Right click the CustmodPkg object, and click Add To Globals. 3. In the CustmodPkg object, create a script named SetSubPaneIndexArg. For more information about this script, see the code sample that follows. 4. In the Custmod Globals object, run the BuildOSpace() script.

98

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

SetSubPaneIndexArg
The following code sample describes how to create a utility script required by the Table Values data type.
Function String SetSubPaneIndexArg( \ String url, \ Integer paneIndex ) List findInfo PatChange change PatFind findPat String tmp change = Pattern.CompileChange( '#1' + Str.String( paneIndex ) ) findPat = Pattern.CompileFind( '<&custpaneindex=><[0-9]+>' ) findInfo = Pattern.Find( url, findPat, True ) if ( IsDefined( findInfo ) ) url = Pattern.Change( url, findPat, change, True ) else url = Str.Format( '%1&custpaneindex=%2', \ url, \ paneIndex ) end return( url ) end

Defining the Data Types API Object


You begin adding the Table Values data type to Livelink by defining the data types API object. To define the data types API object: 1. Orphan WFMain:WFRoot:WFObjectTypes:WFDataTypes in the custmod OSpace in your custom module, and name it WFDataTypes. 2. Create a child of the WFDataTypes object, and name it TableValues. 3. In the TableValues object, change the fSubType feature to an Integer/Real type, and set its value to 1. The fSubType feature stores a unique integer that works with the fType feature to identify the API object. 4. Change the fType feature to an Integer/Real type, and set its value to 11. The fType feature stores a unique integer that works with the fSubType feature to identify the object. 5. Ensure that the fDataName feature is a String type, and set its value to Table Values.

Adding New Data Types

99

Example 2: Adding a New Data Type

The fDataName feature stores the text that is displayed on the custom data type tab in the Livelink interface. 6. Override the following scripts:
CheckTaskDone() CreateNewInstance() CreateWorkData() DeleteWorkData() LoadStartTaskWorkData() LoadTaskWorkData() LoadWorkData() RemoveWorkData() SaveWorkData() SetReviewData() SetSubWorkData() SetSubWorkReturnData()

For more information about these scripts, see the code samples that follow. 7. Create a script, and name it LoadTableValues. For more information about the LoadTableValues() script, see LoadTableValues(),page 105. 8. Create a script, and name it SaveTableValues. For more information about the SaveTableValues() script, see SaveTableValues()page 108. 9. Create a script, and name it UpdateSubWorkData. For more information about the UpdateSubWorkData() script, see UpdateSubWorkData(),page 111. 10. Create a script, and name it UpdateTableValues. For more information about the UpdateTableValues() script, see UpdateTableValues(),page 112. You have created a data types API object. Now you must provide the code required to customize the API object for the Table Values data type.

100

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

CheckTaskDone()
The following code sample describes how to verify that a workflow participant has specified values in all of the required workflow attribute fields on the Project and Customer tabs before the workflow is routed to the next workflow participant.
Function List CheckTaskDone( \ Object prgCtx, \ Dynamic data ) String msg String name List retVal = { True, '' } //Examine each workflow attribute field on the Project and //Customer pages on the Table Values tab, and determine which //fields are required. If a value has not been entered in a //required field, return a message, indicating that a value is //required for that field. for name in Assoc.Keys( data.Fields ) if ( name in data.Required ) if ( !IsDefined( data.fields.Name ) ) msg = Str.Format( 'A Value is required for the' + \ 'table value %1.', \ Str.Quote( name, '"' ) ) retVal = { False, msg } break end //If the creator of a workflow map specified that the //Customer field is a required field for this task (which //means that the entire Customer tab is displayed to //workflow participants in the work package), verify that //the workflow participant has entered a value in the //Customer Name field on the Customer tab. If a value has //not been entered, return a message indicating that a name //for the customer is required. if ( Str.Upper( name ) == 'CUSTOMER' ) if ( !IsDefined( data.Fields.Customer.Name ) ) msg = 'You must enter a name for the customer.' retVal = { False, msg } break end end end end return( retVal ) end

Adding New Data Types

101

Example 2: Adding a New Data Type

CreateNewInstance()
The following code sample describes how to create a new instance of the Table Values data type when it is attached to a workflow map in the Workflow Painter.
Function Dynamic CreateNewInstance( \ Object prgCtx, \ Dynamic map = Undefined, \ Dynamic info = Undefined ) Assoc retVal String name //Create a list named fields, which stores the names of the //fields that are displayed on the Table Values tab when the //creator of the workflow map edits a step in the Workflow //Painter. Then create a list named cust_fields that stores the //names of the fields that are displayed on the Customer tab. List fields = { 'Project_Name', 'Priority', 'DueDate', \ 'Customer', 'ID_Code' } List cust_fields = { 'Name', 'Addr1', 'Addr2', 'City', \ 'State', 'Zip', 'Phone', 'Fax' } //Create an Assoc that stores all the field names. Then set all //field values to Undefined. retVal.Fields = Assoc.CreateAssoc() for name in fields retVal.Fields.( name ) = Undefined end //Set the default value of the Priority field to 3, which is //low priority. retVal.Fields.Priority = 3 //Create an Assoc that stores the field names for the Customer //tab (Customer Name, Address 1, Address 2, City, State, Zip //Code, Phone, and Fax). Then set all field values to Undefined. retVal.Fields.Customer = Assoc.CreateAssoc() for name in cust_fields retVal.Fields.Customer.( name ) = Undefined end retVal.Required = {} retVal.NonEditable = {} return( retVal ) end

102

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

CreateWorkData()
The following code sample describes how to add the Table Values data type to the work package of the specified workflow.
Function Boolean CreateWorkData( \ Object prgCtx, \ WAPIWORK work, \ List wfInfo, \ Dynamic data ) Boolean Record success r

List info = { .fType, .fSubType, wfInfo[ 2 ] } Object session = prgCtx.WSession() success = session.StartTrans() //Call the ReadyForModification() script to verify that the data //type is in a format that can be saved. if ( success ) data = .ReadyForModification( prgCtx, data ) //Add the Table Values data type to the specified workflow. success = $WFMain.WAPIPkg.AddWorkDataPackage( work, \ 'TableValues', info ) //Call the SaveTableValues() script to save the values that a //workflow participant specifies for the workflow attribute //fields on the Customer and Project pages. if ( success ) success = .SaveTableValues( prgCtx, wfInfo[ 2 ], data ) end if ( !session.EndTrans( success ) ) success = False end end return( success ) end

DeleteWorkData()
The following code sample describes how to delete any previous data that has been stored for the custom workflow attributes in the database tables. This script is called by the UpdateTableValues() script when Livelink updates the data in the database tables.
Function Boolean DeleteWorkData( \ Object prgCtx, \ Integer workID )

Adding New Data Types

103

Example 2: Adding a New Data Type

Boolean Dynamic Object Object

success sqlResult session = prgCtx.WSession() connect = session.fDbConnect

//Delete the row in the database table that stores the previous //values for the workflow attribute fields on the Customer tab. sqlResult = CAPI.Exec( connect.fConnection, \ 'Delete from Cust_Customers where ' + \ 'WorkflowID = :A1', \ workID ) success = session.CheckRetVal( sqlResult ) //Delete the row in the database table that stores the previous //values for the workflow attribute fields on the Project tab. if ( success ) sqlResult = CAPI.Exec( connect.fConnection, \ 'Delete from Cust_Project where ' + \ 'WorkflowID = :A1', \ workID ) success = session.CheckRetVal( sqlResult ) end return( success ) end

LoadStartTaskWorkData()
The following code sample describes how to load the information about the data type that is required to display the Table Values tab to the initiator of the workflow when they work on the Start task. This script does not load data from the database tables because no values have previously been specified. Instead, it loads information about each workflow attribute field on the Project and Customer pages on the Table Values tab (that is, whether each field is editable, required, or read-only). Note The creator of a workflow map specifies whether the workflow attribute fields for each task in a workflow are editable, required, or read-only when they define tasks in the Workflow Painter.
Function Dynamic LoadStartTaskWorkData( \ Object prgCtx, \ Record taskInfo, \ Dynamic data, \ Integer holderID ) Dynamic taskData Dynamic retVal = data taskData = taskInfo.FORM retVal.NonEditable = {} retVal.Required = {} //Determine which workflow attribute fields are read-only. Ready//only fields cannot be edited by the initiator of the workflow. if ( IsDefined( taskData ) ) if ( IsDefined( taskData.NONEDITABLE_TABLE_VALUES ) )

104

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

retVal.NonEditable = taskData.NONEDITABLE_TABLE_VALUES end //Determine which workflow attribute fields are required. //Required fields must be edited by the initiator of the //workflow. if ( IsDefined( taskData.REQUIRED_TABLE_VALUES ) ) retVal.Required = taskData.REQUIRED_TABLE_VALUES end end return( retVal ) end

LoadTableValues()
The following code sample describes how to create a script that loads information about the Table Values data type that is stored in the database tables. This script is called by the LoadTaskWorkData() script to retrieve the previously specified values for the workflow attribute fields on the Project and Customer pages on the Table Values tab in the work package for a particular workflow. This ensures that the most recent values are displayed on the Project and Customer pages throughout the execution of the workflow.
Function Assoc LoadTableValues( \ Object prgCtx, \ Integer workID ) Assoc project Assoc retVal Dynamic sqlResult Object Object session = prgCtx.WSession() connect = session.fDbConnect

//Retrieve the current values of the workflow attribute fields on //the Project page for this workflow from the database table. //The workflow ID is used to determine the current values of the //workflow attributes for this particular workflow. sqlResult = CAPI.Exec( connect.fConnection, \ 'select * from Cust_Project where WorkflowID = :A1', \ workID ) //Store the information that you retrieved from the database //tables in an Assoc named project. if ( session.CheckRetVal( sqlResult ) && Length( sqlResult ) ) project = Assoc.FromRecord( sqlResult[ 1 ] ) //Remove the workflow ID from the project Assoc. Assoc.Delete( project, 'WorkflowID' ) //Retrieve the values of the workflow attribute fields on the //Customer page for this workflow from the database table. //The workflow ID is used to determine the current values of //the workflow attributes for this particular workflow.

Adding New Data Types

105

Example 2: Adding a New Data Type

sqlResult = CAPI.Exec( connect.fConnection, \ 'select * from Cust_Customers where ' + \ 'WorkflowID = :A1', \ workID ) //Store the information that you retrieved from the database //tables in an Assoc named project.Customer. if ( session.CheckRetVal( sqlResult ) && Length( sqlResult ) ) project.Customer = Assoc.FromRecord( sqlResult[ 1 ] ) //Remove the workflow ID from the project.Customer Assoc. Assoc.Delete( project.Customer, 'WorkflowID' ) retVal.Fields = project retVal.NonEditable = {} retVal.Required = {} retVal.WorkflowID = workID else //If unsuccessful, return an error. retVal = sqlResult end else //If unsuccessful, return an error. retVal = sqlResult end return( retVal ) end

LoadTaskWorkData()
The following code sample describes how to load information about the Table Values data type from the database tables. This script calls another script named LoadTableValues(), which populates the fields on the Project and Customer pages (based on the workflow maps ID) throughout the execution of a workflow. For more information about the LoadTableValues() script, see LoadTableValues(), page 105.
Function Dynamic LoadTaskWorkData( \ Object prgCtx, \ WAPIWORK work, \ Record taskInfo, \ Dynamic data ) Dynamic taskData

//Call the LoadTableValues() script. Assoc retVal = .LoadTableValues( prgCtx, data ) if ( !IsError( retVal ) ) taskData = taskInfo.MAPTASK_FORM

106

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

//Store the read-only values. if ( IsDefined( taskData ) ) if ( IsDefined( taskData.NONEDITABLE_TABLE_VALUES ) ) retVal.NonEditable = taskData.NONEDITABLE_TABLE_VALUES end //Store the required values. if ( IsDefined( taskData.REQUIRED_TABLE_VALUES ) ) retVal.Required = taskData.REQUIRED_TABLE_VALUES end end end return( retVal ) end

LoadWorkData()
This code sample describes how to return information about the Table Values data type for the specified workflow.
Function Dynamic LoadWorkData( \ Object prgCtx, \ WAPIWORK work, \ Dynamic data ) //Call the LoadTableValues() script. Dynamic retVal = .LoadTableValues( prgCtx, data ) return( retVal ) end

RemoveWorkData()
This code sample describes how to remove the Table Values data type from the specified workflow.
Function Boolean RemoveWorkData( \ Object prgCtx, \ WAPIWORK work, \ Dynamic data ) //Delete the Table Values data type. Boolean success = $WFMain.WAPIPkg.RemoveWorkDataPackage( \ work, 'TableValues' ) //Call the DeleteWorkData() script to delete the previous values //that may have been stored for the Table Values data type in the //database tables. if ( success ) .DeleteWorkData( prgCtx, data ) end return( success ) end

Adding New Data Types

107

Example 2: Adding a New Data Type

SaveTableValues()
This code sample describes how to create a script that saves the values that a workflow participant specifies for the workflow attribute fields on the Customer and Project pages. This script is called by the UpdateTableValues() script after the previous values have been deleted from the database by the DeleteWorkData() script. Notes For more information about the UpdateTableValues() script, see UpdateTableValues(), page 112. For more information about the DeleteWorkData() script, see DeleteWorkData(), page 103.
Function Boolean SaveTableValues( \ Object prgCtx, \ Integer workID, \ Assoc data ) Boolean Dynamic success sqlResult

Assoc fields = data.Fields Assoc customer = data.Fields.Customer Object session = prgCtx.WSession() Object connect = session.fDbConnect //In the database tables, store the workflow ID, along with the //values of the Project Name, Project Code, Due Date, and //Priority fields, as specified on the Project page at the time //that this script is called. sqlResult = CAPI.Exec( connect.fConnection, \ 'insert into Cust_Project( WorkflowID,' + \ 'Project_Name, Priority, DueDate, ID_Code ) ' + \ 'values ( :A1, :A2, :A3, :A4, :A5 )', \ workID, \ fields.Project_Name, \ fields.Priority, \ fields.DueDate, \ fields.ID_Code ) success = session.CheckRetVal( sqlResult ) //In the database tables, store the workflow ID, along with the //values of the Customer Name, Address 1, Address 2, City, State, //Zip Code, Phone, and Fax fields, as specified on the Customer //page at the time that this script is called. if ( success ) sqlResult = CAPI.Exec( connect.fConnection, \ 'insert into Cust_Customers( WorkflowID,' + \ 'Name, Addr1, Addr2, City, State, Zip, Phone,' + \ 'Fax ) ' + \ 'values ( :A1, :A2, :A3, :A4, :A5, :A6, :A7,' + \ ':A8, :A9 )', \ workID, \

108

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

customer.Name, \ customer.Addr1, \ customer.Addr2, \ customer.City, \ customer.State, \ customer.Zip, \ customer.Phone, \ customer.Fax ) success = session.CheckRetVal( sqlResult ) end return( success ) end

SaveWorkData()
The following code sample describes how to update the information stored in the database tables for the Table Values data type. This script calls the UpdateTableValues() script which then calls the DeleteWorkData() script (to remove the previous information) and the SaveTableValues() script (to add the new information).
Function Boolean SaveWorkData( \ Object prgCtx, \ WAPIWORK work, \ Record taskRec, \ Dynamic data ) Boolean success Record r Object session = prgCtx.WSession() success = session.StartTrans() //Call the UpdateTableValues() script. if ( success ) success = .UpdateTableValues( prgCtx, data ) if ( !session.EndTrans( success ) ) success = False end end return( success ) end

SetReviewData()
The following code sample describes how to store information about the Table Values data type that must be passed to the sub-workflow that is created when a workflow participant sends a task for review.
Function Dynamic SetReviewData( \ Object prgCtx, \ Dynamic data ) //If the data that you are passing to the sub-workflow for review //is Undefined, pass all the data. Dynamic retVal return( retVal ) end

Adding New Data Types

109

Example 2: Adding a New Data Type

SetSubWorkData()
The following code sample describes how to retrieve the information about the Table Values data type that the main workflow passes to a sub-workflow.
Function Boolean SetSubWorkData( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer subWorkID, \ Integer taskID, \ Integer returnID, \ RecArray pkgs, \ Dynamic data ) List List Record Boolean mainAttribNames subAttribNames r success = True

//Locate the Table Values data type in the RecArray of data types //that have been added to the workflow. Then call the //UpdateSubWorkData() script which passes the data from the main //workflow to the sub-workflow. for r in pkgs if ( ( r.TYPE == .fType ) && ( r.SUBTYPE == .fSubType ) ) success = .UpdateSubWorkData( \ prgCtx, \ r.USERDATA, \ returnID, \ data ) break end end return( success ) end

SetSubWorkReturnData()
The following code sample describes how to determine what type of information must be passed from a sub-workflow back to the main workflow.
Function Boolean SetSubWorkReturnData( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer returnWorkID, \ Integer returnTaskID, \ RecArray pkgs, \ Dynamic data ) Record Boolean r success = True

110

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

//Locate the Table Values data type in the RecArray of data types //that have been added to the workflow. Then call the //UpdateSubWorkData() script which passes information from the //sub-workflow back to the main workflow. for r in pkgs if ( ( r.TYPE == .fType ) && ( r.SUBTYPE == .fSubType ) ) success = .UpdateSubWorkData( \ prgCtx, \ returnWorkID, \ r.USERDATA, \ data ) break end end return( success ) end

UpdateSubWorkData()
The following code sample describes how to create a script that passes data from the Table Values data type from one workflow to another.
Function Boolean UpdateSubWorkData( \ Object prgCtx, \ Integer toID, \ Integer fromID, \ List fields ) // The fields to pass. Undefined = all Assoc data Assoc subWorkData String name Boolean Boolean success = True updateAll = !IsDefined( fields )

//Call the LoadTableValues() script to load the information about //the Table Values data type from the database tables. data = .LoadTableValues( prgCtx, fromID ) success = !IsError( data ) if ( success ) if ( !updateAll ) updateAll = True //Determine which fields to pass from one workflow to another. for name in Assoc.Keys( data.Fields ) if ( !( name in fields ) ) updateAll = False break end end end

Adding New Data Types

111

Example 2: Adding a New Data Type

if ( !updateAll ) subWorkData = .LoadTableValues( prgCtx, toID ) success = !IsError( data ) //Set the values of the workflow attributes in the new workflow //equal to the values of the workflow attributes in the current //workflow. if ( success ) for name in fields subWorkData.Fields.( name ) = data.Fields.( name ) end end data = subWorkData end if ( success ) //Call the DeleteWorkData() script to delete any previous //data that has been stored for the custom workflow //attributes in the database tables. success = .DeleteWorkData( prgCtx, toID ) end if ( success ) //Save the new data. success = .SaveTableValues( prgCtx, toID, data ) end end return( success ) end

UpdateTableValues()
The following code sample describes how to create a script that updates the values that workflow participants specify for each workflow attribute field on the Project and Customer pages. This script is called throughout the execution of a workflow to update the information for the Table Values data type in the database tables.
Function Boolean UpdateTableValues( \ Object prgCtx, \ Dynamic data ) //Call the DeleteWorkData() script to delete any previously saved //workflow attribute data that has been stored in the database //tables. Boolean success = .DeleteWorkData( prgCtx, data.WorkflowID )

//Call the SaveTableValues() script to add the new workflow //attribute values to the database tables.

112

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

if ( success ) success = .SaveTableValues( prgCtx, data.WorkflowID, data ) end return( success ) end

Defining the Data Types Workflow Painter Information


After you define the API object for the data type that you are creating, you must provide the Workflow Painter with the information it requires to add the data type to a workflow map. To define the data types Workflow Painter information: 1. Orphan WebWFP:WebWFPRoot:WFPackage in the custmod OSpace in your custom module, and name it WFPTableValues. 2. In the WFPTableValues object, change the fSubType feature to an Integer/Real type and set its value to 1. The fSubType feature stores a unique integer that works with the fType feature to identify this object. 3. Change the fType feature to an Integer/Real type and set its value to 11. The fType feature stores a unique integer that works with the fSubType feature to identify this object. 4. Override the following scripts:
GetMapData() PutMapData()

For more information about these scripts, see the code samples that follow. 5. Create an HTML file, and name it t_tablevalues.html. For more information about this HTML file, see t_tablevalues.html, on page 115. You have created the object necessary to define a data types Workflow Painter information. Now you must provide the code required to customize the object for the Table Values data type.

Adding New Data Types

113

Example 2: Adding a New Data Type

GetMapData()
The following code sample specifies the name and location of the HTML file that is displayed to the creator of a workflow map when they define the Table Values data type information for each task in a workflow map.
function Assoc GetMapData( \ Object prgCtx, \ Dynamic context, \ Dynamic data ) Dynamic retVal RecArray array //Create an Assoc that stores the name and location of the HTML //file that is displayed when the creator of a workflow map //clicks the Table Values tab on a Step Definition page in the //Workflow Painter. The file is called t_tablevalues.html and is //stored in the /html folder in the custmod module directory //structure (for example, c:/opentext/module/custmod_1_0_0/html). if ( IsDefined( data ) ) retVal = Assoc.CreateAssoc() retVal.HTMLFile = 't_tablevalues.html' retVal.ModuleName = 'custMod' retVal.Data = Assoc.CreateAssoc() retVal.Data.taskInfo = context retVal.Data.Data = data end return( retVal ) end

PutMapData()
The following code sample describes how to save the information specified on the HTML page that is referenced in the GetMapData() script (that is, t_tablevalues.html). This script saves the settings that the creator of a workflow map specifies when they define whether each workflow attribute field is editable, required, or read-only for a particular workflow task.
function Assoc PutMapData( \ Object prgCtx, \ Dynamic context, \ Dynamic data, \ Record r ) Assoc retVal List nonEditable List required String name String key //Determine whether each workflow attribute field is editable, //required, or read-only.

114

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

for name in Assoc.Keys( data.Fields ) key = 'TV_' + name if ( IsFeature( r, key ) ) if ( r.( key ) == 'ReadOnly' ) nonEditable = { @nonEditable, name } elseif ( r.( key ) == 'Required' ) required = { @required, name } end end end //Store the information about the workflow attribute fields in an //Assoc named context.Form so that it can be accessed when the //workflow is initiated. if IsUndefined( context.Form ) context.Form = Assoc.CreateAssoc() end context.Form.NONEDITABLE_TABLE_VALUES = nonEditable context.Form.REQUIRED_TABLE_VALUES = required retVal.OK = TRUE return retVal end

t_tablevalues.html
The following HTML file is displayed to the creator of a workflow map when they define tasks in a workflow map that contains the Table Values data type. This HTML page lets the creator of a workflow map specify whether each field on the Table Values page (accessed from the Step Definition page) is editable, required, or read-only.
;;webscript t_tablevalues( Dynamic data ) <!-- File: custmod/t_tablevalues.html --> ;;oscript{ String a String selected Integer i = 1 Record theTask = data.taskInfo Assoc theForm = theTask.Form if ( !IsDefined( theForm ) ) theForm = Assoc.CreateAssoc() end ;;} <TABLE BORDER="0" CELLPADDING="2" CELLSPACING="1"> ;for a in Assoc.Keys( data.data.Fields ) <TR> <TD bgcolor="#CCCCCC" NOWRAP VALIGN="CENTER"><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`a`:&nbsp;</FONT></TD> <TD>

Adding New Data Types

115

Example 2: Adding a New Data Type

<SELECT NAME="TV_`a`" ONCHANGE="markTaskEditDirty();"> //Add the Editable option to the popup menu that is displayed for //each workflow attribute on the Table Values page when the creator //of a workflow map defines the task. ;selected = ( !( a in theForm.REQUIRED_TABLE_VALUES ) && !( a in theForm.NONEDITABLE_TABLE_VALUES ) ) ? " SELECTED" : "" <OPTION VALUE="Editable"`selected`>`[WebWFP_HTMLLabel.Editable]` //Add the Entry Required option to the popup menu that is displayed //for each workflow attribute on the Table Values page when the //creator of a workflow map defines the task. ;selected = ( ( selected == "" ) && ( a in theForm.REQUIRED_TABLE_VALUES ) ) ? " SELECTED" : "" <OPTION VALUE="Required"`selected`>`[WebWFP_HTMLLabel.EntryRequired]` //Add the Read-Only option to the popup menu that is displayed for //each workflow attribute on the Table Values page when the creator //of a workflow map defines the task. ;selected = ( ( selected == "" ) && ( a in theForm.NONEDITABLE_TABLE_VALUES ) ) ? " SELECTED" : "" <OPTION VALUE="ReadOnly"`selected`>`[WebWFP_HTMLLabel.ReadOnly]` </SELECT> &nbsp; </TD> </TR> ;i += 1 ;end //Set up the Action row, which contains the Add to Workflow //Definition button. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebDoc_HTMLLabel.Action_]`&nbsp;</FONT></TD> <TD> <INPUT TYPE="Submit" VALUE="`[WebWFP_HTMLLabel.AddToWorkflowDefinitionButtonLabel]`"> </TD></TR> </TABLE> ;;end

116

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

Defining the Data Types Web Object


After you define the API object and the Workflow Painter information for the data type that you are creating, you must define the data types Web object. To define the data types Web object: 1. Orphan WebWork:WebWorkRoot:WFPackage in the custmod OSpace in your custom module, and name it WorkTableValues. 2. In the WorkTableValues object, change the fSubType feature to an Integer/Real type, and set it to 1. The fSubType feature stores a unique integer that works with the fType feature to identify this object. 3. Change the fType feature to an Integer/Real type, and set its value to 11. The fType feature stores a unique integer that works with the fSubType feature to identify this object. 4. In the Custmod Globals object, run the BuildOSpace() script. 5. Override the following scripts:
GetData() GetSubmapData() GetTabInfo() PutSubmapData() SaveData()

For more information about these scripts, see the code samples that follow. 6. Create an HTML file, and name it submap_tablevalues.html. For more information about this HTML file, see submap_tablevalues.html, page 123. 7. Create an HTML file, and name it tablevalues.html. For more information about this HTML file, see tablevalues.html, page 126. 8. Create an HTML file, and name it projectpane.html. For more information about this HTML file, see projectpane.html, page 126. 9. Create an HTML file, and name it customerpane.html. For more information about this HTML file, see customerpane.html, page 129 You have created the object that is used to control a data type when it is manipulated in a workflow by workflow participants. Now you must provide the code required to customize the object for the Table Values data type.

Adding New Data Types

117

Example 2: Adding a New Data Type

GetData()
The following code sample describes how to set up the HTML page that is loaded when the initiator of a workflow specifies values for the workflow attributes on the Project and Customer pages just before the first task is routed to the corresponding workflow participant.
function Assoc GetData( \ Object prgCtx, \ Dynamic context, \ Dynamic data, \ Record request, \ Boolean forStatus = False ) Assoc paneData Assoc tabPaneInfo Assoc tmp Dynamic retVal Integer whichTab RecArray array if ( IsDefined( data ) && Length( data ) ) retVal = Assoc.CreateAssoc() retVal.Data = data retVal.HTMLFile = 'tablevalues.html' retVal.ModuleName = 'custmod' whichTab = ( RecArray.IsColumn( request, 'CustPaneIndex' \ ) ) ? Str.StringToInteger( request.CustPaneIndex ) : 1 //Set up the information required to display the Project tab. tmp.Label = 'Project' tmp.URL = $CustMod.CustModPkg.SetSubPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( request ), 1 ) tmp.Active = FALSE tabPaneInfo.TabList = { tmp } tmp = Assoc.CreateAssoc() tmp.ModuleName = 'custmod' tmp.HTMLFile = 'projectpane.html' tmp.Data = data tabPaneInfo.PaneList = { tmp } //Set up the information required to display the Customer tab. tmp = Assoc.CreateAssoc() tmp.Label = 'Customer' tmp.URL = $CustMod.CustModPkg.SetSubPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( request ), 2 ) tmp.Active = FALSE tabPaneInfo.TabList = { @tabPaneInfo.TabList, tmp } tmp = Assoc.CreateAssoc()

118

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

tmp.ModuleName = 'custmod' tmp.HTMLFile = 'customerpane.html' tmp.Data = data tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, tmp } //If the tab value is less than 2 or greater than the maximum //number of tabs, reset it to 1 so that the first tab is //displayed. if ( ( whichTab < 2 ) || ( whichTab > Length( \ tabPaneInfo.TabList ) ) ) whichTab = 1 end tabPaneInfo.TabList[ whichTab ].Active = True retVal.TabInfo = tabPaneInfo retVal.Tab = whichTab end return( retVal ) end

GetSubMapData()
The following code sample describes how to specify the name and location of the HTML file that is displayed to the creator of a workflow map when they define the sub-workflow task that contains the Table Values data type in a workflow that also contains the Table Values data type.
function Assoc GetSubmapData( \ Object prgCtx, \ Dynamic context, \ Dynamic data, \ Boolean loadData = True ) DAPINODE Dynamic Record Record String node retVal mapRec r mapName

Boolean found = False List passedValues = Undefined //Load the workflow map that will be used for the sub-workflow //task. if ( IsDefined( data ) ) if ( loadData ) if ( IsDefined( context.SUBMAPID ) ) node = DAPI.GetNodeByID( prgCtx.DapiSess(), \ DAPI.BY_DATAID, context.SUBMAPID, True ) if ( !IsError( node ) ) mapName = node.pName mapRec = $WFMain.WFMapPkg.LoadMap( prgCtx, { \

Adding New Data Types

119

Example 2: Adding a New Data Type

node, Undefined } ) //Determine whether the Table Values data type is included in the //work package of the sub-workflow task. if ( IsDefined( mapRec ) ) for r in mapRec.WORK_PACKAGES if ( ( r.Type == .fType ) && \ ( r.SubType == .fSubType ) ) found = True break end end end end end //If the Table Values data type is included in the work package //of the sub-workflow task, retrieve the data that must be passed //from the main workflow to the sub-workflow. if ( found ) for r in context.WORKPKGINFO if ( ( r.Type == .fType ) && \ ( r.SUBTYPE == .fSubType ) ) if ( IsDefined( r.USERDATA ) ) passedValues = r.USERDATA end break end end end end retVal = Assoc.CreateAssoc() retVal.Data = Assoc.CreateAssoc() retVal.Data.taskInfo = context retVal.Data.Data = data retVal.Data.Found = found retVal.Data.Title = mapName retVal.Data.PassedValues = passedValues retVal.HTMLFile = 'submap_tablevalues.html' retVal.ModuleName = 'custmod' end return( retVal ) end

GetTabInfo()
The following code sample describes how to generate the names of the custom data type tabs (Table Values, Project, and Customer) and the URLs required to display the correct HTML pages when workflow participants work on their tasks.
Function Assoc GetTabInfo( \ Object prgCtx, \ Record request, \ Dynamic data, \ Integer paneIndex )

120

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

Assoc

retVal

//Set up the URL that is called when workflow participants click //the Table Values tab when working on tasks in their Task list. String myURL = $WebDSP.HTMLPkg.ArgsToURL( request ) String url = $WebWork.WFPkg.SetPaneIndexArg( myURL, \ paneIndex ) if ( !IsFeature( request, 'subpaneindex' ) ) url = $CustMod.CustModPkg.SetSubPaneIndexArg( url, 1 ) end //Return an Assoc that contains the name of the tab, the URL that //is called when a workflow participant clicks the Table Values //tab, and any online help information associated with the tab. retVal.Label = .GetDataTypeObj().fDataName retVal.URL = url retVal.HelpKey = .GetDataTypeObj().OSName retVal.Active = FALSE return( retVal ) end

PutSubMapData()
The following code sample describes how to save the information that the creator of a workflow map specified when defining data type information for a sub-workflow task (on the Sub-Map Step Definition page).
function Assoc PutSubMapData( \ Object prgCtx, \ Dynamic context, \ Dynamic data, \ Record r ) Assoc commentData Assoc retVal List valsToPass Record pkgRec Record rec String name //Locate the Table Values data type in the RecArray of data types //associated with a workflow. for rec in context.WorkPkgInfo if ( ( rec.Type == .fType ) && ( rec.SubType == .fSubtype ) ) pkgRec = rec break end end //If information //been passed to //RecArray. This //that should be //workflow. about the Table Values data type has not yet the sub-workflow task, add a new Record to the Record contains the list of workflow attributes passed from the main workflow to the sub-

Adding New Data Types

121

Example 2: Adding a New Data Type

if ( !IsDefined( pkgRec ) ) pkgRec = $LLIAPI.RecArrayPkg.NewRecord( context.WORKPKGINFO ) pkgRec.Type = .fType pkgRec.SubType = .fSubType end //Save the list of workflow attributes that should be passed to //the sub-workflow. This is the same list of attributes that //determines which workflow attributes should be passed from the //sub-workflow back to the main workflow. for name in Assoc.Keys( data.Fields ) if ( IsFeature( r, name ) ) valsToPass = { @valsToPass, name } end end pkgRec.USERDATA = valsToPass retVal.OK = TRUE return( retVal ) end

SaveData()
The following code sample describes how to save the information that the initiator of a workflow specifies on the Project and Customer pages for the Start task before routing the workflow to the first workflow participant.
function Dynamic SaveData( \ Object prgCtx, \ Record request, \ Assoc data ) Assoc fields Assoc retVal String name Integer pane = Str.StringToInteger( request.CustPaneIndex ) retVal.OK = True //Save the values of the fields on the Project tab. if ( pane == 1 ) fields = data.Fields fields.Project_Name = request.project_name fields.ID_Code = request.ID_Code fields.DueDate = Str.StringToValue( request.DueDate ) fields.Priority = Str.StringToInteger( request.Priority ) //Save the values of the fields on the Customer tab. elseif ( pane == 2 ) fields = data.Fields.Customer for name in Assoc.Keys( fields ) if ( IsFeature( request, name ) ) fields.( name ) = Request.( name ) end end end retVal.Data = data return( retVal ) end

122

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

submap_tablevalues.html
The following HTML file is displayed when the creator of a workflow map defines a subworkflow task that contains the Table Values data type in a workflow that also contains the Table Values data type (by clicking the Table Values tab on the Sub-Map Step Definition page). It lets the creator of a workflow map specify the type of data that is passed between the main workflow and the sub-workflow.
;;webscript submap_tablevalues( Dynamic data ) <!-- File: custmod/submap_tablevalues.html --> //Edit table values for a sub-workflow map. This file assumes that //you are already in the context of an HTML form. ;Boolean noValues = !IsDefined( data.PassedValues ) ;List vals = data.PassedValues <TABLE BORDER="0" CELLPADDING="2" CELLSPACING="1"> //Set up the Table Values row. <TR> <TD bgcolor="#CCCCCC" NOWRAP VALIGN="TOP"><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;Table Values:&nbsp;</FONT></TD> //Create the Table Values (data exchange) heading in the //Table Values row. <TD> <TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0"> <TR> <TD><STRONG>Table Values (data exchange)</STRONG></TD> </TR> <TR> <TD HEIGHT="12">&nbsp;</TD> </TR> //Set up a check box to specify whether the project //name passes between the main workflow and the //sub-workflow. ;if ( data.Found ) <TR> ;if ( noValues || ( 'Project_Name' in vals ) ) ; checked = "CHECKED" ;else ; checked = "" ;end <TD HEIGHT="20"> <INPUT TYPE="CHECKBOX" `checked` NAME="Project_Name" ONCLICK="markTaskEditDirty();"> Project Name passes between main workflow and sub-workflow </TD>

Adding New Data Types

123

Example 2: Adding a New Data Type

</TR> //Set up a check box to specify whether the priority //values pass between the main workflow and the //sub-workflow. <TR> ;if ( noValues || ( 'Priority' in vals ) ) ; checked = "CHECKED" ;else ; checked = "" ;end <TD HEIGHT="20"> <INPUT TYPE="CHECKBOX" `checked` NAME="Priority" ONCLICK="markTaskEditDirty();"> Priority passes between main workflow and sub-workflow </TD> </TR> //Set up a check box to specify whether the due date //passes between the main workflow and the sub//workflow. <TR> ;if ( noValues || ( 'DueDate' in vals ) ) ; checked = "CHECKED" ;else ; checked = "" ;end <TD HEIGHT="20"> <INPUT TYPE="CHECKBOX" `checked` NAME="DueDate" ONCLICK="markTaskEditDirty();"> Due Date passes between main workflow and sub-workflow </TD> </TR> //Set up a check box to specify whether the project //code passes between the main workflow and the //sub-workflow. <TR> ;if ( noValues || ( 'ID_Code' in vals ) ) ; checked = "CHECKED" ;else ; checked = "" ;end <TD HEIGHT="20"> <INPUT TYPE="CHECKBOX" `checked` NAME="ID_Code" ONCLICK="markTaskEditDirty();"> Project Code passes between main workflow and sub-workflow </TD> </TR> //Set up a check box to specify whether the customer

124

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

//information passes between the main workflow and the //sub-workflow. <TR> ;if ( noValues || ( 'Customer' in vals ) ) ; checked = "CHECKED" ;else ; checked = "" ;end <TD HEIGHT="20"> <INPUT TYPE="CHECKBOX" `checked` NAME="Customer" ONCLICK="markTaskEditDirty();"> Customer Information passes between main workflow and sub-workflow </TD> </TR> ;else //If the Table Values data type is not attached to //the sub-workflow task, return a message to the //creator of the workflow map. <TR> <TD>The selected sub-map does not use the Table Values data type.</TD> </TR> ;end <TR> <TD>&nbsp;</TD> </TR> </TABLE> </TD> </TR> //Set up the Action row, which contains the Add to Workflow //Definition button. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebDoc_HTMLLabel.Action_]`&nbsp;</FONT></TD> <TD> <INPUT TYPE="Submit" VALUE="`[WebWFP_HTMLLabel.AddToWorkflowDefinitionButtonLabel]`"> </TD> </TR> </TABLE> ;;end

Adding New Data Types

125

Example 2: Adding a New Data Type

tablevalues.html
The following HTML file sets up the tabs that are displayed when a workflow participant clicks the Table Values tab in the work package of an executing workflow. This HTML page sets up the Project and Customer pages.
;;webscript tablevalues( Assoc a ) <!-- File: tablevalues.html --> ;Assoc ;Assoc ;String tabInfo = a.TabInfo paneInfo = tabInfo.PaneList[ a.Tab ] thisURL = $WebDSP.HTMLPkg.ArgsToURL( .fArgs )

<INPUT TYPE="HIDDEN" NAME="custPaneIndex" VALUE="1"> <SCRIPT LANGUAGE="JavaScript"> <!-function SubmitMyPage() {if ( document.myForm.xAction != null ) {document.myForm.xAction.value = 'Update';} document.myForm.NextURL.value = "`%LthisURL`"; document.myForm.paneIndex.value = '`.fArgs.PaneIndex`'; document.myForm.custPaneIndex.value = '`.fArgs.custPaneIndex`'; document.myForm.submit();} %// --> </SCRIPT> ;paneInfo.IsEditable = a.IsEditable ;;call <.HTMLPrefix() + 'tabber.html'>( 1, '#666666', tabInfo.TabList, Undefined, 'istedirty' ) ;;call <.ModHTMLPrefix( a.ModuleName ) + paneInfo.HTMLFile>( paneInfo ) ;;call <.HTMLPrefix() + 'tabber.html'>( 0, '#666666' ) ;;end

projectpane.html
The following HTML file sets up the information required to display the Project page when a workflow participant clicks the Project tab on the Table Values page in the work package for an executing workflow.
;;webscript projectpane( Assoc a ) <!-- File: projectpane.html --> ;String selected ;Assoc data = a.Data.Fields <TABLE BORDER="0" CELLPADDING="2" CELLSPACING="1" WIDTH="100%"> //Set up the Project Name row. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;Project Name:&nbsp;</FONT></TD>

126

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

;if ( a.IsEditable && !( 'Project_Name' in a.Data.NonEditable ) ) <TD NOWRAP> <INPUT TYPE="TEXT" SIZE="40" NAME="project_name" VALUE="`data.project_name`" ONCHANGE="markTaskEditDirty();"> </TD> ;else <TD>`data.project_name`</TD> ;end </TR> //Set up the Project Code row. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;Project Code:&nbsp;</FONT></TD> ;if ( a.IsEditable && !( 'ID_Code' in a.Data.NonEditable ) ) <TD NOWRAP> <INPUT TYPE="TEXT" SIZE="10" MAXLENGTH="5" NAME="ID_Code" VALUE="`data.ID_Code`" ONCHANGE="markTaskEditDirty();"> </TD> ;else <TD>`data.id_code`</TD> ;end </TR> //Set up the Due Date row. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;Due Date:&nbsp;</FONT></TD> ;if ( a.IsEditable && !( 'DueDate' in a.Data.NonEditable ) ) <TD NOWRAP> ;;call <.htmlPrefix() + 'datefield.html'>( "duedate", data.duedate, TRUE, TRUE ) </TD> ;else <TD NOWRAP>`.FmtDate( data.duedate, True )`</TD> ;end </TR> //Set up the Priority row. Then set up the values that can be //selected from the Priority list box (that is, High, Medium, //or Low). <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;Priority:&nbsp;</FONT></TD> ;if ( a.IsEditable && !( 'Priority' in a.Data.NonEditable ) ) <TD NOWRAP> <SELECT NAME="priority" ONCHANGE="markTaskEditDirty();"> ;selected = ( data.priority == 1 ) ? 'SELECTED' : '' <OPTION `selected` VALUE="1">High

Adding New Data Types

127

Example 2: Adding a New Data Type

;selected = ( data.priority == 2 ) ? 'SELECTED' : '' <OPTION `selected` VALUE="2">Medium ;selected = ( ( data.priority == 3 ) || !IsDefined( data.priority ) ) ? 'SELECTED' : '' <OPTION `selected` VALUE="3">Low </SELECT> </TD> ;else <TD NOWRAP> ;switch( data.priority ) ; case 1 High ; end ; ; ; case 2 Medium end default Low ; end ;end </TD> ;end </TR> ;if ( a.IsEditable ) //Set up the Action row, which contains the Update and Reset //buttons. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebDoc_HTMLLabel.Action_]`&nbsp;</FONT></TD> <TD> <INPUT TYPE="BUTTON" VALUE="`[WebDoc_HTMLLabel.UpdateButtonLabel]`" NAME="IgnoreMe" ONCLICK="SubmitMyPage();"> <INPUT TYPE="RESET" VALUE="`[WebDoc_HTMLLabel.Reset]`"> </TD> </TR> ;end </TABLE> ;;end

128

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

customerpane.html
The following HTML file sets up the information required to display the Customer page when a workflow participant clicks the Customer tab on the Table Values page in the work package of an executing workflow.
;;webscript customerpane( Assoc a ) <!-- File: customerpane.html --> ;Assoc data = a.Data.Fields.Customer ;Boolean isEditable = ( a.IsEditable && !( 'Customer' in a.Data.NonEditable ) ) <TABLE BORDER="0" CELLPADDING="2" CELLSPACING="1" WIDTH="100%"> //Set up the Customer Name row. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;Customer Name:&nbsp;</FONT></TD> ;if (isEditable ) <TD NOWRAP> <INPUT TYPE="TEXT" SIZE="40" NAME="name" VALUE="`data.name`" ONCHANGE="markTaskEditDirty();"> </TD> ;else <TD>`data.name`</TD> ;end </TR> //Set up the Address 1 row. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;Address1:&nbsp;</FONT></TD> ;if ( isEditable ) <TD NOWRAP> <INPUT TYPE="TEXT" SIZE="40" NAME="addr1" VALUE="`data.addr1`" ONCHANGE="markTaskEditDirty();"> </TD> ;else <TD>`data.addr1`</TD> ;end </TR> //Set up the Address 2 row. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;Address2:&nbsp;</FONT></TD> ;if ( isEditable )

Adding New Data Types

129

Example 2: Adding a New Data Type

<TD NOWRAP> <INPUT TYPE="TEXT" SIZE="40" NAME="addr2" VALUE="`data.addr2`" ONCHANGE="markTaskEditDirty();"> </TD> ;else <TD>`data.addr2`</TD> ;end </TR> //Set up the City row. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;City:&nbsp;</FONT></TD> ;if ( isEditable ) <TD NOWRAP> <INPUT TYPE="TEXT" SIZE="40" NAME="city" VALUE="`data.city`" ONCHANGE="markTaskEditDirty();"> </TD> ;else <TD>`data.city`</TD> ;end </TR> //Set up the State row. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;State:&nbsp;</FONT></TD> ;if ( isEditable ) <TD NOWRAP> <INPUT TYPE="TEXT" SIZE="40" NAME="state" VALUE="`data.state`" ONCHANGE="markTaskEditDirty();"> </TD> ;else <TD>`data.state`</TD> ;end </TR> //Set up the Zip Code row. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;Zip Code:&nbsp;</FONT></TD> ;if ( isEditable ) <TD NOWRAP> <INPUT TYPE="TEXT" SIZE="15" NAME="zip" VALUE="`data.zip`" ONCHANGE="markTaskEditDirty();"> </TD> ;else <TD>`data.zip`</TD> ;end </TR>

130

Developers Guide for Extending Livelink Workflow

Example 2: Adding a New Data Type

//Set up the Phone row. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;Phone:&nbsp;</FONT></TD> ;if ( isEditable ) <TD NOWRAP> <INPUT TYPE="TEXT" SIZE="20" NAME="phone" VALUE="`data.phone`" ONCHANGE="markTaskEditDirty();"> </TD> ;else <TD>`data.phone`</TD> ;end </TR> //Set up the Fax row. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;Fax:&nbsp;</FONT></TD> ;if ( isEditable ) <TD NOWRAP> <INPUT TYPE="TEXT" SIZE="20" NAME="fax" VALUE="`data.fax`" ONCHANGE="markTaskEditDirty();"> </TD> ;else <TD>`data.fax`</TD> ;end </TR> //Set up the Action row, which contains the Update and Reset //buttons. ;if ( isEditable ) <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebDoc_HTMLLabel.Action_]`&nbsp;</FONT></TD> <TD> <INPUT TYPE="BUTTON" VALUE="`[WebDoc_HTMLLabel.UpdateButtonLabel]`" NAME="IgnoreMe" ONCLICK="SubmitMyPage();"> <INPUT TYPE="RESET" VALUE="`[WebDoc_HTMLLabel.Reset]`"> </TD> </TR> ;end </TABLE> ;;end

Adding New Data Types

131

Example 2: Adding a New Data Type

132

Developers Guide for Extending Livelink Workflow

Chapter Six

Adding New Workflow Types


You add new workflow types to Livelink when you want to apply a particular set of custom operations to different workflow map definitions. Adding new workflow types lets you define the set of custom operations that are applied to a workflow map definition when the creator of a workflow map chooses to create that particular type of workflow in Livelink. These custom operations can include adding workflow steps, modifying the duration or name of existing workflow steps, adding callback scripts to workflow events, or any other modification that you want to make to a workflow map definition. The types of workflows that you create can be simple or complex, depending on your workflow requirements. For example, you can create a workflow type that appends the current date to the title of a workflow in Livelink or you can create a workflow type that runs a callback script when the workflow is complete. You can also create many different types of workflowswhich each perform different custom operations. The custom operations that you define for a workflow type are applied to a workflows map definition just before a workflow map of that type is initiated in Livelink. When you add a new type of workflow to Livelink, the Workflow Type list is displayed on the General tab of a workflow maps Properties page. The Workflow Type list lets the creators of workflow maps choose which type of workflow they want to create (that is, which custom operations they want to apply to their workflow map definition just before the workflow is initiated). In this chapter, you will learn how to: Create workflow types Add a workflow type to your custom module

133

Creating Workflow Types

Creating Workflow Types


You begin creating a new workflow type by orphaning the WFCustomScriptPkg object in an OSpace in your custom module. You can find the WFCustomScriptPkg object in WFMain:WFRoot. Note The WFCustomScriptPkg object acts as a superclass object that you can use to create new callback scripts, workflow types, and customized code that is executed before a workflow is initiated. Because the WFCustomScriptPkg object is a superclass object that can be used for many different workflow customizations, you will create a child of the WFCustomScriptPkg object and use it to create new workflow types, specifically. For each workflow type that you create, you can modify the following scripts.
Table 6-1: Scripts Associated With the WFCustomScriptPkg Object Script
GetWFTypeName()

Description Retrieves the name of the custom workflow type defined by the integer values of the wfType and wfSubType variables. If the values of the wfType and wfSubType variables correspond to the values of the wfType and wfSubType variables in the GetWFTypes() script, the corresponding workflow name is assigned to the workflow type and a variable named handled is set to TRUE. If the values of the wfType and wfSubType variables do not correspond to the values of the wfType and wfSubType variables in the GetWFTypes() script, a name is not assigned to the workflow type and the handled variable is set to FALSE. Defines the type, subtype, and name of the custom workflow types that you create. This script returns a list of Assocs. Each Assoc contains an integer that specifies the type (wfType), an integer that specifies the subtype (wfSubType), and a string that specifies the name of the workflow type. Defines the changes that you want to make to a particular workflows map definition. This script runs each time a workflow is initiated in Livelink. It does not apply to sub-workflows.

GetWFTypes()

StartWF()

After you orphan the WFCustomScriptPkg object and modify the GetWFTypeName(), GetWFTypes(), and StartWF() scripts, the Workflow Type list is exposed in the Livelink interface. This list is displayed on the General tab of a workflow maps Properties page and contains the names of the different types of workflows that you have created. The Workflow Type list lets the creators of workflow maps choose which type of workflow they want to create (that is, which custom operations they want to apply to their workflow map definition just before their workflow is initiated).

134

Developers Guide for Extending Livelink Workflow

Example 3: Adding a Custom Workflow Type to the custmod Module

Example 3: Adding a Custom Workflow Type to the custmod Module


This example describes how to create a workflow type that adds the current date to the end of a workflow name in the Livelink Title bar and runs a callback script before the corresponding workflow is initiated. For information about callback scripts, see Understanding Callback Events in Livelink Workflow, page 21. To add a custom workflow type to the custmod module: 1. Orphan WFMain:WFRoot:WFCustomScriptPkg in the custmod OSpace in your custom module, and name it WFCustomScriptPkg. Note If you have already performed workflow customizations that require the WFCustomScriptPkg, you can proceed directly to step 2. 2. Create a child of the WFCustomScriptPkg object, and name it CustomWorkflowTypes. This object contains the information required to create custom workflow types. 3. In the Custmod Globals object, run the BuildOSpace() script. 4. Override the following scripts:
CBExecute() GetWFTypeName() GetWFTypes() StartWF()

For more information about these scripts, see the code samples that follow. You have created the objects necessary to add a workflow type to Livelink. Now you must provide the code required to perform the custom operations. Note Because the WFCustomScriptPkg object is a superclass object, it may contain many child objects. If you want a particular custom operation to be performed by only one of those child objects, you can use the handled variable. When a custom operation needs to be performed, the child objects of the WFCustomScriptPkg object are passed the information in order. When the handled variable is set to TRUE, the operation was performed successfully; otherwise, the information is passed to the remaining child objects.

Adding New Workflow Types

135

Example 3: Adding a Custom Workflow Type to the custmod Module

CBExecute()
The following code sample defines the callback script that this workflow type calls before the workflow is initiated in Livelink. This callback script displays workflow information in the Debug window when you are running Livelink Builder. The workflow information is taken from the WWork table in the Livelink database. For more information about the WWork table, see Using WAPI and OScript for Workflow Management and Status, page 13. Note If you are running Livelink on the Livelink Intranet server and you set Debug to 1, 2, or 11 in the opentext.ini file, workflow information from the WWork table in the Livelink database is displayed in the thread logs. Thread logs are stored in the /logs directory of your primary Livelink installation (for example, c:/opentext/logs).
Function Assoc CBExecute( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer subWorkID, \ Integer taskID, \ Integer returnSubWorkID, \ Integer returnTaskID, \ List info, \ Dynamic extraData ) Assoc data Dynamic retVal Boolean handled = True

//Perform a switch statement on the callback scripts //identifier. If it is handled by this script, the handled field //returns TRUE and Livelink stops searching for a script to //handle the callback scripts operation. Then the workflow //information is echoed to the Debug window in the Livelink //Builder. switch( info[ 1 ] ) case 100 List workStatus RecArray item workStatus = prgCtx.WSession().LoadWorkStatus( workID, \ subWorkID ) for item in workStatus $LLIAPI.RecArrayPkg.DumpRecArray( item ) echo() echo() end retVal = True

136

Developers Guide for Extending Livelink Workflow

Example 3: Adding a Custom Workflow Type to the custmod Module

end default handled = False end end data.Handled = handled data.retVal = retVal return( data ) end

GetWFTypeName()
Before you modify the GetWFTypeName() script, you must define the workflow types in the GetWFTypes() script. For more information about defining workflow types, see GetWFTypes(), page 138. The following code sample describes how to retrieve the names of the workflow types that you define in the GetWFTypes() script:
Function Assoc GetWFTypeName( \ Integer wfType, \ Integer wfSubType ) Assoc Boolean String retVal handled = False name = Undefined

//If the wfType and wfSubType integer values are recognized, //assign the name of the corresponding workflow type and set the //handled variable to TRUE, indicating that a valid name has been //assigned to the workflow type. If the wfType and wfSubType //values are not recognized, the handled variable is set to FALSE //and the wfType and wfSubType integer values are automatically //passed to the next child object. if ( ( wfType == 1 ) && ( wfSubType == 100 ) ) name = 'Custom Workflow Type' handled = True elseif ( ( wfType == 1 ) && ( wfSubType == 101 ) ) name = 'Another Custom Workflow Type' handled = True end //Assign the results to fields in the retval Assoc, and then //return retVal. retVal.Handled = handled retVal.Name = name return( retVal ) end

Adding New Workflow Types

137

Example 3: Adding a Custom Workflow Type to the custmod Module

GetWFTypes()
The following code sample describes how to define the different types of custom workflows in Livelink:
Function List GetWFTypes() Assoc data List retVal //Set the type (wfType), subtype (wfSubType), and name of the //new workflow type. Store these values in an Assoc named data, //and then add the information in the Assoc to a list named //retVal. data.Type = 1 data.SubType = 100 data.Name = 'Custom Workflow Type' retVal = { data } //Create a new Assoc which will store the values of the next //custom workflow type. data = Assoc.CreateAssoc() //Set the type (wfType), subtype (wfSubType), and name of //another new workflow type. Store these values in the new data //Assoc. data.Type = 1 data.SubType = 101 data.Name = 'Another Custom Workflow Type' //Append the information in the data Assoc to the end of the //retVal list, and then return the list. retVal = { @retVal, data } return( retVal ) end

StartWF()
The following code sample contains the custom operations that you want to associate with each workflow type you create. This script tells Livelink to append the current date to the name of a Custom Workflow Type workflow in the Livelink Title bar and to run a callback script before a Custom Workflow Type workflow is initiated in Livelink.
Function Assoc StartWF( \ Object prgCtx, \ Record mapInfo, \ String name, \ List additions ) Assoc List retVal cbInfo

138

Developers Guide for Extending Livelink Workflow

Example 3: Adding a Custom Workflow Type to the custmod Module

//Append the current date to the name of a workflow. name = name + ' - ' + Str.String( Date.Now() ) //Perform custom operations for each workflow type that you have //defined. This script defines the callback script that is run //before the initiation of a Custom Workflow Type workflow. if ( ( mapInfo.MAPINFO.TYPE == 1 ) && ( mapInfo.MAPINFO.SUBTYPE \ == 100 ) ) cbInfo = mapInfo.MAPINFO.INITIATECB cbInfo = ( IsDefined( cbInfo ) ) ? cbInfo : {} cbInfo = { @cbInfo, { 100, Undefined } } mapInfo.MAPINFO.INITIATECB = cbInfo end //Populate the variables in the retVal Assoc, and then return //retVal. retVal.Name = name retVal.Additions = additions retVal.OK = True return( retVal ) end

Adding New Workflow Types

139

Example 3: Adding a Custom Workflow Type to the custmod Module

140

Developers Guide for Extending Livelink Workflow

Chapter Seven

Adding Event Trigger Scripts


Callback events are workflow events or actions that tell WAPI to run certain scripts at specific times in the execution of a workflow map. When you add a callback event to a Livelink workflow, you instruct the workflow to perform an operation when a specific workflow event occurs. For example, you can add a callback event that instructs the workflow to send an e-mail notification to a workflow participant each time a Milestone step becomes ready. Event trigger scripts provide an interface to callback events in Livelink. When you add event trigger scripts to Livelink, this interface (the Event Scripts tab) becomes available in the Livelink interfaceallowing the creator of a workflow map to determine which operations to associate with the particular workflow events that occur in the execution of a workflow map. There are three types of event trigger scripts that you can add: general event trigger scripts, performer event trigger scripts, and submap event trigger scripts. For more information about these event trigger scripts, see Choosing Event Trigger Scripts, page 142. Note For information about callback events and their role in the Livelink Workflow module, see Understanding Callback Events in Livelink Workflow, page 21. In this chapter, you will learn about: General event trigger scripts Performer event trigger scripts Submap event trigger scripts

141

Choosing Event Trigger Scripts

Choosing Event Trigger Scripts


There are three types of event trigger scripts: general event trigger scripts, performer event trigger scripts, and submap event trigger scripts.
Table 7-1: Livelink Workflow Event Trigger Scripts Event Trigger Script General Performer Description Does not return any specific type of datait simply verifies that the data returned is not an error. Returns an integer. This integer represents the ID of the Livelink user to whom a particular workflow task should be assigned when the corresponding step becomes ready. Returns an integer which represents the ID of a WAPIMAP definition. This WAPIMAP definition initiates a sub-workflow process as the next step in the workflow. A submap event trigger script is powerful because it allows Livelink to analyze data and paint a sub-workflow on-the-fly.

Submap

To create an event trigger script, you determine which type of event trigger script can handle the operation that you want to perform, and then orphan one of the following event trigger objects: WFMain:WFRoot:CallbackEventScripts:GeneralCallbackScripts WFMain:WFRoot:CallbackEventScripts:PerformerCallbackScripts WFMain:WFRoot:CallbackEventScripts:SubmapCallbackScripts Each event trigger object is registered in a subsystem. General event trigger objects are registered in $WFMain:WFCBGeneralSubsystem. Performer event trigger objects are registered in $WFMain:WFCBPerformerSubsystem. Submap event trigger objects are registered in $WFMain:WFCBSubmapSubsystem. You determine the custom operations that can be performed throughout the execution of a workflow by creating event trigger scripts that are contained in the event trigger objects. You can create multiple event trigger scripts within an event trigger objecteach of which extends the functionality of a Livelink workflow in different ways. Livelink becomes aware of the event trigger objects when you register them in their subsystems. The event trigger interface (that is, the Event Scripts tab) is also exposed in the Livelink interface when you register the event trigger objects in their subsystems. The event trigger interface lets the creator of a workflow map associate an event trigger script with a specific workflow event. Behind the scenes, the creator of the workflow map specifies the data that is stored in the correct callback column in the WAPI database table. For more information about how Livelink handles callback events, see Understanding Callback Events in Livelink Workflow, page 21. Note After you install the custom module in which the event trigger functionality is contained, the creator of a workflow map can access the

142

Developers Guide for Extending Livelink Workflow

Choosing Event Trigger Scripts

Event Scripts tab when editing a workflow step definition in Livelink. The Event Scripts tab can also be accessed from the workflows Properties page. Use event trigger scripts for operations that require more data than is contained in the work package. If the operations require additional input from a workflow participant, consider creating a new task type. For more information about adding task types, see Adding New Task Types, page 31.

Adding Event Trigger Scripts

143

Creating General Event Trigger Scripts

Creating General Event Trigger Scripts


Before you can create an event trigger script for the General callback event, you must orphan the GeneralCallbackScripts object in your custom module. You can find the GeneralCallbackScripts object in WFMain:WFRoot:CallbackEventsScripts. The GeneralCallbackScripts object will contain the scripts that can be attached to general workflow events. The following workflow events are called general workflow events: Workflow is initiated Workflow is completed Workflow is suspended Workflow is resumed Workflow is stopped Workflow is deleted Workflow is archived Step is ready Step is done Step is killed Step is resurrected Note A workflow step is killed if it is on a workflow route that is not taken because it is part of an Evaluate step. A workflow step is resurrected if the workflow loops back to a step that precedes the Evaluate step, making it possible for the workflow to take the route that contains the killed step again. For more information about Evaluate steps, see the Livelink online help. For each general event trigger script that you create, you can set the following features.
Table 7-2: Features Associated With the GeneralCallbackScripts Object Feature
fEnabled

Description Contains a Boolean value. When set to TRUE, the fEnabled feature lets you register the object with which it is associated in WFMain:WFCBGeneralSubsystem. Contains a String value. This feature stores the name that you want to represent the event trigger script on the Event Scripts tab in the Livelink interface. By default, the name that you give to the script is used to name the option on the Event Scripts tab in the Livelink interface; however, you can customize the name that is displayed by creating this String feature.

fScriptName

144

Developers Guide for Extending Livelink Workflow

Creating General Event Trigger Scripts

Notes If the names of the scripts that you add to the GeneralCallbackScripts object do not begin with a 0 or an underscore (_), they are displayed on the Event Scripts tab in the Livelink interface as options for Livelink users to attach to general workflow events. If the names of your scripts begin with a 0 or an underscore (_), they are not displayed in the Livelink interface, but can be used as utility scripts that are referenced by the event trigger scripts. If you attach a general event trigger script to a Step Ready event and the event trigger script returns a value of TRUE, the step is automatically completed. After you set the features associated with the GeneralCallbackScripts object, you can create the general event trigger scripts. A prototype for the general event trigger scripts is stored in the ODocumentation() script which is contained in the GeneralCallbackScripts object. General event trigger scripts must return a Boolean valueTRUE for success or FALSE for failure.
Table 7-3: Script Associated With the GeneralCallbackScripts Object Script
ScriptName()

Description Defines the operation that you want to perform

After you orphan the general event trigger object, set the fEnabled feature, and create the general event trigger scripts, you must run the BuildOSpace() script in the Globals object of your custom OSpace and restart the Livelink Builder to register your changes. The BuildOSpace() script registers the GeneralCallbackScripts object in WFMain:WFCBGeneralSubsystem and exposes the Event Scripts tab in the Livelink interface. When you install the custom module in which the general event trigger functionality is contained, you can view your changes in the Livelink interface. The names that you specified in the fScriptName features of the GeneralCallbackScripts object are displayed as options in the lists associated with general workflow events on the Event Scripts tab. This lets you add general event trigger scripts to general workflow events. Note For more information about creating workflows in Livelink, see the Livelink online help.

Adding Event Trigger Scripts

145

Example 4: Updating Workflow Attributes

Example 4: Updating Workflow Attributes


This example describes how to create a general event trigger script that is used to update a workflow attribute. This general event trigger script walks through all of the different types of data that are flowing through the workflow (for example, comments, attachments, forms, or attributes) and locates the attributes RecArray. It then walks through all of the attributes in the RecArray and locates the one that you want to update. The script updates the attribute by saving new data from the workflow. To create the general event trigger script: 1. Orphan WFMain:WFRoot:CallbackEventScripts:GeneralCallbackScripts in the custmod OSpace in your custom module, and name it GeneralCallbackScripts. 2. In the GeneralCallbackScripts object, set the value of the fEnabled feature to TRUE. 3. In the GeneralCallbackScripts object, create a String feature, and name it fChangeAttribute. 4. Set the value of the fChangeAttribute feature to Update Workflow Attribute. 5. Create a script, and name it ChangeAttribute. 6. In the Custmod Globals object, run the BuildOSpace() script. 7. Save and export your OSpace, and then exit and restart the Livelink Builder. The GeneralCallbackScripts object is registered in WFCBGeneralSubsystem, and the Event Scripts tab is exposed in the Livelink interface. You have provided the information necessary to display the Update Workflow Attribute option on the Event Scripts tab in the Livelink interface. Now, you must provide the code that the ChangeAttribute() script requires to perform the operation.

ChangeAttribute()
The following code sample describes how to update an attribute in a workflow.
Function Boolean ChangeAttribute( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer subWorkID, \ Integer taskID, \ Integer returnSubWorkID, \ Integer returnTaskID, \ Dynamic extraData = Undefined ) //The following variable declarations are taken from the //prototype for the general event trigger scripts. This //information is stored in the 0Documentation() script //that is contained in the GeneralCallbackScripts object in your

146

Developers Guide for Extending Livelink Workflow

Example 4: Updating Workflow Attributes

//custom module. RecArray Record Boolean Boolean attribs r found = False success = True

//The following variable declaration references the attributes //API object. This object contains the scripts that store data in //the database, retrieve data from the database, and delete data //in the database. All of the different types of data that pass //through a workflow have an attributes API object. The //attributes API objects are all children or orphans of //WFMain:WFRoot:WFObjectTypes:WFDataTypes. Object obj = $WFMain.WFPackageSubsystem.GetItemByName( \ 'WFAttributes' ) //The following variable declaration calls the script that loads //the different types of data that are flowing through the //workflow. RecArray array = $WFMain.WAPIPkg.LoadWorkData( prgCtx, work )

//Walk through all the different types of data that are flowing //through this workflow, (for example, comments, attachments, //forms, attributes) and locate the workflow attributes. if ( IsDefined( obj ) ) for r in array if ( { r.TYPE, r.SUBTYPE } == { obj.fType, obj.fSubType } ) found = True break end end //When the attributes RecArray is found, store it in attribs. //Then reset the found variable so that it can be used again. if ( found ) attribs = r.USERDATA found = False //Walk through the attributes and locate the one that you //want to update. Then update its value. for r in attribs if ( r.Name == 'desired attribute' ) r.Value = 'Some new value' found = True break end end //When the attribute is updated, save the new workflow data. if ( found ) success = obj.SaveWorkData( prgCtx, work, Undefined, \ attribs )

Adding Event Trigger Scripts

147

Example 4: Updating Workflow Attributes

end end end return( success ) end

Notes You can use breakpoints to debug event trigger scripts in the same way that you use breakpoints to debug other scripts in the Livelink Builder; however, placing a breakpoint in an event trigger script always returns a workflow error. Some calls in the event trigger scripts (especially those that use WAPIWORK) will fail if you have added breakpoints. When you finish debugging event trigger scripts, you must remove the breakpoints that you have added. Another way to debug callback scripts is to send messages to the Builders Debug window using the Echo function. For more information about the Echo function, see the Livelink Builder Developers Guide.

148

Developers Guide for Extending Livelink Workflow

Creating Performer Event Trigger Scripts

Creating Performer Event Trigger Scripts


Before you can create the event trigger scripts for the Performer callback event, you must orphan the PerformerCallbackScripts object in your custom module. You can find the PerformerCallbackScripts object in WFMain:WFRoot:CallbackEventScripts. The PerformerCallbackScripts object will contain the scripts that can be attached to the Assign Step Performer workflow event. For each performer event trigger script that you create, you can set the following features.
Table 7-4: Features Associated With the PerformerCallbackScripts Object Feature
fEnabled

Description Contains a Boolean value. When set to TRUE, the fEnabled feature lets you register the object with which it is associated in WFMain:WFCBPerformerSubsystem. Contains a String value. This feature stores the name that you want to represent the event trigger script on the Event Scripts tab in the Livelink interface. By default, the name that you give to the script is used to name the option on the Event Scripts tab in the Livelink interface; however, you can customize the name that is displayed by creating this String feature.

fScriptName

Note If the names of the scripts that you add to the PerformerCallbackScripts object do not begin with a 0 or an underscore (_), they are displayed on the Event Scripts tab in the Livelink interface as options for Livelink users to attach to the Assign Step Performer workflow event. If the names of your scripts begin with a 0 or an underscore (_), they are not displayed in the Livelink interface, but can be used as utility scripts that are referenced by the event trigger scripts. After you set the features associated with the PerformerCallbackScripts object, you can create the performer event trigger scripts. A prototype for the performer event trigger scripts is stored in the ODocumentation() script which is contained in the PerformerCallbackScripts object. If successful, these scripts return the ID of a Livelink user or group to which the workflow step is assigned when it becomes ready; otherwise, these scripts return errors.
Table 7-5: Script Associated With the PerformerCallbackScripts Object Script
ScriptName()

Description Defines the operation that you want to perform

After you create and edit the performer event trigger scripts, you must run the BuildOSpace() script in the Globals object of your custom OSpace and restart the Livelink Builder to register your changes. The BuildOspace() script registers the

Adding Event Trigger Scripts

149

Creating Performer Event Trigger Scripts

PerformerCallbackScripts object in WFMain:WFCBPerformerSubsystem and exposes the Event Scripts tab in the Livelink interface.

When you install the custom module in which the performer event trigger functionality is contained, you can view your changes in the Livelink interface. The names that you specified in the fScriptName features of the PerformerCallbackScripts object are displayed as options in the Assign Step Performer list on the Events Script tab. This lets you add performer event trigger scripts to Assign Step Performer workflow events. Note For more information about creating workflows in Livelink, see the Livelink online help.

150

Developers Guide for Extending Livelink Workflow

Example 5: Assigning Steps to Workflow Participants

Example 5: Assigning Steps to Workflow Participants


This example describes how to create a performer event trigger script that is used to locate a workflow attribute named username. This performer event trigger script locates the user associated with this attribute and assigns the next workflow step to them. To create the performer event trigger script: 1. Orphan WFMain:WFRoot:CallbackEventScripts:PerformerCallbackScripts in the custmod OSpace in your custom module, and name it PerformerCallbackScripts. 2. In the PerformerCallbackScripts object, set the value of the fEnabled feature to TRUE. 3. In the PerformerCallbackScripts object, create a String feature and name it fChooseUser. 4. Set the value of the fChooseUser feature to Choose User. 5. In the PerformerCallbackScripts object, create a Script and name it ChooseUser. 6. In the Custmod Globals object, run the BuildOSpace() script. 7. Save and export your OSpace, and then exit and restart the Livelink Builder. The PerformerCallbackScripts object is registered in WFCBPerformerSubsystem, and the Event Scripts tab is exposed in the Livelink interface. You have provided the information necessary to display the Choose User option on the Event Scripts tab in the Livelink interface. Now you must provide the code that the ChooseUser() script requires to perform the operation.

ChooseUser()
The following code sample describes how to identify the user to which the next step in a workflow is assigned.
Function Integer ChooseUser( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer subWorkID, \ Integer taskID, \ Integer returnSubWorkID, \ Integer returnTaskID, \ Integer performerID = Undefined ) //The following variable declarations are taken from the //prototype for performer event trigger scripts. This //information is stored in the ODocumentation() script associated

Adding Event Trigger Scripts

151

Example 5: Assigning Steps to Workflow Participants

//with the PerformerCallbackScripts object in your custom module. Dynamic RecArray RecArray Record String Boolean Boolean userID attribs performer r userName found = False success = True

//The following variable declaration references the attributes //API object. This object contains the scripts that store data in //the database, retrieve data from the database, and delete data //in the database. All of the different types of data that pass //through a workflow have an attributes API object. The //attributes API objects are all children or orphans of //WFMain:WFRoot:WFObjectTypes:WFDataTypes. Object obj = $WFMain.WFPackageSubsystem.GetItemByName( \ 'WFAttributes' ) Object uSession = prgCtx.USession() Object wSession = prgCtx.WSession() //Call the script that loads the different types of data that are //flowing through the workflow (for example, comments, //attachments, forms, attributes). RecArray array = $WFMain.WAPIPkg.LoadWorkData( prgCtx, work )

//Walk through all the different types of data that are flowing //through this workflow, (for example, comments, attachments, //forms, attributes) and locate the workflow attributes. if ( IsDefined( obj ) ) for r in array if ( { r.TYPE, r.SUBTYPE } == { obj.fType, obj.fSubType } ) found = True break end end //When the attributes RecArray is found, store it in attribs. //Then reset the found variable so that it can be used again. if ( found ) attribs = r.USERDATA found = False //Walk through the attributes RecArray looking for the //UserName attribute. for r in attribs if ( r.Name == 'UserName' ) userName = r.Value found = True break end end //When the UserName attribute is found, retrieve the UserID

152

Developers Guide for Extending Livelink Workflow

Example 5: Assigning Steps to Workflow Participants

//variable. if ( found ) performer = UAPI.GetUser( uSession.fSession, userName ) //When the UserID variable is found, set its value. if ( IsNotError( performer ) ) userID = performer[ 1 ].ID else userID = performer end end end end //If the userID variable is not found, return an error. You can //set the error to Error.Get( 600 ), which is a generic workflow //error. If you want to display a more specific error message, //you can set fErrorMsg in the WSession object. if ( !IsDefined( userID ) ) userID = Error.Get( 600 ) wSession.fErrorMsg = "Could not find the attribute UserName" +\ "to determine user." end return( userID ) end

Notes You can use breakpoints to debug event trigger scripts in the same way that you use breakpoints to debug other scripts in the Livelink Builder; however, placing a breakpoint in an event trigger script always returns a workflow error. Some calls in the event trigger scripts (especially those that use WAPIWORK) will fail if you have added breakpoints. When you finish debugging event trigger scripts, you must remove the breakpoints that you have added. Another way to debug callback scripts is to send messages to the Builders Debug window using the Echo function. For more information about the Echo function, see the Livelink Builder Developers Guide.

Adding Event Trigger Scripts

153

Creating Submap Event Trigger Scripts

Creating Submap Event Trigger Scripts


Before you can create the event trigger scripts for the Submap callback event, you must orphan the SubmapCallbackScripts object in your custom module. You can find the SubmapCallbackScripts object in WFMain:WFRoot:CallbackEventScripts. The SubmapCallbackScripts object will contain the scripts that can be attached to the Determine Sub-Map To Use workflow event. For each submap event trigger script that you create, you can set the following features.
Table 7-6: Features Associated With the SubmapCallbackScripts Object Feature
fEnabled

Description Contains a Boolean value. When set to TRUE, the fEnabled feature lets you register the object with which it is associated in the WFMain:WFCBSubmapSubsystem. Contains a String value. This feature stores the name that you want to represent the event trigger script on the Event Scripts tab in the Livelink interface. By default, the name that you give to the script is used to name the option on the Event Scripts tab in the Livelink interface; however, you can customize the name that is displayed by creating this String feature.

fScriptName

Note If the names of the scripts that you add to the SubmapCallbackScripts object do not begin with a 0 or an underscore (_), they are displayed on the Event Scripts tab in the Livelink interface as options for Livelink users to attach to the Determine Sub-Map To Use workflow event. If the names of your scripts begin with a 0 or an underscore (_), they are not displayed in the Livelink interface, but can be used as utility scripts that are referenced by the event trigger scripts. After you set the features associated with the SubmapCallbackScripts object, you can create the submap event trigger scripts. A prototype for the submap event trigger scripts is stored in the ODocumentation() script associated with the SubmapCallbackScripts object. If successful, these scripts return an integer representing the ID of a workflow map definition (as stored in WAPI) that is initiated as a sub-workflow when the corresponding step becomes ready; otherwise these scripts return errors.
Table 7-7: Script Associated With the SubmapCallbackScripts Object Script
ScriptName()

Description Defines the operation that you want to perform

After you create and edit the submap event trigger scripts, you must run the BuildOSpace() script in the Globals object of your custom OSpace, and restart the Livelink Builder to register your changes. The BuildOSpace() script registers the GeneralCallbackScripts object in WFMain:WFCBSubmapSubsystem and exposes the Event Scripts tab in the Livelink interface.

154

Developers Guide for Extending Livelink Workflow

Creating Submap Event Trigger Scripts

When you install the custom module in which the submap event trigger functionality is contained, you can view your changes in the Livelink interface.The names that you specified in the fScriptName features of the SubmapCallbackScripts object are displayed as options in the Determine Sub-Map To Use list on the Event Scripts tab. This lets you add submap event trigger scripts to Determine Sub-Map To Use workflow events. Note For more information about creating workflows in Livelink, see the Livelink online help.

Adding Event Trigger Scripts

155

Example 6: Defining Sub-Workflows On-The-Fly

Example 6: Defining Sub-Workflows On-The-Fly


This example describes how to create a submap event trigger script that is used to define a sub-workflow map and save the map definition in WAPI. This map definition instructs Livelink to assign the same task to each Livelink user in the user list. To create the submap event trigger script: 1. Orphan WFMain:WFRoot:CallbackEventScripts:SubmapCallbackScripts in the custmod OSpace in your custom module, and name it SubmapCallbackScripts. 2. In the SubmapCallbackScripts object, set the value of the fEnabled feature to TRUE. 3. In the SubmapCallbackScripts object, create a String feature and name it fsubworkflow. 4. Set the value of the fsubworkflow feature to Generate Sub-Workflow. 5. In the SubmapCallbackScripts object, create a Script and name it subworkflow. 6. In the Custmod Globals object, run the BuildOSpace() script. 7. Save and export your OSpace, and then exit and restart the Livelink Builder. The SubmapCallbackScripts object is registered in WFCBSubmapSubsystem, and the Event Scripts tab is exposed in the Livelink interface. You have provided the information necessary to display the Generate Sub-Workflow option on the Event Scripts tab in the Livelink interface. Now you must provide the code that the subworkflow() script requires to perform the operation.

subworkflow()
The following code sample describes how to define a sub-workflow map on-the-fly and save the map definition in WAPI.
Function Integer subworkflow( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer subWorkID, \ Integer taskID, \ Integer returnSubWorkID, \ Integer returnTaskID, \ Dynamic extraData = Undefined ) //The following variable declarations are taken from the //prototype for submap event trigger scripts. This information //is stored in the ODocumentation() script associated with the //SubmapCallbackScripts object in your custom module. Assoc taskData

156

Developers Guide for Extending Livelink Workflow

Example 6: Defining Sub-Workflows On-The-Fly

Dynamic user Dynamic mapID Integer flags List dispositions RecArray attrRec Record mapRec Record newTask Boolean Object Object Object Object Object Object ok = True mapPkg = $WFMain.WFMapPkg pkgSubSystem = $WFMain.WFPackageSubsystem recArrayPkg = $LLIAPI.RecArrayPkg taskSubSystem = $WFMain.WFTaskSubsystem uSession = prgCtx.USession() wSession = prgCtx.WSession()

//Set the position of the first step in the sub-workflow map. //Because the Start step is just a placeholder step, it is not //counted as the first step in a sub-workflow. Point pos = Point( 100, 50 )

//Retrieve a generic map record to use as the base for the sub//workflow map. You can also retrieve a standard, empty map that //has the Start step, Attributes, Attachments, and Comments data //types added by calling //mapRec = mapPkg.VFMakeNewFreshMap( prgCtx, \ //Assoc.CreateAssoc() ) mapRec = mapPkg.CreateMapRec() //Specify a title for the sub-workflow map. mapRec.MAPINFO.TITLE = 'The on-the-fly sub-workflow' //Add a Start step to the sub-workflow map. mapPkg.VFAddNewTask( \ prgCtx, \ taskSubsystem.GetItemByName( 'WFStartTask' ), \ mapRec, \ Undefined, \ Point( 20, 50 ) ) //Add the Attributes and Comments data types to the sub-workflow //map. mapPkg.VFAddPackage( \ prgCtx, \ pkgSubsystem.GetItemByName( 'WFComments' ), \ mapRec, \ Undefined ) mapPkg.VFAddPackage( \ prgCtx, \ pkgSubsystem.GetItemByName( 'WFAttributes' ), \ mapRec, \ Undefined ) //Add attributes to the Attribute data type.

Adding Event Trigger Scripts

157

Example 6: Defining Sub-Workflows On-The-Fly

attrRec = mapRec.WORK_PACKAGES[ 2 ].USERDATA //attrRec is a RecArray that stores the attributes. There are //five columns in attrRec: Name, DataType, DisplayType, //ValidValues, and Value. RecArray.AddRecord( attrRec, { 'Name', StringType, 'Field', {}, \ Undefined } ) RecArray.AddRecord( attrRec, { 'Due Date', DateType, 'Field', \ {}, Undefined } ) RecArray.AddRecord( attrRec, { 'Country', StringType, 'Popup', \ {'Canada','Germany','USA'}, 'USA' } ) RecArray.AddRecord( attrRec, { 'Critical Report?', BooleanType, \ 'Checkbox', {}, 0 } ) //Add a step for each user to whom the data should be sent. The //first step is assigned to the Admin user. user = UAPI.GetUser( uSession.fSession, 'Admin' ) if ( !IsError( user ) ) user = user[ 1 ] newTask = mapPkg.VFAddNewTask( \ prgCtx, \ taskSubsystem.GetItemByname( 'GenericUserTask' ), \ mapRec, \ user, \ pos ) //Set any additional information for the step. //Specify the title of the step. taskData.TITLE = 'Step for Admin' //Specify the duration of the step in seconds (for example, one //day is 86400 seconds. taskData.DUEDURATION = 86400 //Specify the instructions for the step. taskData.INSTRUCTIONS = 'Work on this step' //Specify the options associated with the step. In this case //give the user permission to see all comments, to send the //task for review, to delegate permissions, and to require //dispositions. flags flags flags flags |= |= |= |= $WFPComments $WFPReview $WFPDelegate $WFPDisposition

dispositions = { 'Approve', 'Reject' } taskData.USERFLAGS = { flags, { dispositions, 1 } } //taskData.USERFLAGS[1] contains the permission flags. //USERFLAGS[2][1] contains a list of string dispositions.

158

Developers Guide for Extending Livelink Workflow

Example 6: Defining Sub-Workflows On-The-Fly

//USERFLAGS[2][2] contains the ordinal number of the //defaulted disposition. //Specify the attribute data. Assoc formData List visibleAttrs = { 'Name', 'Due Date', 'Country', \ 'Critical Report?' } List requiredAttrs = { 'Name', 'Country' } List nonEditableAttrs = {} formData.VISIBLE_ATTRIBS = visibleAttrs formData.REQUIRED_ATTRIBS = requiredAttrs formData.NONEDITABLE_ATTRIBS = nonEditableAttrs taskData.FORM = formData //Specify all remaining data for the step. SetTaskData( newTask, taskData ) //Add a link between the Start step and this step. mapPkg.AddLinkRecord( \ mapRec.TASKS, \ mapRec.LINKS, \ mapRec.TASKS[ 1 ], \ mapRec.TASKS[ 2 ], \ 0 ) else ok = False //Specify the error message that is displayed if the userID //value is not found. wSession.fErrorMsg = 'Could not find Admin user.' end if ( ok ) //Generate a new position for the next step in the sub//workflow. pos += Point( 75, 0 ) //Add an Initiator step to the sub-workflow map. newTask = mapPkg.VFAddNewTask( \ prgCtx, \ taskSubsystem.GetItemByname( 'InitiatorTask' ), \ mapRec, \ Undefined, \ pos ) //Specify the title of the step. taskData.TITLE = 'Step for Initiator' //Specify all the data required for the step.

Adding Event Trigger Scripts

159

Example 6: Defining Sub-Workflows On-The-Fly

SetTaskData( newTask, taskData ) //Add a link between the User step and this step. mapPkg.AddLinkRecord( \ mapRec.TASKS, \ mapRec.LINKS, \ mapRec.TASKS[ 2 ], \ mapRec.TASKS[ 3 ], \ 0 ) end if ( ok ) RecArray array

//Generate a new position for the next step in the sub//workflow. pos += Point( 75, 0 ) //Add an Evaluate step to the sub-workflow map. newTask = mapPkg.VFAddNewTask( \ prgCtx, \ taskSubsystem.GetItemByname( 'Conditional' ), \ mapRec, \ Undefined, \ pos ) //Specify the title of the step. newTask.TITLE = 'Evaluate' array = newTask.CONDITIONCB //array is a RecArray that stores the evaluation information. //There are six columns in the RecArray: Type, Name, Condition, //Value, Display, and Conjunction. RecArray.AddRecord( array, \ { 2, 'Step for Initiator', '=', 'Approve', \ 'Step for Initiator = Approve', ' and' } ) RecArray.AddRecord( array, { { 1, 3 }, 'Country', '=', \ 'USA', 'Country = USA', Undefined } ) //Specify all the data required for the step. SetTaskData( newTask, taskData ) //Add a link between the Initiator step and this step. mapPkg.AddLinkRecord( \ mapRec.TASKS, \ mapRec.LINKS, \ mapRec.TASKS[ 3 ], \ mapRec.TASKS[ 4 ], \ 0 ) end if ( ok )

160

Developers Guide for Extending Livelink Workflow

Example 6: Defining Sub-Workflows On-The-Fly

//Generate a new position for the next step in the sub//workflow. pos += Point( 75, -30 ) //Add a MileStone step to the sub-workflow map. newTask = mapPkg.VFAddNewTask( \ prgCtx, \ taskSubsystem.GetItemByname( 'MileStone' ), \ mapRec, \ Undefined, \ pos ) //Specify the title of the step. newTask.TITLE = 'True Branch' //Specify the duration of the step in seconds (for example, one //day is 86400 seconds). newTask.DUEDURATION = 86400 //Add a link between the Evaluate step and this workflow step. mapPkg.AddLinkRecord( \ mapRec.TASKS, \ mapRec.LINKS, \ mapRec.TASKS[ 4 ], \ mapRec.TASKS[ 5 ], \ WAPI.MAPTASK_TRUELINKS ) end if ( ok ) //Generate a new position for the next step in the sub//workflow. pos += Point( 0, 75 ) //Add a MileStone step to the sub-workflow map. newTask = mapPkg.VFAddNewTask( \ prgCtx, \ taskSubsystem.GetItemByname( 'MileStone' ), \ mapRec, \ Undefined, \ pos ) //Specify the title of the step. newTask.TITLE = 'False Branch' newTask.DUEDURATION = 86400 //Add a link between the Evaluate step and this step. mapPkg.AddLinkRecord( \ mapRec.TASKS, \

Adding Event Trigger Scripts

161

Example 6: Defining Sub-Workflows On-The-Fly

mapRec.LINKS, \ mapRec.TASKS[ 4 ],\ mapRec.TASKS[ 6 ], \ WAPI.MAPTASK_FALSELINKS ) end if ( ok ) //Save the sub-workflow map definition to WAPI. mapID = $WFMain.WAPIPkg.SaveSubMap( prgCtx, mapRec ) end if ( !ok ) mapID = Error.Get( 600 ) end return( mapID ) end Function Void SetTaskData( \ Record task, \ Assoc data ) String key for key in Assoc.Keys( data ) task.( key ) = data.( key ) end end

Notes You can use breakpoints to debug event trigger scripts in the same way that you use breakpoints to debug other scripts in the Livelink Builder; however, placing a breakpoint in an event trigger script always returns a workflow error. Some calls in the event trigger scripts (especially those that use WAPIWORK) will fail if you have added breakpoints. When you finish debugging event trigger scripts, you must remove the breakpoints that you have added. Another way to debug callback scripts is to send messages to the Builders Debug window using the Echo function. For more information about the Echo function, see the Livelink Builder Developers Guide.

162

Developers Guide for Extending Livelink Workflow

Restricting Access to Event Trigger Scripts

Restricting Access to Event Trigger Scripts


Creating event trigger scripts lets the creators of workflows access the Event Scripts tab in the Livelink interface, where they can add custom operations to specific workflow events. This means that the creators of workflows can determine which operation to perform when a specific workflow event occurs in the execution of a workflow. If you want to restrict access to an event trigger script that you add to Livelink so that only certain Livelink users can attach the event trigger script to specific workflow events, you can modify the fObjectFactory feature and the FactoryName() script associated with the corresponding event trigger object. For example, if you want to specify which Livelink users can attach a general event script to specific workflow events, you can modify the fObjectFactory feature and the FactoryName() script contained in the general event trigger object in your custom module. Similarly, if you want to specify which Livelink users can attach a performer event trigger script to the Assign Step Performer workflow event, you can modify the fObjectFactory feature and the FactoryName() script contained in the performer event trigger object in your custom module. If you want to specify which Livelink users can attach a submap event trigger script to the Determine Sub-Map To Use workflow event, you can modify the fObjectFactory feature and the FactoryName() script contained in the submap event trigger object.
fObjectFactory

Contains a Boolean value. When set to TRUE, this feature works with the FactoryName() script to allow the Admin user (Livelink Administrator) to restrict access to event trigger scripts. Returns a string that identifies the event trigger scripts to which you want to restrict access

FactoryName()

To restrict access to event trigger scripts: 1. Locate the event trigger object that contains the event trigger script to which you want to restrict access in your custom module. 2. Set the fObjectFactory feature to TRUE. 3. Type the string value that you want to return in the FactoryName() script. This string value is displayed in the Usage Privileges section of the Administer Object and Usage Privileges page. Tip You can use the name that you specified in the fScriptName feature by referencing that feature (.fScriptName) in the FactoryName() script. 4. In the Custmod Globals object, run the BuildOSpace() script 5. Save and export your OSpace, and then exit and restart the Livelink Builder. You can restrict access to general event trigger scripts, performer event trigger scripts, and submap event trigger scripts individually by setting different return values for the FactoryName() scripts for each event trigger object. You can restrict access to all general

Adding Event Trigger Scripts

163

Restricting Access to Event Trigger Scripts

event trigger scripts, performer event trigger scripts, and submap event trigger scripts together by setting the same return values for the FactoryName() scripts for each event trigger object. After you modify the fObjectFactory features and the FactoryName() scripts contained in the event trigger objects for the event trigger scripts to which you want to restrict access, you must run the BuildOSpace() script in the Globals object of your custom OSpace. Then, the Admin user can go to the Administer Privileges section of the Livelink Intranet Administration page and specify which Livelink users can attach the event trigger scripts to workflow events. Suppose that you create general, performer, and submap event trigger scripts in your custom module and you want to restrict access to the performer and submap event trigger scripts. You can set the fObjectFactory feature to TRUE and modify the FactoryName() script to return the same string value in the performer and submap event trigger objects. This means that the Admin user can specify which Livelink users can attach the performer and submap event trigger scripts to workflow events. In this case, certain Livelink users can attach the performer and submap event trigger scripts to workflow events and all Livelink users can attach the general event trigger scripts to workflow events.

164

Developers Guide for Extending Livelink Workflow

Appendix A

Creating the Form Task Type


The following example describes how to create a more complex task type that is dependent on the Livelink Forms module. The forms task type prompts workflow participants to complete a form and submit data to the workflow. It displays a form-type interface to the performer of the task, prompts the performer to complete the form, and routes the work package based on the performers input. Notes This section documents the task type that is created for you when you install the Livelink Forms Workflow and Livelink Forms Workflow Painter modules. You must install the Livelink Forms module before you can use the task type that you create in this chapter. This task type is dependent on the components of the Livelink Forms module.

165

The FormTaskAPI Object

The FormTaskAPI Object


You begin adding the form task type to Livelink by defining the form task types API object. To define the form task types API object: 1. Orphan WFMain:WFRoot:WFObjectTypes:WFTaskTypes:StandardTasks in an OSpace in your custom module, and name it FormTaskAPI. 2. In the FormTaskAPI object, change the fSubType feature to an Integer/Real type, and set its value to 1. The fSubType feature holds a unique integer that identifies the object. 3. Ensure that the fType feature is an Integer/Real type, and set its value to 11. The fType feature holds a unique integer that identifies the object. 4. Ensure that the fTaskName feature is a String, and set its value to Custom Form
Task.

The fTaskName feature holds the text that is displayed in the Workflow Painter for the form task type. 5. Override the following scripts: GetPainterInfo() ReadyTaskForInitiation() SetTaskDefaults() SetTaskRecFromMapTask() For more information about these scripts, see the code samples that follow. You have created the objects necessary to define a task types API. Now you must provide the code required to customize the API object for the form task type.

GetPainterInfo()
The following code sample describes the default values of the GetPainterInfo() script.
Function Dynamic GetPainterInfo( \ Object prgCtx, \ Record task, \ Dynamic context = Undefined ) //Store the name of the task type in a dynamic variable named //retVal. By default, this is the name that you specified in the //fTaskName feature of the FormTaskAPI object (that is, Custom //Task Type). Dynamic retVal = Str.String( task.TITLE ) return( retVal ) end

166

Developers Guide for Extending Livelink Workflow

The FormTaskAPI Object

ReadyTaskForInitiation()
The following code sample describes how to prepare the task for initiation in Livelink. It stores callback scripts and any other additional data that is required by the form task type throughout the execution of the workflow.
Function Boolean ReadyTaskForInitiation( \ Object prgCtx, \ Record r, \ RecArray workPkg ) Assoc expandInfo List cbInfo List prevCBs RecArray user Boolean success = True Object const = $WFMain.WFConst Object uSession = prgCtx.USession() Record userData = $WFMain.WFMapPkg.CreateTaskUserDataRec() userData.TYPE = r.TYPE userData.SUBTYPE = r.SUBTYPE userData.PERMFLAGS = r.USERFLAGS r.USERDATA = userData //Add a standard Done callback script. This callback script is //called when the task is completed and the workflow is routed to //the next workflow participant. The Done callback script //instructs all of the data types in the workflow to make a copy //of the information that was entered at this step. The form data //type stores the information in a separate table so that each //version is available at the end of a workflow. if ( IsDefined( r.DONECB ) ) cbInfo = { @r.DONECB, { const.kCBSetTaskDoneData, Undefined } } else cbInfo = { { const.kCBSetTaskDoneData, Undefined } } end r.DONECB = cbInfo //If the performer of the task is a group, add a callback script //that identifies the group name. Then, if the group task is //part of a loopback, the task is reassigned to the whole group //when the route loops back (and not to the individual group //member who initially accepted the task). if ( IsDefined( r.PERFORMERID ) ) if ( r.PERFORMERID > 0 ) user = UAPI.GetByID( uSession.fSession, r.PERFORMERID ) if ( !IsError( user ) ) if ( user[ 1 ].TYPE != UAPI.USER ) prevCBs = r.PERFORMERCB prevCBs = ( IsDefined( prevCBs ) ) ? prevCBs : {} r.PERFORMERCB = { { const.kCBSetGrpStepPerformer, r.PERFORMERID }, @prevCBs } end end

Creating the Form Task Type

167

The FormTaskAPI Object

else //If the performer is Undefined, add a callback script that //assigns the task to the initiator of the workflow. r.PERFORMERCB = { { const.kCBGetInitiator, Undefined } } r.PERFORMERID = Undefined end end //If the performer of the task is a group, and that group should //be expanded so that the task is assigned to all members of the //group, add a Submap callback script. This Submap callback //script creates a sub-workflow that expands the members of the //group. if ( IsSet( r.EXATTS.GroupFlags ) && \ ( r.EXATTS.GroupFlags != const.kWFGroupStandard ) ) expandInfo.Type = r.TYPE expandInfo.SubType = r.SUBTYPE expandInfo.Flag = r.EXATTS.GroupFlags r.SUBMAPIDCB = { { const.kCBExpandGroup, expandInfo } } end //Verify that a form has been selected for display with this task //type. if ( IsDefined( r.FORM ) && IsDefined( r.FORM.FORM_DISPLAYFORM )) r.FORM.FORM_FORMS = { r.FORM.FORM_DISPLAYFORM } else success = False end return( success ) end

SetTaskDefaults()
The following code sample describes how to specify the default information required by Livelink to recognize the form task type. This is also where you specify data that must be present if the creator of the workflow does not edit the task before the workflow is initiated.
Function Void SetTaskDefaults( \ Object prgCtx, \ Record taskRec, \ Dynamic context = Undefined ) //Set values for the default information required by Livelink to //recognize the form task type. The fType and fSubType values //hold unique integers that identify the task type. taskRec.TYPE = .fType taskRec.SUBTYPE = .fSubType taskRec.EXATTS = Assoc.CreateAssoc( Assoc.NotSetValue() ) taskRec.CUSTOMDATA = Assoc.CreateAssoc( Assoc.NotSetValue() ) //Specify the name of the task and the performer ID associated

168

Developers Guide for Extending Livelink Workflow

The FormTaskAPI Object

//with the task. These values are displayed as default values on //the Step Definition page when a form task is edited for the //first time. taskRec.TITLE = Str.String( .fTaskName ) taskRec.PERFORMERID = Undefined end

SetTaskRecFromMapTask()
The following code sample describes how to convert a form task that has been prepared for initiation to a workflow map definition task.
Function Void SetTaskRecFromMapTask( \ WAPIMAPTASK task, \ Record r, \ Record taskData ) List Dynamic Object Integer cbInfo data = task.pUserData const = $WFMain.WFConst groupFlag = const.kWFGroupStandard

//Convert the values stored in the WAPIMAPTASK to the //corresponding fields in the map definition task. r.TYPE = data.TYPE r.SUBTYPE = data.SUBTYPE r.USERFLAGS = data.PERMFLAGS r.SUBMAPID = task.pSubMapID r.PERFORMERID = taskData.WORK.SUBWORKTASK_PERFORMERID r.READYCB = task.pReadyCB //Remove the Done callback script. r.DONECB = task.pDoneCB r.DONECB = $WFMain.WFMapPkg.RemoveCBInfoType( r.DONECB, \ const.kCBSetTaskDoneData ) r.KILLCB = task.pKillCB //Remove the Performer callback script. r.PERFORMERCB = task.pPerformerCB r.PERFORMERCB = $WFMain.WFMapPkg.RemoveCBInfoType( \ r.PERFORMERCB, const.kCBSetGrpStepPerformer ) r.CONDITIONCB = task.pConditionCB //Convert the values stored in the WAPIMAPTASK to the //corresponding fields in the map definition task. r.FORM = task.pForm r.PAINTER = task.pPainter r.STARTDATE = task.pStartDate r.DUEDURATION = task.pDueDuration r.DUEDATE = task.pDueDate r.DUETIME = task.pDueTime

Creating the Form Task Type

169

The FormTaskAPI Object

r.FLAGS = task.pFlags r.TITLE = taskData.WORK.SUBWORKTASK_TITLE r.DESCRIPTION = task.pDescription r.INSTRUCTIONS = task.pInstructions r.PRIORITY = task.pPriority //Walk through the SubMap callback scripts and search for a //callback script that expands group members. If this type of //callback script is found, determine whether the callback script //expands the group and its subgroups or whether the callback //script expands only the first level of group members. r.SUBMAPIDCB = task.pSubMapIdCB for cbInfo in r.SUBMAPIDCB if ( cbInfo[ 1 ] == const.kCBExpandGroup ) groupFlag = cbInfo[ 2 ].Flag break end end //Remove the callback script that expands group members and //store the expand group flag value in the appropriate field of //the r.EXATTS Assoc. r.SUBMAPIDCB = $WFMain.WFMapPkg.RemoveCBInfoType( r.SUBMAPIDCB, \ const.kCBExpandGroup ) r.EXATTS = Assoc.CreateAssoc( Assoc.NotSetValue() ) r.EXATTS.GroupFlags = groupFlag end

170

Developers Guide for Extending Livelink Workflow

The FormTaskPaint Object

The FormTaskPaint Object


After you define the API for the task type that you are creating, you must provide the Workflow Painter with the information it requires to display the form and handle the data that the performer of the task enters in the form. To define the Workflow Painter information: 1. Orphan WebWFP:WebWFPRoot:WFTask in an OSpace in your custom module, and name it FormTaskPaint. 2. Change the fSubType feature to an Integer/Real type, and set it to 1. The fSubType feature holds a unique integer that works with the fType feature to identify the object. It must match the value specified for the fSubType feature in the FormTaskAPI object. 3. Change the fType feature to an Integer/Real type, and set it to 11. The fType feature holds a unique integer that works with the fSubType feature to identify the object. It must match the value specified for fType in the FormTaskAPI object. 4. Override the following scripts:
GetMapData() PutMapData()

For more information about these scripts, see the code samples that follow. 5. Create an HTML script, and name it formtask.html. For more information about this HTML file, see formtask.html, page 177. You have created the object necessary to define a task types Workflow Painter information. Now you must provide the code required to customize the information for the form task type.

GetMapData()
The following code sample describes how to display the Step Definition page for this task type when the creator of a workflow map edits the task in the Workflow Painter.
function Assoc GetMapData( \ Object prgCtx, \ Integer mapID, \ Integer taskID, \ Record mapRec, \ Record request ) Assoc Assoc Assoc a paneData performerInfo

Creating the Form Task Type

171

The FormTaskPaint Object

Assoc retVal Assoc tabPaneInfo Dynamic tmp Integer whichTab List tabList Object obj Record p Boolean Integer Record String String String knownUser = False i = 1 taskInfo = mapRec.TASKS[ taskID ] gif = 'guy.gif' name = [WebWFP_HTMLLabel.User] objName = .OSName

whichTab = ( RecArray.IsColumn( request, 'PaneIndex' ) ) ? \ Str.StringToInteger( request.PaneIndex ) : 1 //Specify the commonedittask.html file as the HTML file to //display when the creator of a workflow map edits the form task //type in the Workflow Painter. Specify the location of the //commonedittask.html file (that is, the webwfp module). retVal.HTMLFile = "commonedittask.html" retVal.ModuleName = 'webwfp' //Create an Assoc named retVal.Data and populate it with the //task and map information, including the ID of the workflow map, //the ID of the task, the URL of the next page to display, and the //header information for the task. retVal.Data = Assoc.CreateAssoc() retVal.Data.MapID = mapID retVal.Data.TaskID = taskID retVal.Data.NextURL = request.NextURL retVal.Data.HeaderLabel = 'Form Step Definition' retVal.Data.HeaderArgs = $WebWork.WFPkg.GetHeaderTypeArgs( \ 'PAINT', $WebWork.WFPkg.GetMapName( prgCtx, mapID, mapRec ) ) //Create an Assoc named tmp that stores all of the data required //to paint the first tab that appears when the creator of a //workflow map double-clicks the form task icon in the Workflow //Painter (that is, the General tab). tmp = Assoc.CreateAssoc() tmp.Label = [WebWork_HTMLLabel.General] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( $WebDSP.HTMLPkg.ArgsToURL( request ), i ) tmp.HelpKey = objName tmp.Active = FALSE //This task type uses the commonedittask.html file. This HTML //file expects to be passed a list of tab names, along with a //list of the data to be displayed on each tab. TabList is a list //of Assocs that lists each tab to be displayed. There is another //list of Assocs that identifies the panes to be displayed with //each tab. This second list of Assocs contains the HTML //information and all other information that the pane needs to //draw itself.

172

Developers Guide for Extending Livelink Workflow

The FormTaskPaint Object

tabPaneInfo.TabList = { tmp } a = Assoc.CreateAssoc() a.TaskInfo = taskInfo a.Gif = '16form.gif' a.MapID = mapID a.TaskID = taskID - 1 a.Forms = GetFormNames( prgCtx, mapRec ) a.NextURL = request.NextURL //Retrieves the name of the performer of the task and a .gif file //that represents the performer type (that is, a user or a //group). If the step is assigned to the initiator of the //workflow, then <Initiator> is returned as the performers name. if ( IsDefined( taskInfo.PERFORMERID ) ) if ( taskInfo.PERFORMERID == 0 ) name = [WebWFP_Label.LtInitiatorGt] else tmp = UAPI.GetByID( prgCtx.USession().fSession, \ taskInfo.PERFORMERID ) if ( !IsError( tmp ) ) knownUser = True name = tmp[ 1 ].NAME if ( tmp[ 1 ].TYPE != UAPI.USER ) gif = '2-guys.gif' end end end end performerInfo.Name = name performerInfo.Gif = gif performerInfo.KnownUser = knownUser performerInfo.ID = taskInfo.PERFORMERID a.PerformerInfo = performerInfo //Create an Assoc named tmp that stores the name of your custom //module, the HTML file to display, and the data that appears on //the General tab. tmp = Assoc.CreateAssoc() tmp.ModuleName = 'custmod' tmp.HTMLFile = 'formtask.html' tmp.Data = a tabPaneInfo.PaneList = { tmp } i += 1 //List the callback scripts that fire, if applicable. Assoc eventInfo List fields = { 'PERFORMERCB', 'READYCB', 'DONECB', 'KILLCB', \ 'RESURRECTCB' } List events = { [WebWFP_HTMLLabel.AssignStepPerformer], \ [WebWFP_HTMLLabel.StepBecomesReady], \ [WebWFP_HTMLLabel.StepIsDone], \ [WebWFP_HTMLLabel.StepIsKilled], \ [WebWFP_HTMLLabel.StepIsResurrected] }

Creating the Form Task Type

173

The FormTaskPaint Object

eventInfo.Events = events eventInfo.FieldNames = fields eventInfo = $WFMain.WFMapPkg.GetValidEvents( prgCtx, eventInfo ) if ( eventInfo.NumberOfEvents > 0 ) tmp = Assoc.CreateAssoc() tmp.Label = [WebWFP_HTMLLabel.EventScripts] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( request ), i ) tmp.HelpKey = objName + "." + 'EventScripts' tmp.Active = FALSE tabPaneInfo.TabList = { @tabPaneInfo.TabList, tmp } a = Assoc.CreateAssoc() a.EventInfo = eventInfo a.DataRec = taskInfo tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwfp' tmp.HTMLFile = 't_events.html' tmp.Data = a tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, tmp } end //Store the pane information for the tabs in the data Assoc. Then //set the active tab. By default, the active tab is 1 (or //whichever tab was originally passed into the script). if ( ( whichTab < 2 ) || ( whichTab > Length( \ tabPaneInfo.TabList ) ) ) whichTab = 1 end tabPaneInfo.TabList[ whichTab ].Active = True retVal.Data.TabInfo = tabPaneInfo retVal.Data.Tab = whichTab return( retVal ) end //Retrieves the names of the forms that have been added to the //workflow map, so that they can be displayed in the Form To //Display list on the Step Definition page. Function List GetFormNames( \ Object prgCtx, \ Record mapRec ) Assoc tmp List retVal Record r Object obj = $WFMain.WFPackageSubsystem.GetItemByName( \ 'Form' ) if ( IsDefined( obj ) ) for r in mapRec.WORK_PACKAGES if ( { r.Type, r.SubType } == { obj.fType, obj.fSubType } ) for tmp in obj.ReadyForModification( prgCtx, r.USERDATA ) retVal = { @retVal, tmp.Name } retVal = { @retVal, @tmp.SubForms } end

174

Developers Guide for Extending Livelink Workflow

The FormTaskPaint Object

break end end end //Add None as the first item in the Form To Display list on the //Step Definition page for the form task type. retVal = { [WebWFP_HTMLLabel.None], @retVal } return( retVal ) end

PutMapData()
The following code sample describes how to save the data that the creator of a workflow map enters on the Form Step Definition page.
function assoc PutMapData( \ Object prgCtx, \ Record mapRec, \ Record taskInfo, \ Record r ) Assoc paneData Assoc retVal Integer defaultDispo Integer flags Integer i Integer permAndDispositionFlags List dispositions List emptyDispos Object obj Real time Record p Integer count = 0 retVal.OK = TRUE if ( ( r.PaneIndex == 1 ) || ( r.PaneIndex == 0 ) ) //Save the step name. if ( RecArray.IsColumn( r, 'Title' ) ) taskInfo.Title = $LLIAPI.FormatPkg.ValToString( r.title ) end //Save the start date. if ( RecArray.IsColumn( r, 'StartDate' ) ) taskInfo.StartDate = ._CrackDate( r.StartDate ) end //Save the instructions. if ( RecArray.IsColumn( r, 'Instructions' ) ) taskInfo.Instructions = $LLIAPI.FormatPkg.ValToString( r.Instructions ) end

Creating the Form Task Type

175

The FormTaskPaint Object

//Save the duration. if ( RecArray.IsColumn( r, 'Duration' ) ) if IsDefined( r.Duration ) && Length( r.Duration ) Boolean inDays = ( r.DurationUnits == "Days" ) time = $LLIAPI.FormatPkg.StringToVal( r.Duration, RealType ) if ( Type( time ) != RealType ) retVal.OK = FALSE if inDays retVal.ErrMsg = [WebWork_ErrMsg.DurationMustBeANumberOfDays] else retVal.ErrMsg = [WebWork_ErrMsg.DurationMustBeANumberOfHours] end else taskInfo.DueDuration = $LLIAPI.FormatPkg.ConvertToSeconds( inDays, time ) end else taskInfo.DueDuration = Undefined end end //Save the group options. if RecArray.IsColumn( r, "GroupFlags" ) taskInfo.EXATTS.GroupFlags = Str.StringToInteger( r.GROUPFLAGS ) end //Save the selected form. if RecArray.IsColumn( r, "FormName" ) taskInfo.Form = IsDefined( taskInfo.Form ) ? taskInfo.Form : Assoc.CreateAssoc() taskInfo.Form.FORM_DISPLAYFORM = ( r.FormName == [WebWFP_HTMLLabel.None] ) ? Undefined : r.FormName end else //Save the callback script information. $WEBWFP.WFContentManager.StoreCallbackData( taskInfo, r ) end return retVal end

176

Developers Guide for Extending Livelink Workflow

The FormTaskPaint Object

formtask.html
The following code sample describes how to design the Web page that is displayed when the creator of a workflow map edits a form task in the Workflow Painter. You edit a form task on the Form Step Definition page, which is accessed by double-clicking the task's icon or by right-clicking the task's icon, and then clicking Edit.
//Pass Assoc.data into the formtask.html file. Assoc.data contains //all of the information required to display this Web page. ;;webscript formtask( Assoc data ) <!-- File: aaaformwfpaint/formtask.html --> ;;oscript{ Integer i List durationInfo String checked String dueDuration Dynamic taskInfo = data.TaskInfo String selForm = 'None' //Set up the URLs that define the links on the Step Definition //page (for example, the Map Editor link which jumps back to //the Workflow Painter). String nextURL = Str.Format( \ "%1?func=wfp.TaskEdit&MapID=%2&TaskID=%3&NextURL=%4", \ .URL(), data.MapID, data.TaskID, Web.Escape( data.NextURL ) ) String chooseUserURL = .url() + Str.Format( \

"?func=wfp.TaskUserSet&MapID=%1&TaskID=%2&PerformerID=%3&nextURL=% 4", \ data.MapID, data.TaskID, taskInfo.PerformerID, Web.Escape( nextURL ) ); List flags = { $WFMain.WFConst.kWFGroupStandard, \ $WFMain.WFConst.kWFGroupExpand, \ $WFMain.WFConst.kWFGroupExpandFull } List flagLabels = { [WebWFP_HTMLLabel.MemberAccept],\ [WebWFP_HTMLLabel.OneLevelExpand], \ [WebWFP_HTMLLabel.FullExpand] } ;;} //Set up the information that is displayed on the General tab of //the Form Step Definition page. This includes the title, the //performer, the group options, the form to display, and the //duration of the task. <TABLE BORDER="0" CELLPADDING="2" CELLSPACING="1"> //Set up the Step Name field. <TR>

Creating the Form Task Type

177

The FormTaskPaint Object

<TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebWFP_HTMLLabel.StepName_]`&nbsp;</FONT></TD> <TD> <IMG SRC="`.ModImg( 'custmod' )``data.Gif`" WIDTH="16" HEIGHT="16" ALIGN="BOTTOM" BORDER="0" VALIGN="TOP" HALIGN="RIGHT">&nbsp; <INPUT TYPE="TEXT" NAME="Title" SIZE="33" VALUE="`taskInfo.Title`" MAXLENGTH="255" ONCHANGE="markTaskEditDirty();"> <INPUT TYPE="HIDDEN" NAME="PerformerID" VALUE="`taskInfo.PerformerID`"> </TD> </TR> //Set up the Assigned To field. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebWFP_HTMLLabel.AssignedTo_]`&nbsp;</FONT></TD> <TD> <A HREF="`chooseUserURL`" ONCLICK="if ( leaveTaskEdit() ) taskEditGo( '`chooseUserURL`' ); else return false;"> <B>`[WebWFP_HTMLLabel.UserOrGroupColon]`</B> </A> <IMG SRC="`.Img()``data.PerformerInfo.Gif`" WIDTH="16" HEIGHT="16" ALIGN="BOTTOM" BORDER="0" VALIGN="TOP" HALIGN="RIGHT"> ;if ( data.PerformerInfo.KnownUser ) ;;call <.HTMLPrefix() + 'douserdialog.html'>( data.PerformerInfo.ID, data.PerformerInfo.Name ) ;else `%Ldata.PerformerInfo.Name` ;end </TD> </TR> //Set up the Group Options field. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebWFP_HTMLLabel.GroupOptions_]`&nbsp;</FONT></TD> <TD> <SELECT NAME="GroupFlags" ONCHANGE="markTaskEditDirty();"> ;for i = 1 to Length( flags ) ;if ( taskInfo.EXATTS.GroupFlags == flags[ i ] ) <OPTION VALUE="`flags[ i ]`" SELECTED>`flagLabels[ i ]` ;else <OPTION VALUE="`flags[ i ]`">`flagLabels[ i ]` ;end ;end </SELECT> </TD>

178

Developers Guide for Extending Livelink Workflow

The FormTaskPaint Object

</TR> //Set up the Form To Display field. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebFormWF_HTMLLabel.FormToDisplay_]`&nbsp;</FONT></T D> ;if ( IsDefined( taskInfo.Form ) ) ;if ( IsDefined( taskInfo.Form.FORM_DISPLAYFORM ) ) ;selForm = taskInfo.Form.FORM_DISPLAYFORM ;end ;end <TD> <SELECT NAME="FormName" ONCHANGE="markTaskEditDirty();"> `%L$HTMLPkg.FmtPopupItems( data.Forms, selForm )` </SELECT> </TD> </TR> //Set up the Accept Message field. <TR> <TD bgcolor="#CCCCCC" NOWRAP valign="TOP"><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebFormWF_HTMLLabel.AcceptMessage_]`&nbsp;</FONT></T D> <TD> <TEXTAREA NAME="Instructions" ROWS="6" COLS="45" WRAP="SOFT" ONCHANGE="markTaskEditDirty();">`taskInfo.Instructions`</TEXTAREA> </TD> </TR> //Set up the Duration field. ;;oscript{ if IsDefined( taskInfo.DueDuration ) durationInfo = $LLIAPI.FormatPkg.ConvertFromSeconds( taskInfo.DueDuration ) dueDuration = $LLIAPI.FormatPkg.ValToString( durationInfo[2] ) else durationInfo = { TRUE, 0 } end ;;} <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebWFP_HTMLLabel.Duration_]`&nbsp;</FONT></TD> <TD> <INPUT TYPE="TEXT" NAME="Duration" VALUE="`dueDuration`" SIZE="5" ONCHANGE="markTaskEditDirty();">

Creating the Form Task Type

179

The FormTaskPaint Object

;checked = ( durationInfo[ 1 ] ) ? "CHECKED" : "" <INPUT TYPE="RADIO" NAME="DurationUnits" `checked` VALUE="Days" ONCLICK="markTaskEditDirty();">`[WebWFP_HTMLLabel.Days]` ;checked = ( !durationInfo[ 1 ] ) ? "CHECKED" : "" <INPUT TYPE="RADIO" NAME="DurationUnits" `checked` VALUE="Hours" ONCLICK="markTaskEditDirty();">`[WebWFP_HTMLLabel.Hours]` </TD> </TR> //Set up the Start Date field. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebWFP_HTMLLabel.StartDate_]`&nbsp;</FONT></TD> <TD NOWRAP> ;;call <.htmlPrefix() + 'datefield.html'>( 'StartDate', taskInfo.StartDate, TRUE, TRUE ) </TD> </TR> //Set up the Action field, which contains the Add to Workflow //Definition button. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2">&nbsp;`[WebDoc_HTMLLabel.Action_]`&nbsp;</FONT></TD> <TD> <INPUT TYPE="Submit" VALUE="`[WebWFP_HTMLLabel.AddToWorkflowDefinitionButtonLabel]`"> </TD> </TR> </TABLE> ;;end

180

Developers Guide for Extending Livelink Workflow

The FormTaskWork Object

The FormTaskWork Object


After you define the API and Workflow Painter information for the form task type, you must define the information that handles the task when a workflow participant is working on it and when it is displayed on the Detailed Status page in Livelink. To define the information that handles the task and displays it on the Detailed Status page: 1. Orphan WebWork:WebWork Root:WFTask in an OSpace in your custom module, and name it FormTaskWork. 2. Set the fPaletteTask feature to TRUE. 3. Change the fSubType feature to an Integer/Real type, and set it to 1. The fSubType feature holds a unique integer that identifies the object. It must match the value specified for fSubType in the FormTaskAPI object and the FormTaskPaint object. 4. Change the fType feature to an Integer/Real type, and set it to 11. The fType feature holds a unique integer that works with the fSubType feature to identify the object. It must match the value specified for fType in the FormTaskAPI object and the FormTaskPaint object. 5. Change the fTaskGif feature to a String type, and set it to formtask.gif. The formtask.gif file identifies the graphic that you want to display for the form task in the Step Icon Palette. This image must be placed in your modules /support directory (for example, c:/opentext/modules/custmod_1_0_0/support). 6. Override the following scripts: GetDisplayPerformerInfo() GetPainterInfo() GetPainterMenu() GetStatusDisplay() GetTaskEditData() NewPerformer() For more information about these scripts, see the code samples that follow. 7. Create a script, and name it ReassignStep. For more information about the ReassignStep() script, see ReassignStep(), page 192. You have created the objects necessary to handle the task operations. Now you must provide the code required to customize the information for the form task type.

Creating the Form Task Type

181

The FormTaskWork Object

GetDisplayPerformerInfo()
The following code sample describes how to retrieve the name and ID of the Livelink user to which the task is assigned.
Function Dynamic GetDisplayPerformerInfo( \ Object prgCtx, \ Record taskRec ) Dynamic performer Dynamic retVal Integer performerID Object uSession = prgCtx.USession() //Search for the ID of the Livelink user to which the task is //assigned. if ( RecArray.IsColumn( taskRec, 'PERFORMERID' ) ) performerID = taskRec.PERFORMERID elseif ( RecArray.IsColumn( taskRec, 'WORK' ) ) performerID = taskRec.WORK.SUBWORKTASK_PERFORMERID elseif ( RecArray.IsColumn( taskRec, 'SUBWORKTASK_PERFORMERID' ) ) performerID = taskRec.SUBWORKTASK_PERFORMERID else performerID = Undefined end //If the Livelink user ID is defined, retrieve the Livelink user //name that is associated with it. if ( IsDefined( performerID ) ) performer = UAPI.GetByID( uSession.fSession, performerID ) //If the Livelink user name is found, create an Assoc named retVal //in which you store the Livelink user name and ID. if ( !IsError( performer ) ) retVal = Assoc.CreateAssoc() retVal.ID = performer[ 1 ].ID retVal.Name = performer[ 1 ].NAME end else retVal = [WebWork_Label.User] end return( retVal ) end

GetPainterInfo()
The following code sample describes how to define the information that the Workflow Painter needs to know about the form task type.
Function Assoc GetPainterInfo( \ Object prgCtx, \ Record task = Undefined ) Assoc info Assoc linkData Assoc retVal

182

Developers Guide for Extending Livelink Workflow

The FormTaskWork Object

String String

gif = .fTaskGif name = .GetTaskName()

//Retrieve the title and image used to represent the form task //type in the Workflow Painter. if ( IsDefined( task ) ) info = .GetDisplayInfo( prgCtx, task ) name = info.Title gif = info.Gif end retVal.ID = Str.String( .fType ) + '_' + Str.String( .fSubType ) //Specify a name for the form task in the Workflow Painter. retVal.Name = name //Specify the image that is displayed in the Workflow Painter to //represent the form task type. retVal.Gif = gif //Specify whether this task should be added to the Step Icon //Palette in the Workflow Painter. retVal.PaletteTask = .fpaletteTask //Specify whether this task can be duplicated. retVal.Duplicatable = .fDuplicatable //Specify the name of the Edit request handler for the form task //type.This request handler displays the form task when you click //the task name in your Tasks list. retVal.RHandler = 'wfp.TaskEdit' //Specify the name of the View request handler for the form //task type. This request handler displays the detailed status //for the form task when you click a task name on the Step List //tab. retVal.RHandlerWorkView = 'work.TaskDetail' //Specify the name of the Choose User request handler for the //form task type. This request handler is called when you right//click the form task icon in the Workflow Painter and then click //Choose Performer to specify the performer of the task. retVal.RHandlerChoose = 'wfp.TaskUserSet' //Specify the background color of the task in the Map Overview //window in the Workflow Painter. retVal.Background = 'flesh' //Specify the name of the module that defines this task type. retVal.Module = 'custmod'

Creating the Form Task Type

183

The FormTaskWork Object

//Retrieve the link information associated with the task type. //This includes the maximum number of link types that can come //from the task type and the maximum number of link types that //can go to this task type. linkData = .GetTaskTypeObj().GetLinkInfo() //Specify the maximum number of link types that can come from //this task type. Most task types can only have a single link //type coming from them (either a standard link or a loopback //link); however, a conditional step can have two link types //coming from it. retVal.MaxLinkTypes = linkData.MaxLinkTypes //Specify the type of links that can go to this task type. retVal.LinkTypesTo = linkData.LinkTypesTo //Specify the type of links that can come from this task type. retVal.LinkTypesFrom = linkData.LinkTypesFrom return( retVal ) end

GetPainterMenu()
The following code sample describes how to define the menu commands that appear when you right-click the form task types icon in the Workflow Painter:
Function List GetPainterMenu( Boolean viewonly ) //If the menu commands are not set to viewonly, populate the //following Assocs. List retval if ( !viewonly ) Assoca Assocb Assocc Assocd Assoce Assocf //Populate Assoca with the label, font, help, and userdata //values. The label is the name of the menu command, as it //appears in the popup menu that is displayed when you right//click the task type in the Workflow Painter. The font value //specifies the type of font used to display the menu command. //The help value is the text that is displayed on the Status //Bar when you position your cursor over the Edit command in //the popup menu. The userdata value identifies the request //handler that executes the Edit command. a.label = [WebWork_MenuLabel.Edit] a.font = "bold"

184

Developers Guide for Extending Livelink Workflow

The FormTaskWork Object

a.help = [WebWork_MenuLabel.EditThisStepSAttributes] a.userdata = "rhandler" //Populate Assocb with the label, help, and userdata values. //The label is the name of the menu command, as it appears in //the popup menu that is displayed when you right-click the //task type in the Workflow Painter. The help value is the text //that is displayed on the Status Bar when you position your //cursor over the Duplicate command in the popup menu. The //userdata value identifies the request handler that executes //the Duplicate command. b.label = [WebWork_MenuLabel.Duplicate] b.help = [WebWork_MenuLabel.DuplicateTheCurrentStepSelection] b.userdata = "duplicate" //Set c.separator to TRUE to insert a separator line between //the Duplicate and Choose Performer commands in the popup menu //that appears when you right-click the task type in the //Workflow Painter. c.separator = "true" //Populate Assocd with the label, help, and userdata values. //The label is the name of the menu command, as it appears in //the popup menu that is displayed when you right-click the //task type in the Workflow Painter. The help value is the text //that is displayed on the Status Bar when you position your //cursor over the Choose Performer command in the popup menu. //The userdata value identifies the request handler that //executes the Choose Performer command. d.label = [WebWork_MenuLabel.ChoosePerformer] d.help = [WebWork_MenuLabel.ChooseAUserOrGroupForThisStep] d.userdata = "rhandlerChoose" //Set e.separator to TRUE to insert a separator line between //the Choose Performer and Delete commands in the popup menu //that appears when you right-click the task type in the //Workflow Painter. e.separator = "true" //Populate Assocf with the label, help, and userdata values. //The label is the name of the menu command, as it appears in //the popup menu that is displayed when you right-click the //task type in the Workflow Painter. The help value is the text //that is displayed on the Status Bar when you position your //cursor over the Delete command in the popup menu. The //userdata value identifies the request handler that //executes the Delete command. f.label = [WebWork_MenuLabel.Delete] f.help = [WebWork_MenuLabel.DeleteTheCurrentStepSelection] f.userdata = "delete" //Create a list of the Assocs that hold the values for the menu //commands, and name the list retval. retval = { a, b, c, d, e, f }

Creating the Form Task Type

185

The FormTaskWork Object

//If the menu commands are set to viewonly, populate Assoca with //the label, font, help, and userdata values for the read-only //menu command (View). else Assoca a.label = [WebWork_MenuLabel.View] a.font = "bold" a.help = [WebWork_MenuLabel.ViewThisStep] a.userdata = "rhandlerWorkView" //Store Assoca in a list and name the list retval. retval = { a } end return retval end

Note The GetPainterMenu() script displays the Edit, Duplicate, Choose Performer, and Delete commands on the menu that appears when you right-click the form task icon in the Workflow Painter. The Choose Performer command is separated from the rest of the commands in the menu by two separator lines.

GetStatusDisplay()
The following code sample describes how to retrieve the information that is displayed when a workflow participant clicks the task name on the Step List page. The Step List page is accessed by clicking the Step List tab on the Detailed Status page.
function Assoc GetStatusDisplay( \ Object prgCtx, \ Dynamic context, \ Dynamic data = Undefined ) Assoc a Assoc retVal Assoc tabPaneInfo Assoc tmp Integer whichTab RecArray auditInfo RecArray disposition RecArray performer String title Record Record mapRec = context.MAP_PAINTER task = context

whichTab = ( RecArray.IsColumn( data, 'PaneIndex' ) ) ? \ Str.StringToInteger( data.PaneIndex ) : 1 //Populate the tmp Assoc with Label, URL, HelpKey, and Active //values. The Label value specifies the name of the tab that you

186

Developers Guide for Extending Livelink Workflow

The FormTaskWork Object

//are preparing for display (General). The URL value //identifies the page to display on the General tab. The HelpKey //value specifies the help page to display for this task type. If //set to TRUE, the Active value indicates that the tab is the //active tab (currently displayed). If set to FALSE, the Active //value specifies that the General tab is not the active tab and //must be called for display. tmp.Label = [WebWork_HTMLLabel.General] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( data ), 1 ) tmp.HelpKey = 'User' tmp.Active = FALSE //Store the tmp Assoc in a list and assign it to //tabPaneInfo.TabList. tabPaneInfo.TabList = { tmp } a.Gif = '16form.gif' //Determine whether the workflow participant can reassign the task //by checking permissions. a.CanReassign = $WFMain.WAPIPkg.CheckWFPermissions( \ prgCtx, task, $WFMain.WFConst.kWFChangeWork ) //Retrieve the disposition data for the task type. disposition = $WFMain.WAPIPkg.GetDispositionData( \ prgCtx, task.SUBWORKTASK_SUBWORKID, task.SUBWORKTASK_TASKID ) //If a disposition was specified for this task, add it to the //Assoc of data that is passed to the HTML file so that it can be //displayed. if ( IsDefined( disposition ) && Length( disposition ) ) a.Disposition = Str.Quote( $LLIAPI.FormatPkg.ValToString( \ disposition[ 1 ].VALUE ), '"' ) end a.WorkRec = task tmp = Assoc.CreateAssoc() tmp.ModuleName = 'formwf' tmp.HTMLFile = 'wwtuser.html' tmp.Data = a tabPaneInfo.PaneList = { tmp } //Retrieve the audit trail, if necessary. if ( whichTab == 2 ) auditInfo = $WFMain.WAPIPkg.GetAuditRec( \ prgCtx, task.WORK_WORKID, task.SUBWORK_SUBWORKID, \ task.SUBWORKTASK_TASKID ) if ( IsError( auditInfo ) ) auditInfo = Undefined end end

Creating the Form Task Type

187

The FormTaskWork Object

//Add the Audit tab to the Step Detail page for this type of //task. tmp = Assoc.CreateAssoc() tmp.Label = [WebWork_HTMLLabel.Audit] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( data ), 2 ) tmp.HelpKey = 'Audit' tmp.Active = FALSE tabPaneInfo.TabList = { @tabPaneInfo.TabList, tmp } tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwork' tmp.HTMLFile = 'audittrail.html' tmp.Data = auditInfo tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, tmp } tmp = .GetPackageStatusData( prgCtx, task, data, tabPaneInfo ) //Set the Active flag for the tab that is currently selected. if ( tmp.OK ) if ( ( whichTab < 2 ) || ( whichTab > Length( \ tabPaneInfo.TabList ) ) ) whichTab = 1 end tabPaneInfo.TabList[ whichTab ].Active = TRUE end //Set up an Assoc that returns all of the data required by //Livelink to draw the Step Detail page. retVal.OK = tmp.OK retVal.ErrMsg = tmp.ErrMsg retVal.HTMLFile = "wwt.html" retVal.ModuleName = 'webwork' retVal.Tab = whichTab retVal.TabInfo = tabPaneInfo retVal.Data = task //Set the masthead information so that the correct header is //displayed at the top of the Detailed Status page. retVal.HeaderArgs = $WebWork.WFPkg.GetHeaderTypeArgs( 'STATUS', \ task.SUBWORK_TITLE ) return( retVal ) end

188

Developers Guide for Extending Livelink Workflow

The FormTaskWork Object

GetTaskEditData()
The following code sample describes how to retrieve the information that is displayed when a workflow participant clicks the task name on the Tasks page in their Personal Workspace.
function Assoc GetTaskEditData( \ Object prgCtx, \ Record taskInfo, \ Record r ) Assoc a Assoc data Assoc paneData Assoc retVal Assoc tabPaneInfo Assoc tmp Dynamic status List tabList Object obj RecArray packages Record p String nextURL String url WAPIWORK work Boolean Boolean Integer String ok = true groupStep = False flags = WAPI.STARTTASK_FLAG_REEXECUTE formName = Undefined

//Determine whether the task has been assigned to a Livelink user //or a group. if !( $WFMain.WAPIPkg.CheckTaskAssignedToUser( prgCtx, taskInfo \ ) ) groupStep = True end //If the task is assigned to a group, set the HTMLFile, //ModuleName, and TaskInfo values. The HTMLFile value specifies //the name of the HTML file that displays the task and other data //that the form needs. The ModuleName value indicates in which //module the task type is defined. if ( groupStep ) data.HTMLFile = 'tgeneric.html' data.ModuleName = 'webwork' data.TaskInfo = taskInfo //Set the masthead information for the Tasks tab. data.HeaderArgs = $WebWork.WFPkg.GetHeaderTypeArgs( 'WORK', \ taskInfo.SUBWORK_TITLE ) //Set up the information required to display the first tab on //the Work Package page.

Creating the Form Task Type

189

The FormTaskWork Object

tmp.Label = [WebWork_HTMLLabel.General] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( r ), 1 ) tmp.Active = FALSE tabPaneInfo.TabList = { tmp } a.TaskInfo = taskInfo a.GroupStep = groupStep //Use the standard General tab that lets a group member accept //the task and add it to their task list. This is the same //General tab that is used for a User task type. tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwork' tmp.HTMLFile = 'taskgeneralpane.html' tmp.Data = a tabPaneInfo.PaneList = { tmp } tabPaneInfo.TabList[ 1 ].Active = True data.TabInfo = tabPaneInfo data.Tab = 1 else //Get a work handle. work = prgCtx.WSession().AllocWork() if ( !IsError( work ) ) //Use the StartTask() script to verify that the performer of //this task can access the data associated with the task. //This script sets up the work object so that it can be used //to access the work package. You can also use the //StartTask() script to make sure that this task is ready to //be complete (and was not completed already). status = WAPI.StartTask( \ work, \ r.WorkID, \ r.SubWorkID, \ r.TaskID, \ flags ) if ( !IsError( status ) ) //Retrieve the current work package. packages = $WFMain.WAPIPkg.GetWorkPackages( prgCtx, \ work, taskInfo ) if ( !IsError( packages ) ) if ( IsDefined( taskInfo.MAPTASK_FORM ) ) formName = taskInfo.MAPTASK_FORM.FORM_DISPLAYFORM end if ( IsDefined( formName ) )

190

Developers Guide for Extending Livelink Workflow

The FormTaskWork Object

nextUrl = \ Str.Format('%1?func=work.taskdone&workid=%2&subworkid=%3&' + \ 'taskid=%4&xAction=save&paneindex=1&nexturl=%5', \ r.SCRIPT_NAME, r.WorkID, r.SubWorkID, r.TaskID, Web.Escape( \ r.NextURL ) ) url = \ Str.Format('%1?func=custmod.formedit&workid=%2&subworkid=%3&' + \ 'taskid=%4&formname=%5&editable=true&nexturl=%6', \ r.SCRIPT_NAME, r.WorkID, r.SubWorkID, r.TaskID, Web.Escape( \ formName ), Web.Escape( nextURL ) ) retVal.RHandler = url //If the data was not retrieved correctly (OK=FALSE), //return an error message. else ok = False retVal.ErrMsg = \ [WebFormWF_ErrMsg.CouldNotFindFormContactWorkflowManager] end //If the data was not retrieved correctly (OK=FALSE), //return an error message. else ok = False retVal.ApiError = work retVal.ErrMsg = [Web_ErrMsg2.CouldNotAccessWorkpackage] end //If the data was not retrieved correctly (OK=FALSE), //return an error message. else ok = False retVal.ApiError = work retVal.ErrMsg = [Web_ErrMsg2.CouldNotAccessWork] end //Free the memory held by the WAPIWORK object. WAPI.FreeWork( work ) else ok = False retVal.ApiError = work retVal.ErrMsg = [Web_ErrMsg2.CouldNotAllocwork] end end retVal.OK = ok retVal.Data = data return( retVal ) end

Creating the Form Task Type

191

The FormTaskWork Object

NewPerformer()
The following code sample describes how to update task information if the task is reassigned in Livelink.
Function Assoc NewPerformer( \ Object prgCtx, \ Record taskRec, \ Integer newID ) //The name of this task is not dependent on the name of the //performer of the task, which means that there is no need to //call this script. Assoc retVal Boolean success = True taskRec.PERFORMERID = newID retVal.OK = success return( retVal ) end

ReassignStep()
The following code sample describes how to create a script that lets workflow participants reassign a task on the Step Detail page.
Function Boolean ReassignStep( \ Object prgCtx, \ Record taskRec, \ Record user, \ Record workData, \ WAPIWORK work ) Assoc userInfo Integer where List cbData Boolean ok = False Object uSession = prgCtx.USession() Object wSession = prgCtx.WSession() String cr = Str.EOL() //Verify that the performer of the task has permission to //reassign the task. if ( $WFMain.WAPIPkg.CheckWFPermissions( prgCtx, workData, \ $WFMain.WFConst.kWFChangeWork ) ) ok = wSession.StartTrans() //Reassign the task, and update the audit trail. if ( ok ) ok = UpdatePerformer( prgCtx, work, taskRec, user )

192

Developers Guide for Extending Livelink Workflow

The FormTaskWork Object

//If the task was reassigned to a group, remove the //group performer ID callback script. if ( ok ) if ( user.TYPE != UAPI.USER ) cbData = { { $WFMain.WFConst.kCBSetGrpStepPerformer, \ user.ID } } else cbData = Undefined end ok = UpdateMapTask( prgCtx, taskRec, cbData ) end if ( !wSession.EndTrans( ok ) ) ok = False end end end return( ok ) end Function Boolean UpdatePerformer( \ Object prgCtx, \ WAPIWORK work, \ Record old, \ Record new ) Boolean success List info //Make the WAPI call that reassigns the task. success = prgCtx.WSession().CheckRetVal( \ WAPI.ReassignTask( \ work, \ old.WORK.SUBWORKTASK_WORKID, \ old.WORK.SUBWORKTASK_SUBWORKID, \ old.WORK.SUBWORKTASK_TASKID, \ new.ID ) ) if ( success ) info = { 1, { Str.String( [WebWork_Message.StepReassignedTo] \ ), new.ID } } $WFMain.WAPIPkg.AddAuditData( prgCtx, work, info ) old.WORK.SUBWORKTASK_PERFORMERID = new.ID end return( success ) end Function Boolean UpdateMapTask( \ Object prgCtx, \ Record taskData, \ List cbData = Undefined ) Boolean success

Creating the Form Task Type

193

The FormTaskWork Object

WAPIMAPTASK Object WAPIMAP

task

session = prgCtx.WSession() map = session.AllocMap()

success = session.CheckRetVal( WAPI.LoadMapByID( map, \ taskData.WORKINFO.SUBWORK_MAPID ) ) if ( success ) task = WAPI.AllocNthMapTask( map, \ taskData.WORK.SUBWORKTASK_TASKID ) success = session.CheckRetVal( task ) if ( success ) task.pPerformerCB = cbData //Free the memory held by the WAPIMAPTASK object. WAPI.FreeMapTask( task ) success = session.CheckRetVal( WAPI.ReplaceMap( map ) ) end end //Free the memory held by the WAPIMAP object. WAPI.FreeMap( map ) return( success ) end

194

Developers Guide for Extending Livelink Workflow

Request Handlers

Request Handlers
Now that you have modified the scripts associated with the FormTaskWork object, you must create the custom request handlers that perform the operations requested when a workflow participant executes the new task type. The request handlers that you create are based on the LLRequestHandler object and are called FormEdit, FormEditStart, SaveForm, and SaveFormStart. To create the FormEdit and FormEditStart request handlers: 1. Orphan WebLL:LLRequestHandler in an OSpace in your custom module, and name it CustModuleLLRequestHandler. 2. Create a child object of the CustModuleLLRequestHandler object, and name it FormEdit. This custom request handler will be used to execute the form task operations. 3. In the FormEdit object, set the value of the fEnabled feature to TRUE. 4. Create a child object of the FormEdit object, and name it FormEditStart. 5. In the FormEdit object, edit the SetPrototype() script to define how to validate a request handler argument. For more information about the SetPrototype() script, see FormEdit SetPrototype(), page 197. 6. Run the SetPrototype() script. This script defines how to create a prototype that validates request handler arguments for the FormEdit request handler. This prototype is stored in the fPrototype feature. 7. Create a new Dynamic feature and name it fResponse. 8. Edit the Execute() script. For more information about the Execute() script for the FormEdit object, see FormEditExecute(), page 197. 9. In the FormEditStart object, edit the SetPrototype() script to define how to validate a request handler argument. For more information about the SetPrototype() script for the FormEditStart object, see FormEditStartSetPrototype(), page 201. 10. Run the SetPrototype() script. This script defines how to create a prototype that validates request handler arguments for the FormEditStart request handler. This prototype is stored in the fPrototype feature.

Creating the Form Task Type

195

Request Handlers

11. Edit the Execute() script. For more information about the Execute() script for the FormEditStart object, see FormEditStartExecute(), page 201. 12. In the Globals object, run the BuildOSpace() script. To create the SaveForm and SaveFormEdit request handlers: 1. Create another child object of the CustModuleLLRequestHandler object, and name it SaveForm. This custom request handler will be used to save the form when a workflow participant clicks the Save button. 2. In the SaveForm object, set the value of the fEnabled feature to TRUE. 3. Edit the SetPrototype() script to define how to validate a request handler argument. For more information about the SetPrototype() script for the SaveForm object, see SaveFormSetPrototype(), page 204. 4. Run the SetPrototype() script. 5. Edit the Execute() script. 6. Create a child object of the SaveForm object, and name it SaveFormStart. 7. In the SaveFormStart object, edit the Execute() script. 8. In the Globals object, run the BuildOSpace() script. To create a request handler group: 1. Orphan WebDSP:WebDSPRoot:RequestHandlerGroup in an OSpace in your custom module, and name it CustModuleRequestHandlerGroup. 2. In the CustModuleRequestHandlerGroup object, set the value of the fEnabled feature to TRUE. 3. Run the SetRequestHandlers() script. This script creates a list of the request handlers in the OSpace, and stores this list in the fRequestHandlers feature. 4. In the Globals object, run the BuildOSpace() script.

196

Developers Guide for Extending Livelink Workflow

Request Handlers

FormEditSetPrototype()
The following code sample describes how to validate request handler arguments for the FormEdit request handler. After you edit this script, you must run it to set the fPrototype feature for the request handler.
function void SetPrototype() .fPrototype =\ {\ { 'WorkID', IntegerType, [WebWork_RHParams.WorkID], FALSE \ },\ { 'SubWorkID', IntegerType, [WebWork_RHParams.SubWorkID], \ FALSE },\ { 'TaskID', IntegerType, [WebWork_RHParams.TaskID], TRUE, \ 0 },\ { 'FormName', StringType, [WebFormWF_RHParams.FormName], \ FALSE },\ { 'Editable', BooleanType, [WebFormWF_RHParams.Editable], \ FALSE },\ { 'NextURL', StringType, [WebWork_RHParams.NextURL], \ FALSE } \ } end

FormEditExecute()
The following code sample defines the operation of the FormEdit request handler for the form task type.
function Dynamic Execute( Dynamic ctxIn, Dynamic ctxOut, Record r ) Assoc extendedData Assoc form Assoc response Assoc saveData Assoc tmp Dynamic forms Dynamic status Dynamic taskInfo Object obj String mapName Object Object String prgCtx = .prgSession() subSystem = $WebForm.WebFormSubsystem pageType = 'WORK'

//Find the controller object for the forms task type (that is, the //form tasks API object). obj = $WFMain.WFPackageSubsystem.GetItemByName( 'Form' ) if ( IsDefined( obj ) ) taskInfo = prgCtx.WSession().LoadTaskStatus( \

Creating the Form Task Type

197

Request Handlers

r.WorkID, \ r.SubWorkID, \ r.TaskID ) if ( .Check( taskInfo ) ) mapName = taskInfo[ 1 ].SUBWORK_TITLE //Retrieve the forms that are available to the workflow. //This value depends on whether the workflow is at the Start //task and if the data in the workflow is editable. The list //of forms gets loaded from a different place if the Start //task is active or if it is loaded from the Detailed Status //page. if ( r.TaskID > 0 ) if ( r.Editable ) forms = obj.LoadTaskWorkData( prgCtx, Undefined, \ taskInfo[ 1 ], r.SubWorkID ) else forms = obj.GetTaskStatusWork( prgCtx, taskInfo[ 1 ], \ r.SubWorkID ) end else forms = obj.LoadWorkData( prgCtx, Undefined, r.SubWorkID ) end .Check( forms ) end else .fError = 'Could not find Form controller object.' end if ( !IsDefined( .fError ) ) forms = forms.Forms //Locate the form that is being edited, in the list of //forms that are available to the workflow. form = GetForm( forms, r.FormName ) if ( !IsDefined( form ) ) .fError = \ Str.Format( [WebFormWF_ErrMsg.CouldNotFindForm1], \ r.FormName ) end end if ( !IsDefined( .fError ) ) //Set up the Assoc that will control the operation of the Save //button on the form. if ( r.Editable ) saveData.Func = 'formwf.saveform' saveData.LL_ID = r.SubworkID saveData.LL_FormName = r.FormName saveData.LL_NextURL = Web.Escape( r.NextURL ) end if ( Str.Locate( Str.Upper( r.NextURL ), 'WORK.STATUSTASKS' ) )

198

Developers Guide for Extending Livelink Workflow

Request Handlers

pageType = 'STATUS' end //Retrieve any extra data that may be needed to display the //form. extendedData = subSystem.GetWorkflowExtendedData( \ prgCtx, \ r, \ mapName, \ pageType, \ form.TemplateID ) if ( .CheckError( extendedData ) ) //Add ctxOut to the request record. This is a gateway to the //Web server that serves up the HTML pages. tmp = Assoc.FromRecord( r ) tmp.CtxOut = ctxOut r = Assoc.ToRecord( tmp ) //Tell the forms subsystem to display the specified form. status = subSystem.DisplayForm( \ prgCtx, \ r, \ form.TemplateID, \ form.Data, \ saveData, \ extendedData ) if ( .CheckError( status ) ) response.Data = Assoc.CreateAssoc() response.Data.extendedData = extendedData //Store information about the form being displayed and //determine which HTML file to use to display the form. .fResponse = response if ( IsDefined( extendedData.HTMLFile ) ) .fHTMLFile = extendedData.HTMLFile else .fHTMLFile = Undefined end end end end return Undefined end function Assoc GetForm( \ List forms, \ String formName ) Assoc Assoc form formData

Creating the Form Task Type

199

Request Handlers

Assoc status Integer templateID Integer where Assoc retVal = Undefined Boolean found = False Object prgCtx = .prgSession() //Look through all the forms and sub-forms that are //available to the workflow and see if the specified //form exists. for form in forms if ( form.Name == formName ) templateID = form.TemplateID formData = form.Data found = True else where = formName in form.SubForms //Find the ID of the template object for the form and find //all the data that has been entered into the form. if ( IsDefined( where ) && ( where > 0 ) ) templateID = form.SubFormTemplateIDs[ where ] formData = form.Data found = True end end if ( found ) retVal = Assoc.CreateAssoc() retVal.TemplateID = templateID retVal.Data = formData break end end return( retVal ) end

200

Developers Guide for Extending Livelink Workflow

Request Handlers

FormEditStartSetPrototype()
The following code sample describes how to validate request handler arguments for the FormEditStart request handler. After you edit this script, you must run it to set the fPrototype feature for the request handler.
function void SetPrototype() .fPrototype =\ {\ { 'ID', IntegerType, [WebFormWF_RHParams.ID], FALSE },\ { 'FormName', StringType, [WebFormWF_RHParams.FormName], \ FALSE },\ { 'NextURL', StringType, [WebWork_RHParams.NextURL], \ FALSE } \ } end

FormEditStartExecute()
The following code sample defines the operation of the FormEditStart request handler for the form task type.
function Dynamic Execute( Dynamic ctxIn, Dynamic ctxOut, Record r ) Assoc extendedData Assoc form Assoc mapData Assoc response Assoc saveData Assoc tmp Dynamic status String mapName Object Object prgCtx = .prgSession() subSystem = $WebForm.WebFormSubsystem

//Load the workflow map. mapData = $WebWFP.WFPkg.LoadMap( prgCtx, r.ID ) if ( mapData.OK ) mapName = $WebWork.WFPkg.GetMapName( prgCtx, r.ID ) //Locate the specified form in the work package. form = GetForm( mapData.MapInfo.WORK_PACKAGES, r.FormName ) if ( !IsDefined( form ) ) .fError = Str.Format( [WebFormWF_ErrMsg.CouldNotFindForm1], r.FormName ) end else .fError = mapData.ErrMsg

Creating the Form Task Type

201

Request Handlers

end if ( !IsDefined( .fError ) ) //Set up the Assoc that will control the operation of the Save //button on the form. saveData.Func = 'formwf.saveformstart' saveData.LL_ID = r.ID saveData.LL_FormName = r.FormName saveData.LL_NextURL = Web.Escape( r.NextURL ) //Get any extra data that will be needed to display the form //in a workflow (for example, the masthead information). extendedData = subSystem.GetWorkflowExtendedData( \ prgCtx, \ r, \ mapName, \ 'WORK', \ form.TemplateID ) if ( .CheckError( extendedData ) ) //Add ctxOut to the request record. This is a gateway to the //Web server that serves up the HTML pages. tmp = Assoc.FromRecord( r ) tmp.CtxOut = ctxOut r = Assoc.ToRecord( tmp ) //Call the Livelink Forms module and tell it to display the //specified form. status = subSystem.DisplayForm( \ prgCtx, \ r, \ form.TemplateID, \ form.Data, \ saveData, \ extendedData ) if ( .CheckError( status ) ) response.Data = Assoc.CreateAssoc() response.Data.extendedData = extendedData .fResponse = response if ( IsDefined( extendedData.HTMLFile ) ) .fHTMLFile = extendedData.HTMLFile else .fHTMLFile = Undefined end end end end return Undefined end

202

Developers Guide for Extending Livelink Workflow

Request Handlers

function Assoc GetForm( \ RecArray work_packages, \ String formName ) Assoc form Assoc status Dynamic formData Dynamic retVal Integer templateID Integer where List forms List key Record r Boolean Object 'Form' ) Object found = False objType = $WFMain.WFPackageSubsystem.GetItemByName( \ prgCtx = .prgSession()

//Find the form data type in the workflows work package and //prepare it for use. if ( IsDefined( objType ) ) key = { objType.fType, objType.fSubType } for r in work_packages if ( key == { r.TYPE, r.SUBTYPE } ) forms = objType.ReadyForModification( prgCtx, r.USERDATA ) break end end end //Look through all the forms and sub-forms that are //available to the workflow and see if the specified //form exists. for form in forms if ( form.Name == formName ) templateID = form.TemplateID formData = form.Data found = True else where = formName in form.SubForms if ( IsDefined( where ) && ( where > 0 ) ) templateID = form.SubFormTemplateIDs[ where ] formData = form.Data found = True end end if ( found ) retVal = Assoc.CreateAssoc() retVal.TemplateID = templateID retVal.Data = formData

Creating the Form Task Type

203

Request Handlers

break end end return( retVal ) end

SaveFormSetPrototype()
The following code sample describes how to validate request handler arguments for the SaveForm request handler. After you edit this script, you must run it to set the fPrototype feature for the request handler.
function void SetPrototype() .fPrototype =\ {\ { 'LL_ID', IntegerType, [WebFormWF_RHParams.ID], FALSE },\ { 'LL_FormName', StringType, \ [WebFormWF_RHParams.FormName], FALSE },\ { 'LL_NextURL', StringType, [WebWork_RHParams.NextURL], \ FALSE } \ } end

SaveFormExecute()
The following code sample defines the operation of the SaveForm request handler for the form task type.
function Dynamic Execute( Dynamic ctxIn, Dynamic ctxOut, Record r ) Assoc form Assoc formData Assoc tmp Dynamic forms Dynamic status DAPINODE template Integer index Object obj String name Object prgCtx = .prgSession()

//Find the controller object for the forms task type (that is, //the form tasks API object). obj = $WFMain.WFPackageSubsystem.GetItemByName( 'Form' ) if ( IsDefined( obj ) ) //Load the forms that are available to this workflow. forms = obj.LoadWorkData( prgCtx, Undefined, r.LL_ID )

204

Developers Guide for Extending Livelink Workflow

Request Handlers

if ( .Check( forms ) ) forms = forms.Forms //Find the specific form that is being saved in the list of //forms that are available to the workflow. form = GetForm( forms, r.LL_FormName ) if ( IsDefined( form ) ) index = form.Index template = form.Template form = form.Form else .fError = Str.Format( [WebFormWF_ErrMsg.CouldNotFindForm1], r.LL_FormName ) end end else .fError = [WebFormWF_ErrMsg.CouldNotFindFormControllerObject] end if ( !IsDefined( .fError ) ) //Retrieve the data that was entered into the form. status = $WebForm.WebFormSubsystem.ValuesFromRequest( \ prgCtx, \ template, \ r, \ this ) if ( .CheckError( status ) ) //Store the data that was entered into the HTML page (form) //in the workflow. for name in Assoc.Keys( status.FormInfo ) form.Data.( name ) = status.FormInfo.( name ) end //Save the workflows form values back to the workflow. status = obj.UpdateForm( prgCtx, r.LL_ID, index, form ) end //Reset LL_NextURL to NextURL and set the NextURL to display //after the data is saved. tmp = Assoc.FromRecord( r ) tmp.NextURL = ( r.LL_NextURL[ 1 ] == '%' ) ? Web.UnEscape( tmp.LL_NextURL ) : tmp.LL_NextURL r = Assoc.ToRecord( tmp ) $WebForm.WebFormSubsystem.RedirectAfterSave( template, ctxOut, this, r, status ) end

Creating the Form Task Type

205

Request Handlers

return Undefined end function Assoc GetForm( \ List forms, \ String formName ) Assoc form Assoc status Integer templateID Integer where Assoc retVal = Undefined Boolean found = False Integer index = 1 Object prgCtx = .prgSession() for form in forms if ( form.Name == formName ) templateID = form.TemplateID found = True else where = formName in form.SubForms if ( IsDefined( where ) && ( where > 0 ) ) templateID = form.SubFormTemplateIDs[ where ] found = True end end if ( found ) if ( !IsDefined( form.Data ) ) form.Data = Assoc.CreateAssoc() end status = $FormApi.TemplateSubsystem.TemplateNodeFromID( \ templateID, \ prgCtx.DSession() ) if ( status.OK ) retVal = Assoc.CreateAssoc() retVal.Form = form retVal.Index = index retVal.Template = status.Template else echo( 'Could not find template for ', formName ) end break end index += 1 end return( retVal ) end

206

Developers Guide for Extending Livelink Workflow

Request Handlers

SaveFormStartExecute()
The following code sample describes how to validate request handler arguments for the SaveFormStart request handler. After you edit this script, you must run it to set the fPrototype feature for the request handler.
function Dynamic Execute( Dynamic ctxIn, Dynamic ctxOut, Record r ) Assoc form Assoc formData Assoc mapData Assoc status Assoc tmp DAPINODE template String name Object prgCtx = .prgSession()

//Load the workflow map. mapData = $WebWFP.WFPkg.LoadMap( prgCtx, r.LL_ID ) if ( mapData.OK ) //Locate the specified form in the work package. formData = GetForm( mapData.MapInfo.WORK_PACKAGES, r.LL_FormName ) if ( IsDefined( formData ) ) form = formData.Form template = form.Template //Retrieve the data that was entered into the form. status = $WebForm.WebFormSubsystem.ValuesFromRequest( \ prgCtx, \ template, \ r, \ this ) if ( .CheckError( status ) ) form.Template = Undefined //Store the values from the HTML page (form) into the //workflow. for name in Assoc.Keys( status.FormInfo ) form.Data.( name ) = status.FormInfo.( name ) end //Store the data in the workflow map definition.

Creating the Form Task Type

207

Request Handlers

formData.Record.USERDATA[ formData.Index ] = form //Save the workflow map definition. if ( !$WebWFP.WFPkg.SaveMap( prgCtx, mapData.MapInfo, mapData.MapHolder, True ) ) status.OK = False status.ErrMsg = Str.Format( [WebWFP_ErrMsg.CouldNotSaveMap1], prgCtx.WSession().fErrorMsg ) end end //Reset LL_NextURL to NextURL and set the NextURL to //display. tmp = Assoc.FromRecord( r ) tmp.NextURL = ( r.LL_NextURL[ 1 ] == '%' ) ? \ Web.UnEscape( tmp.LL_NextURL ) : tmp.LL_NextURL r = Assoc.ToRecord( tmp ) $WebForm.WebFormSubsystem.RedirectAfterSave( template, ctxOut, this, r, status ) else .fError = Str.Format( 'Could not find form "%1" in map.', r.LL_FormName ) end else .fError = mapData.ErrMsg end return Undefined end function Assoc GetForm( \ RecArray work_packages, \ String formName ) Assoc form Assoc status Integer templateID Integer where List forms List key Record r Assoc retVal = Undefined Boolean found = False Integer index = 1 Object objType = $WFMain.WFPackageSubsystem.GetItemByName( 'Form' ) // No XLATE Object prgCtx = .prgSession() //Find the forms data type in the workflows work package and //prepare it for use. if ( IsDefined( objType ) ) key = { objType.fType, objType.fSubType } for r in work_packages

208

Developers Guide for Extending Livelink Workflow

Request Handlers

if ( key == { r.TYPE, r.SUBTYPE } ) forms = r.USERDATA = objType.ReadyForModification( \ prgCtx, r.USERDATA ) break end end end for form in forms if ( form.Name == formName ) templateID = form.TemplateID found = True else where = formName in form.SubForms if ( IsDefined( where ) && ( where > 0 ) ) templateID = form.SubFormTemplateIDs[ where ] found = True end end //Look through all the forms and sub-forms //available to the workflow and see if the //form exists. If the form is found, store //about the form in an Assoc and return it //used to display the form. that are specified the information so that it can be

if ( found ) status = $FormApi.TemplateSubsystem.TemplateNodeFromID( \ templateID, \ prgCtx.DSession() ) if ( status.OK ) form.Template = status.Template if ( !IsDefined( form.Data ) ) form.Data = Assoc.CreateAssoc() end retVal = Assoc.CreateAssoc() retVal.Form = form retVal.Record = r retVal.Index = index else echo( 'Could not find template for ', formName ) end break end index += 1 end return( retVal ) end

Creating the Form Task Type

209

Request Handlers

210

Developers Guide for Extending Livelink Workflow

Index
A
access restricting access to event trigger scripts, 163 API object adding data types, 86, 99 adding task types, 32 architecture, 12 attachments, work packages, 2 attributes, work packages, 2 OSpace, 26 packaging, 30 query string, 28 request handler group, 28 Custom Display task example 1, 39 custom module adding data types, 85 adding event trigger scripts, 141 adding task types, 31 adding the form task type, 165 adding workflow types, 133 CustomDisplayAPI object, 39 CustomDisplayPaint object, 48 CustomDisplayWork object, 61 customerpane.html, 129

C
callback events, 21 callback scripts event trigger scripts, 141 CBExecute() script WFCustomScriptPkg object, 80, 136 ChangeAttribute() script, 146 CheckTaskDone() script, 87, 101 ChooseUser() script, 151 comments, work packages, 2 Configure request handler object, 28 CreateNewInstance() script, 87, 102 CreateReviewMapRec() script, 40 CreateWorkData() script, 87, 103 creator, workflow relationship, 3 cust_drop() script, 98 cust_sql() script, 97 custmod module, 26 Configure request handler, 28 configuring, 27 directory structure, 26

D
data types adding new, 85 adding schema, 95 adding the Table Values data type, 94 API object, 86, 99 creating database tables, 95 defining the Web object, 117 example 2, 94 Livelink Workflow architecture, 18 overview, 9 Web object, 91 Workflow Painter information, 89, 113 data types, routing legacy data, 18 database storage workflow management information, 14 DeleteWorkData() script, 87, 103

211

Request Handlers

directory structure module, 26

getting started, 25 GetWFTypeName() script, 134, 137 GetWFTypes() script, 134, 138

E
Evaluate step, 5, 17 event trigger scripts adding new, 141 architecture, 23 choosing, 142 example 4, 146 example 5, 151 general, 142, 144 Livelink Workflow architecture, 21 overview, 10 performer, 142, 149 restricting access, 163 submap, 142, 154 subsystems, 142 ExecuteCustTaskScript() script, 80 ExecuteScript() script, 81

H
HTML files customerpane.html, 129 projectpane.html, 126 redirect.html, 78 submap_tablevalues.html, 123 t_tablevalues.html, 115 t_user.html, 56 tablevalues.html, 126

I
icons Step Icon Palette, 5 Initiator step, 5, 17 initiator, workflow relationship, 3 introduction, 1

F
FactoryName() script, 163 Figure 1-1, The Workflow Painter, 4 Figure 1-2, Function Overview window, 6 Figure 1-3, Map Overview Window, 6 Figure 2-1, Workflow Management Group Permissions, 14 Figure 2-2, The WWork Table, 14 Figure 2-3, The WSubWork Table, 15 Figure 2-4, The WMapTask Table, 16 Figure 2-5, Executing Callback Scripts, 22 fObjectFactory feature, 163 forms creating the form task type, 165 Function Window, 6

L
legacy data, routing, 18 ListScripts() script, 82 ListTemplates() script, 82 Livelink Workflow, 1 architecture, 11, 12 Livelink Workflow Map, 19 LoadStartTaskWorkData() script, 87, 104 LoadTableValues() script, 105 LoadTaskWorkData() script, 88, 106 LoadWorkData() script, 88, 107

M
manager permissions, 4, 13 workflow relationship, 3 Map Editor page. See Workflow Painter Map Overview Window, 6 Milestone step, 5, 17 module architecture, 25 module directory structure, 26 module.ini file, 28

G
general event trigger scripts, 144, 146 GeneralCallbackScripts object, 144 GetData() script, 92, 118 GetDisplayPerformerInfo() script, 37, 62 GetMapData() script, 34, 48, 89, 114 GetPainterInfo() script, 33, 37, 43, 63 GetPainterMenu() script, 37, 64 GetStatusDisplay() script, 37, 66 GetSubmapData() script, 92 GetSubMapData() script, 119 GetTabInfo() script, 92, 120 GetTaskEditData() script, 37, 69 GetTaskGif() script, 72

N
NewPerformer() script, 38, 73

212

Developers Guide for Extending Livelink Workflow

Request Handlers

O
OScript Livelink Workflow architecture, 12 workflow status, 13 OSpace creating new, 26 overview, 1 extending Livelink Workflow, 9 Map Overview window, 6

P
package types, 85 packages, work, 2 Painter. See Workflow Painter Palette. See Step Icon Palette participant, workflow relationship, 3 paths, workflow, 2 performer event trigger scripts, 149 PerformerCallbackScripts object, 149 pFlags, 16 projectpane.html, 126 properties Function Overview window, 6 PutMapData() script, 35, 52, 90, 114 PutReviewData() script, 74 PutSubmapData() script, 92 PutSubMapData() script, 121

Q
query string setting up a custom module, 28

R
ReadyTaskForInitiation() script, 33, 43 ReassignStep() script, 75 redirect.html, 78 relationships, workflow, 3 RemoveWorkData() script, 88, 107 request handlers Configure, 28 RequestHandlerGroup object, 28 RequestHandlerGroup object, 28 roles. See workflow relationships routes, workflow, 2

S
SaveData() script, 92, 122 SaveTableValues() script, 108

SaveWorkData() script, 88, 109 schema, adding, 95 scripts callback, 21 CBExecute(), 80, 136 ChangeAttribute(), 146 CheckTaskDone(), 87, 101 ChooseUser(), 151 CreateNewInstance(), 87, 102 CreateReviewMapRec(), 40 CreateWorkData(), 87, 103 cust_drop(), 98 cust_sql(), 97 DeleteWorkData(), 87, 103 event trigger scripts, 141 ExecuteCustTaskScript(), 80 ExecuteScript(), 81 FactoryName(), 163 GetData(), 92, 118 GetDisplayPerformerInfo(), 37, 62 GetMapData(), 34, 48, 89, 114 GetPainterInfo(), 33, 37, 43, 63 GetPainterMenu(), 37, 64 GetStatusDisplay(), 37, 66 GetSubmapData(), 92 GetSubMapData(), 119 GetTabInfo(), 92, 120 GetTaskEditData(), 37, 69 GetTaskGif(), 72 GetWFTypeName(), 134, 137 GetWFTypes(), 134, 138 ListScripts(), 82 ListTemplates(), 82 LoadStartTaskWorkData(), 87, 104 LoadTableValues(), 105 LoadTaskWorkData(), 88, 106 LoadWorkData(), 88, 107 NewPerformer(), 38, 73 PutMapData(), 35, 52, 90, 114 PutReviewData(), 74 PutSubmapData(), 92 PutSubMapData(), 121 ReadyTaskForInitiation(), 33, 43 ReassignStep(), 75 RemoveWorkData(), 88, 107 SaveData(), 92, 122 SaveTableValues(), 108 SaveWorkData(), 88, 109 SetReviewData(), 88, 109 SetSubWorkData(), 88, 110 SetSubWorkReturnData(), 88, 110 SetTaskDefaults(), 33, 45 SetTaskRecFromMap(), 33 SetTaskRecFromMapTask(), 46 StartWF(), 134, 138 subworkflow(), 156

Index

213

Request Handlers

UpdateSubWorkData(), 111 UpdateTableValues(), 112 SetReviewData() script, 88, 109 SetSubWorkData() script, 88, 110 SetSubWorkReturnData() script, 88, 110 SetTaskDefaults() script, 33, 45 SetTaskRecFromMap() script, 33 SetTaskRecFromMapTask() script, 46 StandardTasks object adding task types, 32 Start step, 5, 16 StartWF() script, 134, 138 status Detailed Status page, 7 Workflow Status page, 7 status and display adding task types, 36 Step Icon Palette, 5 storage workflow management information, 14 submap event trigger scripts, 154, 156 Submap step, 5, 17 submap_tablevalues.html, 123 SubmapCallbackScripts object, 154, 156 subsystems event trigger scripts, 142 subworkflow() script, 156 sub-workflows on the fly example 6, 156

U
UpdateSubWorkData() script, 111 UpdateTableValues() script, 112 User step, 5, 17

W
WAPI Livelink Workflow architecture, 12 workflow status, 13 WAPI.MAPTASK_FLAG_AUTODONE, 16 WAPI.MAPTASK_FLAG_MILESTONE, 16 WAPIMAP, 20 WebModule object, 27 WFCustomScriptPkg object, 79, 134 WFDataTypes object adding data types, 86 WFPackage object defining the Web information, 91 defining Workflow Painter information, 89 WFPTableValues object defining the data types Workflow Painter information, 113 WFTask object defining status and display, 36 defining Workflow Painter information, 34 work package adding data types, 85 components, 6 definition, 2 workflow API, 12 definition, 1, 2 saving, 6 terminology, 2 workflow attributes example 4, 146 workflow events, 144 workflow maps definition, 2 saving, 6 storage, 19 Workflow Painter, 4 Function Window, 6 Map Overview Window, 6 Step Icon Palette, 5 workflow paths. See workflow routes workflow properties, 6 workflow relationships, 3 workflow roles. See workflow relationships workflow routes, 2 workflow status Detailed Status page, 7 WAPI and OScript, 13

T
t_tablevalues.html, 115 t_user.html, 56 Table Values data type example 2, 94 tablevalues.html, 126 task types adding custom scripts and templates, 83 adding the Custom Display task, 39 API object, 32 creating the form task type, 165 CustomDisplayPaint object, 48 CustomDisplayWork object, 61 example 1, 39 Livelink Workflow architecture, 16 overview, 9, 31 status and display, 36 Step Icon Palette, 5 WFCustomScriptPkg object, 79 Workflow Painter information, 34 terminology, 2

214

Developers Guide for Extending Livelink Workflow

Request Handlers

Workflow Status page, 7 workflow types adding new, 133 CBExecute(), 136

example 3, 135 Livelink Workflow architecture, 19 overview, 9, 133 WorkTableValues object, 117

Index

215

Request Handlers

216

Developers Guide for Extending Livelink Workflow

You might also like