231

Live Link

Embed Size (px)

Citation preview

Page 1: Live Link
Page 2: Live Link

Open Text Corporationprovides certain warrantiesand limitations in connectionwith the software that thisdocument describes. Forinformation about thesewarranties and limitations,refer to pages iii to vi.

Livelink SDK™

Developer’s Guidefor ExtendingLivelink Workflow™

This guide describes how to extend thefunctionality of Livelink Workflow™ using theLivelink Builder. Livelink Workflow is apreinstalled, optional Livelink® module that letsyou create and manage workflows in Livelink.

You can extend the functionality of the LivelinkWorkflow module by creating a custom modulethat incorporates new task types, data types,workflow types, and event trigger scripts withexisting workflow functionality.

3201810.1

Page 3: Live Link

ii

Copyright 1999 by Open Text Corporation. The copyright to these materials and any accompanying software isowned, without reservation, by Open Text. These materials and any accompanying software may not be copied in wholeor 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 ProjectCollaboration, 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 andare 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, OntarioN2L 5Z5Canada

(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 [email protected] or (800) 540-7292 or (519) 888-9933. Our supporthours are Monday through Friday, 8:30 a.m. to 8:00 p.m., EST. If you have suggestions for this publication, contact theOpen Text Publications Group at [email protected].

Visit our home page at http://www.opentext.com.

Page 4: Live Link

iii 11/97

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 andsimilar state and territory laws in Australia or the Consumer Guarantees Act in New Zealand, in respect of whichcertain liability may not be excluded.

LIMITED EXPRESS WARRANTY. Licensor warrants that (i) the media on which the SOFTWARE is distributed will befree from defects in materials and workmanship under normal use for a period of thirty (30) days from the date ofdelivery of the SOFTWARE; and (ii) unless altered or changed by someone other than Licensor, the SOFTWARE willperform substantially in accordance with the Licensor’s accompanying documentation for a period of thirty (30) daysfrom the date of delivery of the SOFTWARE.

LICENSEE REMEDIES. To the maximum extent permitted under applicable law, Licensor’s entire liability and your soleand exclusive remedy under the express limited warranty for media shall be the replacement of any defective mediawithout charge. To the maximum extent permitted under applicable law, Licensor’s entire liability and your sole andexclusive remedy under the express limited warranty for performance of the SOFTWARE shall be, at Licensor’s option,either to (i) correct the error, (ii) help you work around or avoid the error, or (iii) refund the purchase price for theSOFTWARE 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 imposedor implied by law are hereby excluded. Licensee may nevertheless have the benefit of certain rights or remedies pursuantto the Trade Practices Act and similar state and territory laws in Australia or the Consumer Guarantees Act in NewZealand in respect of which liability may not be excluded. Insofar as such liability may not be excluded then to themaximum 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 havingdefects in SOFTWARE repaired.

EXCLUSION OF LIABILITY/DAMAGES. The following is without prejudice to any rights you may have at law whichcannot legally be excluded or restricted. You acknowledge that no promise, representation, warranty or undertakinghas been made or given by Licensor (or related company of Licensor) to any person or company on its behalf inrelation to the profitability of or any other consequences or benefits to be obtained from the delivery or use of theSOFTWARE and any accompanying manuals or written materials. You have relied upon your own skill and judgmentin deciding to acquire the SOFTWARE and any accompanying manuals and written materials for use by you. Exceptas and to the extent provided in this Agreement, Licensor (or related company of Licensor) will not in anycircumstances 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 orinability to use or supply or non-supply of the SOFTWARE and any accompanying written materials. Licensor’s (orrelated company’s) total liability under any provisions of this Agreement is in any case limited to the amount actuallypaid by you for the SOFTWARE. This Agreement is governed by the laws of New South Wales, Australia or, wheresupplies are made in New Zealand, by the laws of New Zealand.

Page 5: Live Link

11/97 iv

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 fromdefects in materials and workmanship under normal use for a period of thirty (30) days from the date of delivery of theSOFTWARE; and (ii) unless altered or changed by someone other than Licensor, the SOFTWARE will performsubstantially in accordance with the Licensor’s accompanying documentation for a period of thirty (30) days from thedate 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 maynot apply to you.

LICENSEE REMEDIES. Licensor’s entire liability and your sole and exclusive remedy under the express limited warrantyfor media shall be the replacement of any defective media without charge. Licensor’s entire liability and your sole andexclusive remedy under the express limited warranty for performance of the SOFTWARE shall be, at the Licensor’soption, either to (i) correct the error, (ii) help you work around or avoid the error, or (iii) refund the purchase price forthe SOFTWARE which is returned to Licensor with a copy of your receipt. This Limited Warranty is void if failure ofthe SOFTWARE has resulted from accident, abuse, or misapplication. Any replacement SOFTWARE will be warrantedfor the remainder of the original warranty period.

NO OTHER WARRANTIES. To the maximum extent permitted by applicable law, Licensor and its suppliers disclaim allother representations, warranties, either express or implied, including, but not limited to implied warranties ofmerchantability and fitness for a particular purpose, with regard to the SOFTWARE and the accompanying writtenmaterials. This limited warranty gives you specific legal rights. You may have others which vary from state/jurisdictionto state/jurisdiction.

NO LIABILITY FOR CONSEQUENTIAL DAMAGES. To the maximum extent permitted by applicable law, in no eventshall Licensors (or related companies) be liable for any damages whatsoever (including without limitation, direct orindirect damages for personal injury, loss of business profits, business interruption, loss of business information, orany other pecuniary loss) arising out of the use of or inability to use this product, even if Licensor has been advised ofthe possibility of such damages. In any case, the Licensor’s (or related company’s) entire liability under any provisionof this Agreement shall be limited to the amount actually paid by you for the SOFTWARE. Because somestates/jurisdictions do not allow the exclusion or limitation of liability for consequential or incidental damages, theabove limitation may not apply to you.

EXPORT LAWS. Licensee shall comply fully with all laws and regulations of Canada (“Export Laws”) to assure thatneither the SOFTWARE nor any direct products thereof (1) are exported, directly or indirectly, in violation of ExportLaws, or (2) are used for any purpose prohibited by Export Laws, including without limitation, nuclear, chemical, orbiological weapons proliferation. Licensor may audit Licensee’s use of the SOFTWARE. All terms of any Licenseepurchase 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 attornsto the jurisdiction of the courts of the Province of Ontario and further agrees to commence any litigation which mayarise hereunder in the courts located in the Judicial District of York, Province of Ontario, Canada.

Page 6: Live Link

v 11/97

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 fromdefects in materials and workmanship under normal use for a period of thirty (30) days from the date of delivery of theSOFTWARE; and (ii) unless altered or changed by someone other than Licensor, the SOFTWARE will performsubstantially in accordance with the Licensor’s accompanying documentation for a period of thirty (30) days from thedate of delivery of the SOFTWARE. Any implied warranties on the SOFTWARE are limited to thirty (30) days. Somestates/jurisdictions do not allow limitations on duration of an implied warranty, so the above limitation may not applyto you.

LICENSEE REMEDIES. Licensor’s entire liability and your sole and exclusive remedy under the express limited warrantyfor media shall be the replacement of any defective media without charge. Licensor’s entire liability and your sole andexclusive remedy under the express limited warranty for performance of the SOFTWARE shall be, at the Licensor’soption, either to (i) correct the error, (ii) help you work around or avoid the error, or (iii) refund the purchase price forthe SOFTWARE which is returned to Licensor with a copy of your receipt. This Limited Warranty is void if failure ofthe SOFTWARE has resulted from accident, abuse, or misapplication. Any replacement SOFTWARE will be warrantedfor the remainder of the original warranty period.

NO OTHER WARRANTIES. To the maximum extent permitted by applicable law, Licensor and its suppliers disclaim allother representations, warranties, conditions or other terms, either express or implied, including, but not limited toimplied warranties and/or conditions of merchantability and fitness for a particular purpose, with regard to theSOFTWARE and the accompanying written materials. This limited warranty gives you specific legal rights. You mayhave others which very from state/jurisdiction to state/jurisdiction.

NO LIABILITY FOR CONSEQUENTIAL DAMAGES. To the maximum extent permitted by applicable law, in no eventshall Licensors (or related companies) be liable for any damages whatsoever (including without limitation, direct orindirect damages for personal injury, loss of business profits, business interruption, loss of business information, orany other pecuniary loss) arising out of the use of or inability to use this product, even if Licensor has been advised ofthe possibility of such damages. In any case, the Licensor’s (or related company’s) entire liability under any provisionof this Agreement shall be limited to the amount actually paid by you for the SOFTWARE. Because somestates/jurisdictions do not allow the exclusion or limitation of liability for consequential or incidental damages, theabove 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 applicablelaw.

This EULA is governed by the laws of England and Wales.

Page 7: Live Link

11/97 vi

Warranty and Special Provisions for the United States of Americaor Any Other Country

Limited Warranty

LIMITED WARRANTY. Licensor warrants that (i) the media on which the SOFTWARE is distributed will be free fromdefects in materials and workmanship under normal use for a period of thirty (30) days from the date of delivery of theSOFTWARE; and (ii) unless altered or changed by someone other than Licensor, the SOFTWARE will performsubstantially in accordance with the Licensor’s accompanying documentation for a period of thirty (30) days from thedate of delivery of the SOFTWARE. Any implied warranties on the SOFTWARE are limited to thirty (30) days. Somestates/jurisdictions do not allow limitations on duration of an implied warranty, so the above limitation may not applyto you.

LICENSEE REMEDIES. Licensor’s entire liability and your sole and exclusive remedy for media warranty shall be thereplacement of any defective media without charge. Licensor’s entire liability and your sole and exclusive remedy forperformance of the SOFTWARE shall be, at the Licensor’s option, either to (i) correct the error, (ii) help you workaround or avoid the error, or (iii) refund the purchase price for the SOFTWARE which is returned to Licensor with acopy of your receipt. This Limited Warranty is void if failure of the SOFTWARE has resulted from accident, abuse, ormisapplication. 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 disclaimall other warranties, either express or implied, including, but not limited to implied warranties of merchantability andfitness for a particular purpose, with regard to the SOFTWARE and the accompanying written materials. This limitedwarranty 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 eventshall 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, lossof business information, or any other pecuniary loss) arising out of the use of or inability to use this product, even ifLicensor has been advised of the possibility of such damages. In any case, the Licensor’s (or related company’s) entireliability under any provision of this Agreement shall be limited to the amount actually paid by you for theSOFTWARE. Because some states/jurisdictions do not allow the exclusion or limitation of liability for consequentialor 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 oftechnical data relating to certain commodities. Licensee hereby agrees to comply with the Export AdministrationRegulations of the United States as and hereby gives to Licensor the assurances required in the Export AdministrationRegulations.

U.S. Government Rights

Department of Defense End Users. Pursuant to DFARS Section 227.7202 and its successors, the Government’s rightto use, reproduce, or disclose the SOFTWARE and any accompanying documentation acquired under this contract issubject to the restrictions of Open Text’s standard commercial SOFTWARE license agreement.

Other Government Agency End Users. Pursuant to FARS Section 12.212 and its successors, the Government’s right touse, reproduce, or disclose the SOFTWARE and any accompanying documentation acquired under this contract issubject to the restrictions of Open Text’s commercial SOFTWARE license agreement. Manufacturer is Open TextCorporation, 185 Columbia Street West, Waterloo, Ontario N2L 5Z5.

Page 8: Live Link

vii

Typographical Conventions Used in This GuideAll information in the following table is case-sensitive unless otherwise noted.

Items Convention

File names, directorynames, folder names,path names, windownames, dialog box names,Web page names, URLs,and e-mail addresses

These items appear in regular (normal) typeface. Some elements maybe shown in italic to indicate placeholders.Examples:

• Run setup.exe to start the installation program.

• Open the Livelink_home/config/opentext.ini file in a text editor.

Note The placeholder Livelink_home represents the Livelinkroot directory (directory where Livelink was installed).

• Contact Open Text Customer Support at [email protected].

• In Windows NT Control Panel, double-click the Services icon toopen the Services dialog box.

• In your Web browser, go to the Open Text Web site athttp://www.opentext.com.

Names of user interfaceelements, such asbuttons, links, menus,check boxes, radiobuttons, lists, fields, andso on

These items appear in bold typeface.Examples:

• On the Go to menu, click Personal Workspace.

• In the Services list, click Livelink Intranet : service_name, and thenclick the Startup button.

• To store your documents outside the database, select the ExternalDocument Storage check box, and then click the Create Tablesbutton.

• Click the item’s Info icon to open its General Info page.

• Click the Admin Home link to go to the Livelink IntranetAdministration page.

Variable placeholders,references to otherdocuments, new orspecial terminology, andemphasis

These items appear in italic typeface.Examples:

• For more information, see the Livelink Administrator’s Guide.

• After you modify this parameter, you must restart the LivelinkIntranet server for the changes to take effect.

• You can scan new documents for content of interest by saving yoursearch criteria in a special type of query called a prospector.

• In your Web browser, go to the default Livelink start page atprotocol://host:port/URL_prefix/livelink.exe, where protocol is httpor https, host is the DNS name of the HTTP server host, port is theport number on which the HTTP server is listening, andURL_prefix is the prefix mapped to the Livelink_home/cgi directoryin the HTTP server.

Page 9: Live Link

viii

Items Convention

References to chaptersand sections ofdocuments, and citationsof messages displayed tousers

These items appear in “quotation marks.”

Examples:

• For more information, see Chapter Three, “Projects,” in theLivelink QuickStart guide.

• For more information, see “Item Types” in Chapter Five, “LivelinkItems.”

• For more information, see “Item Types,” page 150.

• If the import completes successfully, Oracle displays the message“Database import completed without errors.”

Text typed by users,operating systemcommands, codeexamples, feature names,method names, andobject names

These items appear in monospaced font.

Examples:

• In the User Name field, type Admin.

• At the operating system prompt, type start-llserver, and thenpress ENTER.

• When searching for users, you can set the maximum number ofusers displayed per page by setting the value (default is 30) of theMaxUsersToListPerPage parameter in the [general] section ofthe opentext.ini file.

Key names 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 clickthe items that you want to select.

Page 10: Live Link

ix

Table of Contents

Chapter OneChapter OneChapter OneChapter One

IntroductionIntroductionIntroductionIntroduction ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................1111Livelink Workflow Terminology ......................................................................................................................................2

Workflows, Workflow Routes, and Work Packages .........................................................................................2Workflow Relationships.............................................................................................................................................3The Workflow Painter.................................................................................................................................................4Workflow Status ...........................................................................................................................................................7

Extending Livelink Workflow ...........................................................................................................................................9

Chapter TwoChapter TwoChapter TwoChapter Two

The Livelink Workflow ModuleThe Livelink Workflow ModuleThe Livelink Workflow ModuleThe Livelink Workflow Module........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................ 11111111Livelink Workflow Architecture .................................................................................................................................... 12

WAPI .............................................................................................................................................................................. 12OScript .......................................................................................................................................................................... 12Using WAPI and OScript for Workflow Management and Status............................................................ 13

Understanding Task Types in Livelink Workflow ................................................................................................... 16Understanding Data Types in Livelink Workflow................................................................................................... 18Understanding Workflow Types in Livelink Workflow......................................................................................... 19Understanding Callback Events in Livelink Workflow.......................................................................................... 21

Event Trigger Scripts................................................................................................................................................ 23

Chapter ThreeChapter ThreeChapter ThreeChapter Three

Setting Up a Custom ModuleSetting Up a Custom ModuleSetting Up a Custom ModuleSetting Up a Custom Module.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... 25252525Setting up the Custmod Module ................................................................................................................................. 26

Creating the Module's Directory Structure ..................................................................................................... 26Creating an OSpace ................................................................................................................................................. 26Configuring the Module......................................................................................................................................... 27Orphaning the Configure Request Handler Object...................................................................................... 28Setting Up the Query String and the module.ini File .................................................................................. 28Orphaning a RequestHandlerGroup Object ................................................................................................... 28

Completing the Custmod Module .............................................................................................................................. 30

Page 11: Live Link

x

Chapter FourChapter FourChapter FourChapter Four

Adding New Task TypesAdding New Task TypesAdding New Task TypesAdding New Task Types............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................ 31313131Defining the Task Type’s API Object........................................................................................................................... 32Defining the Workflow Painter Information ............................................................................................................ 34Defining the Status and Display of the Task............................................................................................................ 36Example 1: Adding the Custom Display Task Type ............................................................................................... 39

The CustomDisplayAPI Object ............................................................................................................................. 39CreateReviewMapRec().................................................................................................................................. 40GetPainterInfo() ................................................................................................................................................ 43ReadyTaskForInitiation()................................................................................................................................ 43SetTaskDefaults().............................................................................................................................................. 45SetTaskRecFromMapTask()........................................................................................................................... 46

The CustomDisplayPaint Object ......................................................................................................................... 48GetMapData() .................................................................................................................................................... 48PutMapData() .................................................................................................................................................... 52t_user.html ......................................................................................................................................................... 56

The CustomDisplayWork object.......................................................................................................................... 61GetDisplayPerformerInfo()............................................................................................................................ 62GetPainterInfo() ................................................................................................................................................ 63GetPainterMenu()............................................................................................................................................. 64GetStatusDisplay() ........................................................................................................................................... 66GetTaskEditData() ............................................................................................................................................ 69GetTaskGif() ........................................................................................................................................................ 72NewPerformer() ................................................................................................................................................ 73PutReviewData()............................................................................................................................................... 74ReassignStep()................................................................................................................................................... 75redirect.html ...................................................................................................................................................... 78

The WFCustomScriptPkg Object......................................................................................................................... 79CBExecute() ........................................................................................................................................................ 80ExecuteCustTaskScript() ................................................................................................................................ 80ExecuteScript() .................................................................................................................................................. 81ListScripts() ......................................................................................................................................................... 82ListTemplates().................................................................................................................................................. 82

Adding Custom Scripts and Templates ............................................................................................................ 83

Page 12: Live Link

xi

Chapter FiveChapter FiveChapter FiveChapter Five

Adding New Data TypesAdding New Data TypesAdding New Data TypesAdding New Data Types ........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................ 85858585Defining the Data Type’s API Object .......................................................................................................................... 86Defining the Data Type’s Workflow Painter Information.................................................................................... 89Defining the Data Type’s Web Object........................................................................................................................ 91Example 2: Adding a New Data Type ......................................................................................................................... 94

Creating the Database Tables .............................................................................................................................. 95cust_sql() ............................................................................................................................................................. 97cust_drop() ......................................................................................................................................................... 98

Creating a Utility Script........................................................................................................................................... 98SetSubPaneIndexArg...................................................................................................................................... 99

Defining the Data Type’s API Object.................................................................................................................. 99CheckTaskDone() ...........................................................................................................................................101CreateNewInstance() ....................................................................................................................................102CreateWorkData() ..........................................................................................................................................103DeleteWorkData() ..........................................................................................................................................103LoadStartTaskWorkData() ...........................................................................................................................104LoadTableValues() .........................................................................................................................................105LoadTaskWorkData().....................................................................................................................................106LoadWorkData()..............................................................................................................................................107RemoveWorkData() .......................................................................................................................................107SaveTableValues() ..........................................................................................................................................108SaveWorkData() ..............................................................................................................................................109SetReviewData() .............................................................................................................................................109SetSubWorkData() .........................................................................................................................................110SetSubWorkReturnData()............................................................................................................................110UpdateSubWorkData().................................................................................................................................111UpdateTableValues().....................................................................................................................................112

Defining the Data Type’s Workflow Painter Information .........................................................................113GetMapData() ..................................................................................................................................................114PutMapData() ..................................................................................................................................................114t_tablevalues.html.........................................................................................................................................115

Defining the Data Type’s Web Object .............................................................................................................117GetData() ...........................................................................................................................................................118GetSubMapData() ..........................................................................................................................................119GetTabInfo() .....................................................................................................................................................120PutSubMapData()...........................................................................................................................................121SaveData().........................................................................................................................................................122

Page 13: Live Link

xii

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

Chapter SixChapter SixChapter SixChapter Six

Adding New Workflow TypesAdding New Workflow TypesAdding New Workflow TypesAdding New Workflow Types ........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................133133133133Creating Workflow Types .............................................................................................................................................134Example 3: Adding a Custom Workflow Type to the custmod Module ......................................................135

CBExecute() ...............................................................................................................................................................136GetWFTypeName() .................................................................................................................................................137GetWFTypes() ...........................................................................................................................................................138StartWF() ....................................................................................................................................................................138

Chapter SevenChapter SevenChapter SevenChapter Seven

Adding Event Trigger ScriptsAdding Event Trigger ScriptsAdding Event Trigger ScriptsAdding Event Trigger Scripts ............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................141141141141Choosing Event Trigger Scripts ..................................................................................................................................142Creating General Event Trigger Scripts ...................................................................................................................144Example 4: Updating Workflow Attributes ............................................................................................................146

ChangeAttribute() ..................................................................................................................................................146Creating Performer Event Trigger Scripts...............................................................................................................149Example 5: Assigning Steps to Workflow Participants.......................................................................................151

ChooseUser() ............................................................................................................................................................151Creating Submap Event Trigger Scripts ..................................................................................................................154Example 6: Defining Sub-Workflows On-The-Fly..................................................................................................156

subworkflow() ..........................................................................................................................................................156Restricting Access to Event Trigger Scripts............................................................................................................163

Appendix AAppendix AAppendix AAppendix A

Creating the Form Task TypeCreating the Form Task TypeCreating the Form Task TypeCreating the Form Task Type ............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................165165165165The FormTaskAPI Object...............................................................................................................................................166

GetPainterInfo() .......................................................................................................................................................166ReadyTaskForInitiation() ......................................................................................................................................167SetTaskDefaults() ....................................................................................................................................................168SetTaskRecFromMapTask() .................................................................................................................................169

The FormTaskPaint Object ...........................................................................................................................................171GetMapData()...........................................................................................................................................................171PutMapData() ...........................................................................................................................................................175

Page 14: Live Link

xiii

formtask.html...........................................................................................................................................................177The FormTaskWork Object...........................................................................................................................................181

GetDisplayPerformerInfo() ..................................................................................................................................182GetPainterInfo() .......................................................................................................................................................182GetPainterMenu() ...................................................................................................................................................184GetStatusDisplay() ..................................................................................................................................................186GetTaskEditData() ...................................................................................................................................................189NewPerformer() .......................................................................................................................................................192ReassignStep() .........................................................................................................................................................192

Request Handlers ............................................................................................................................................................195FormEdit—SetPrototype()...................................................................................................................................197FormEdit—Execute() .............................................................................................................................................197FormEditStart—SetPrototype() .........................................................................................................................201FormEditStart—Execute()....................................................................................................................................201SaveForm—SetPrototype() .................................................................................................................................204SaveForm—Execute()............................................................................................................................................204SaveFormStart—Execute() ..................................................................................................................................207

IndexIndexIndexIndex ....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................211211211211

Page 15: Live Link

xiv

Page 16: Live Link

1

Chapter One

Introduction

Livelink Workflow is a preinstalled, optional Livelink module that lets you create andmanage workflows in Livelink. Workflows are business processes that follow sequentialpaths—from start to completion. You create workflows to automate procedures that havea series of defined steps. For example, you can create a workflow that runs each time yousubmit an expense report. In this case, Livelink can route the document to a manager orsupervisor for approval, and then to your company’s Accounting department forpayment. The workflows you create can be simple or complex, depending on the tasksthat you want to automate.

You can extend Livelink’s workflow functionality by incorporating new task types, datatypes, workflow types, and event trigger scripts with the existing Livelink Workflowmodule. When you extend Livelink's workflow functionality in this way, the new tasktypes, data types, workflow types, and event trigger scripts are stored in a separate, custommodule. This simplifies installation and reduces the possibility of conflict when youupgrade Livelink or the Livelink Workflow module. For information about creating aseparate, custom module, see “Setting Up a Custom Module,” page 25.

Note Do not extend Livelink's workflow functionality by altering the originalLivelink Workflow module. If you do, future upgrades to Livelink or theLivelink 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

Page 17: Live Link

Livelink Workflow Terminology

2 Developer’s Guide for Extending Livelink Workflow

Livelink Workflow TerminologyBefore you can extend Livelink's workflow functionality, you must be familiar with theterminology and components involved in creating workflows. Understanding thesignificance of workflows, workflow routes, work packages, workflow relationships, andthe Workflow Painter from a Livelink user’s perspective, gives you the base knowledgerequired 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 paths—from start to completion.You create workflows to automate procedures that have a series of defined steps or tasksthat must be performed in sequence. The sequential paths of defined steps, along whichworkflows travel are called workflow routes. Some steps in a workflow route are tasks thatparticular Livelink users or groups are responsible for completing. Other steps do notinvolve direct user-interaction—these include Milestone steps which specify a date bywhich a certain number of workflow tasks should be complete, and Sub-workflow stepswhich initiate other workflow processes within the main workflow.

When a workflow is initiated, Livelink analyzes the first step and routes the correspondingtask to the Tasks page of the Livelink user or group to which it is assigned. When thatLivelink user or group completes the task, Livelink analyzes the second step in theworkflow and routes the corresponding task to the Tasks page of the Livelink user orgroup to which it is assigned (if necessary). Livelink continues analyzing and routinginformation in this way, until the workflow is complete.

Tip You can view the workflow tasks for which you are responsible byclicking the Tasks tab in your Personal Workspace.

The data that is associated with each Livelink workflow and that travels along theworkflow route is stored in a work package. Work packages contain the attachments (forexample, documents and other Livelink items) with which workflow participants work.Work packages can also contain attributes and comments. Attributes are fields whichstore information that you want to track throughout the workflow. Workflowparticipants 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 asthey complete their task assignments.

Note For more information about workflow attributes, see the Livelink onlinehelp.

In Livelink, workflows are originally represented by workflow maps. Workflow maps areLivelink items that let you define a specific business process and provide a graphicalrepresentation of the workflow route and work package.

Page 18: Live Link

Livelink Workflow Terminology

Introduction 3

Workflow Relationships

Workflow relationships define the responsibilities and capabilities that particular Livelinkusers and groups have in a workflow. There are four workflow relationships that Livelinkusers have: creator, initiator, participant, and manager.

Table 1-1: Workflow Relationships

Relationship Description

Creator Adds the workflow map to Livelink, paints it, and defines its work package

Initiator Initiates the workflow map, starting a workflow process

Participant Works on the components of the work package and completes tasks

Manager 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 workpackage and work route. Workflow tasks are displayed on the Tasks page in a workflowparticipant’s Personal Workspace. If you are responsible for completing the first task in aworkflow, the task is displayed on your Tasks page when the workflow is initiated. If youare responsible for completing a task that occurs later in the workflow, the task isdisplayed on your Tasks page when the task that precedes it in the workflow route iscomplete.

The initiator of a workflow is the Livelink user that starts the workflow process (usuallyby clicking the workflow name in Livelink). If the creator of the workflow map selects theDisplay at Initiation check box on the Start Step Definition page when creating theworkflow map, the workflow’s work package is routed to the initiator's task list before thefirst task is displayed on the first workflow participant’s Tasks page. This lets the initiatorof the workflow modify the work package (for example, add attachments or editattributes) before routing the first task to the assigned participant. If the creator of theworkflow map does not select the Display at Initiation check box on the Start StepDefinition page when creating the workflow map, the first task in the workflow is routedto the assigned participant immediately.

Workflow participants are responsible for reviewing the work package and completing thetasks that are listed on the Tasks page in their Personal Workspaces. When a workflowparticipant completes a task, Livelink routes the work package to the participant who isresponsible for completing the next task in the sequence.

The manager of a workflow monitors the workflow’s progress, confirms that milestonesare met, and makes adjustments to the work package when necessary. If you are themanager of an active workflow, you can click Workflow Status on the Go to menu tomonitor the workflow’s status. The Workflow Status page displays the titles of all theactive 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

Page 19: Live Link

Livelink Workflow Terminology

4 Developer’s Guide for Extending Livelink Workflow

workflow participant that is assigned to the current task, the workflow’s due date andtime, 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. Thismeans that the creator of a workflow map can specify a group of Livelink users as themanager of the workflow, and can then expand that group and assign differentpermission levels to the base group members and the workflow initiator. This means thatsome members of the workflow management group may view the detailed workflowstatus while others may not. Other members may have permissions to suspend, stop, ordelete workflows but may not have permission to change the data in the work package.

Note For more information about the Workflow Status page, see “WorkflowStatus,” page 7.

The Workflow Painter

You create workflow maps using Livelink’s Java-based Workflow Painter. The WorkflowPainter lets you paint a graphical representation of a workflow process and is located onthe Map Editor page in Livelink. The Workflow Painter provides a drag-and-dropinterface where you define the workflow route and work package using icons. TheWorkflow Painter contains the Step Icon Palette, the Function window, and the MapOverview window.

Figure 1-1: The Workflow Painter

Page 20: Live Link

Livelink Workflow Terminology

Introduction 5

Step Icon Palette

The Step Icon Palette contains icons which represent the different types of tasks that youcan 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 workpackage or cancel the workflow before the first task is routed to the workflowparticipant. The Start step is the first step in every workflow map and is displayed in theWorkflow Painter by default.

Defines a User step in the workflow, which lets you assign work to a Livelink user orgroup. You can use the User step to define the task that you want a Livelink user orgroup to perform and to provide guidelines for completing that task.

Defines an Evaluate step in the workflow, which lets you specify different workflowroutes based on the current status of the workflow. You can use the Evaluate step tospecify workflow conditions and to design the workflow routes that are associated withthose conditions. If the condition you specify is true, the workflow is routed along onepath. 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 onthe disposition of specific tasks.

Defines a Milestone step in the workflow, which specifies an important date that occursduring the execution of the workflow. You can use Milestone steps to set target dates atwhich time certain workflow tasks must be complete. You can then tell Livelink toinform the initiator and manager of the workflow if milestone dates have beenexceeded by sending them an e-mail notification (using Change Agents™). Milestonesteps 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 separateworkflow map as a sub-process within the main workflow

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

Page 21: Live Link

Livelink Workflow Terminology

6 Developer’s Guide for Extending Livelink Workflow

Function Window

By default, the Function window lets you edit the properties of a workflow map, save theworkflow 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 packagethat contains the data that the workflow participants require to complete their taskassignments. 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 updatingthe status and execution of the workflow map.

• Select documents and other Livelink items (for example, queries, URLs, forms, andspreadsheets) to include as part of the work package.

• Define attributes that store information as a means of tracking and managing thework process and that prompt for user input.

The Function Overview window also lets you save and initiate workflows. If you want toreuse a workflow map that you create in the Workflow Painter, you can save it to aLivelink location for which you have the Modify permission. If you do not want to reuse aworkflow 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 inthe Workflow Painter.

Figure 1-3: Map Overview Window

Page 22: Live Link

Livelink Workflow Terminology

Introduction 7

The modifications that you make to a workflow map in the Workflow Painter aredynamically updated in the Map Overview window. This window is especially useful forlarge 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 theworkflow after it is initiated in Livelink. The Workflow Status page displays the title,status, step information, due date, and relationship associated with the active workflowsin Livelink. The Workflow Status page is displayed when you click Workflow Status in theGo to menu.

Table 1-3: The Workflow Status Page

Status Description

Title The name assigned to the workflow at initiation. You can click this title todisplay detailed workflow status.

Info/Status Status information about the workflow. Valid values include OK, Step Late,Workflow Late, Suspended, Stopped, or Completed.

Step The name of the Livelink user or group that is assigned to the active task inthe workflow

Due Date The date and time by which the active task should be complete. If a due dateis not specified for a workflow, this column is set to No Due Date.

Relationship Your relationship to the active workflow. This value is set to Manage orInitiated, 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 inLivelink are displayed. If you click Initiated, only the workflows that you have initiatedare displayed on the Workflow Status page. If you click Managed, only the activeworkflows that you manage are displayed on the Workflow Status page. You can alsochoose to display workflows that are archived, not archived, or currently executing.

You can view more detailed workflow status by clicking the workflow title on theWorkflow Status page. The Detailed Status page contains five tabs, each describing thestatus of the active workflow in more detail.

Table 1-4: The Detailed Status Page

Status Tab Description

General 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 dateon which the workflow was completed. The General tab also displays theStop and Suspend buttons, which let you stop or suspend the execution ofthe workflow.

Page 23: Live Link

Livelink Workflow Terminology

8 Developer’s Guide for Extending Livelink Workflow

Table 1-4: The Detailed Status Page

Status Tab Description

Map View Displays a non-editable, graphical representation of the workflow map. Formore information about the Map View tab, see the Livelink online help.

Step List Displays the names of the steps in the workflow, the name of the performerof each step, the due date for each step, and the date on which the step iscompleted. If a step has not yet been completed, it is set to <AwaitingCompletion>.

Note You can view detailed information about each step by clickingthe step name. This displays the Step Detail page which letsyou reassign a task to another Livelink user or group, ifappropriate.

Audit Lists all events that occurred during the execution of the workflow,including the date, time, and name of the Livelink user or group thatperformed the events

Attachments Displays links to all of the items that are attached to the workflow. You canalso add attachments to the workflow from the Attachments page.

Attributes Displays all of the attributes that are defined for the workflow. If you havenot defined attributes for the workflow, this tab is not displayed on theDetailed Status page.

Page 24: Live Link

Extending Livelink Workflow

Introduction 9

Extending Livelink WorkflowIf you understand how the Livelink Workflow module operates within Livelink to let youcreate and manage workflows, you can begin to explore the possibilities for extendingLivelink'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 theStep Icon Palette. New task types let Livelink users add custom tasks to the workflowmaps that they create in Livelink. You add a new task type to Livelink when you want tocustomize the interface and operations presented to workflow participants for particulartypes of tasks in a workflow. When you add a new task type, you design the interface thatis presented to the performer of a workflow task and you define the custom functionalitythat the new interface provides. You can create task types that require workflowparticipants to perform automated tasks, such as filling out a form or sending a file to theprinter or fax machine. Then, when the new task becomes ready in an active workflow, acustom task is displayed on the corresponding workflow participant’s Tasks page. Formore 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 thework packages that accompany your workflows. By default, work packages can containthree data types: attachments, attributes, and comments; however, you can create a datatype for any new type of information that you want to add to a work package. Adding newdata types to Livelink also lets you customize the Livelink interface. For example, if thework packages that you include with your workflows usually contain many attributes, youcan create a data type that creates the attributes and groups them on tabs in the Livelinkinterface. This makes it easy for workflow participants to locate and use the attributes inthe 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 workflowtypes to Livelink. You add new workflow types when you want to apply a particular set ofcustom operations to different workflow map definitions. Adding new workflow types letsyou define the set of custom operations that are applied to a workflow map definitionwhen the creator of a workflow map chooses to create that particular type of workflow inLivelink. These custom operations can include adding workflow tasks, modifying theduration 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 moreinformation about adding new types of workflows, see “Adding New Workflow Types,”page 133.

Page 25: Live Link

Extending Livelink Workflow

10 Developer’s Guide for Extending Livelink Workflow

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

Page 26: Live Link

11

Chapter Two

The Livelink Workflow Module

A high-level extension of Livelink's workflow functionality, requires an advancedunderstanding of the architecture and operation of the existing Livelink Workflowmodule.

The functionality of the Livelink Workflow module is controlled by the WorkflowApplication Programming Interface (WAPI) and the OScript programming language.WAPI and OScript work together to create a robust workflow management system thatprovides 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 workin the main Livelink Workflow module, you can create your own module that extendstheir 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

Page 27: Live Link

Livelink Workflow Architecture

12 Developer’s Guide for Extending Livelink Workflow

Livelink Workflow ArchitectureThe Livelink Workflow module lets you create and manage workflows in Livelink usingthe Workflow Application Programming Interface (WAPI) and the OScript programminglanguage.

WAPI

WAPI is an application programming interface that maintains a database of workflowmaps (maps) and work packages (work). Workflow maps define specific tasks and thelinks that join those tasks in a workflow. Work is created when a workflow map isinitiated 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 mapsand work packages.

WAPI is responsible for most of the database interaction involved in the creation andexecution of Livelink workflows, including the maintenance of basic workflowinformation such as titles, due dates, status, and to-do lists. WAPI is also responsible formaintaining a complete audit trail.

OScript

The OScript programming language is an object-oriented scripting language that lets youdevelop and customize applications using the Livelink Builder. In Livelink, OScript isused to control the graphical user interface (GUI) for painting workflow maps anddisplaying to-do lists and task information to workflow participants.

OScript is used along with Livelink’s document management functionality to storeworkflow maps. It stores workflow maps as document nodes that can manipulateLivelink’s extensive document management functionality (including permissions, versioncontrol, and attributes). Additionally, OScript almost completely handles the flow of dataas it is routed through a workflow.

Note For information about the Workflow API (WAPI) built-in package whichis available in OScript, see the Livelink Developer’s Documentation.

Page 28: Live Link

Livelink Workflow Architecture

The Livelink Workflow Module 13

Using WAPI and OScript for Workflow Management andStatus

WAPI and OScript work together to manage the permissions that you can assign toworkflow managers and the status of workflows in Livelink.

The manager of a workflow monitors a workflow’s status and has the authority to modify,suspend, resume, or stop a workflow after it is initiated. WAPI lets the creator of aworkflow 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 asingle management group. Because WAPI and OScript work together in this way, thecreator of a workflow map can specify a group of Livelink users as the manager of theworkflow, and can then expand that group and assign different permission levels to thebase group members and the workflow initiator. This means that some members of theworkflow management group may view the detailed workflow status while others maynot. Other members may have permission to suspend, stop, or delete workflows but maynot have permission to change the data in the work package.

In the following image, the Publications group is the manager of the workflow and thegroup members and initiator have different permission levels.

Page 29: Live Link

Livelink Workflow Architecture

14 Developer’s Guide for Extending Livelink Workflow

Figure 2-1: Workflow Management Group Permissions

Whether the manager of a workflow is an individual Livelink user or a group of Livelinkusers, they are concerned with the due dates and status of the workflows that theymanage. In Livelink, WAPI is responsible for calculating and maintaining all of the duedates and completed dates for a workflow; however, WAPI is only aware of workflowstatus in terms of Suspended, Stopped, Archived, and so on. OScript is responsible fordefining 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 thetasks it contains.

Tip Because detailed status levels are determined by OScript, OScriptprogrammers can change the definition of the status levels or add newlevels 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 theLivelink database.

Figure 2-2: The WWork Table

The rows in the WWork table contain specific workflow management information, suchas due dates, milestone dates, permission settings, and status. For example, theWORK_DATECOMPLETED row, contains the date on which the workflow wascompleted.

Page 30: Live Link

Livelink Workflow Architecture

The Livelink Workflow Module 15

Workflow management and status information for sub-workflows is stored in a tablenamed 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. Forexample, the SUBWORK_DATECOMPLETED row, contains the date on which the sub-workflow was completed.

Page 31: Live Link

Understanding Task Types in Livelink Workflow

16 Developer’s Guide for Extending Livelink Workflow

Understanding Task Types in Livelink WorkflowTo 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 aworkflow are essentially the same—the difference is primarily determined by the valuesstored in the pFlags attribute that is associated with each task.

Each workflow task is associated with an attribute named pFlags which controls thebehavior of the task. There are two optional values for the pFlags attribute:WAPI.MAPTASK_FLAG_AUTODONE and WAPI.MAPTASK_FLAG_MILESTONE. These values arestored 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 thecurrent task does not require user interaction and can be marked finished as soon as itbecomes ready.

The WAPI.MAPTASK_FLAG_MILESTONE flag is a constant value that indicates to WAPI thatthe task should be marked as a Milestone step and that milestone dates should becalculated and stored. This flag also stores the milestone date (that is, the date by whichthe task is scheduled to be complete).

Table 2-1: Standard Workflow Task Types

Task Type Description

Start A preliminary task type which lets the initiator of a workflow add data tothe work package or cancel the workflow before it is routed to the firstworkflow participant. The Start task type is not included in theWAPIMAP database table as a step in the workflow route.

Note For more information about the WAPIMAP database table,see “Understanding Workflow Types in LivelinkWorkflow,” page 19.

Page 32: Live Link

Understanding Task Types in Livelink Workflow

The Livelink Workflow Module 17

Table 2-1: Standard Workflow Task Types

Task Type Description

User and Initiator Workflow task types which provide the main user interface into the datain the workflow. The only difference between the User and Initiator tasktypes is that Initiator tasks use Performer event trigger scripts to assignthe task to the Livelink user that initiated the workflow when the stepbecomes ready. The creator of a workflow map determines which Livelinkuser is assigned to a User task.

Note The User and Initiator task types do not set theWAPI.MAPTASK_FLAG_AUTODONE orWAPI.MAPTASK_FLAG_MILESTONE flags.

Evaluate A workflow task type which sets the WAPI.MAPTASK_FLAG_AUTODONEflag and enables a special callback script. This callback script tells WAPIwhether Livelink will take the TRUE route, the FALSE route, or theLOOPBACK route to continue the workflow. The Evaluate task type asksthe workflow's data types to return their data, and then compares the datawith the evaluation criteria stored for the task type to determine whichroute to take.

Milestone A workflow task type which sets the WAPI.MAPTASK_FLAG_AUTODONEand WAPI.MAPTASK_FLAG_MILESTONE flags. When the workflowreaches a Milestone task type, the task is marked finished as soon as itbecomes ready. This task tells WAPI to store and calculate a milestonedate, by which the task is scheduled to be complete.

Submap A workflow task type which uses a Submap callback script to start a sub-workflow. The task type lets the creator of a workflow map choose asecond workflow map from the Livelink database. This workflow map isinitiated as a sub-workflow of the main workflow map.

When a Submap task is reached, a Submap callback script locates theselected sub-workflow map node in Livelink, reads the sub-workflow mapdefinition, stores the definition in WAPI, and initiates it as a sub-workflow process. Callback scripts are also used to set up the data that ispassed into the sub-workflow from the main workflow and the data thatis returned to the main workflow when the sub-workflow finishes.

Note This task type does not set theWAPI.MAPTASK_FLAG_AUTODONE or theWAPI.MAPTASK_FLAG_MILESTONE flags.

Many of the task types that you define in a workflow map use callback scripts to performtheir operations. For example, the One Level Expand and Full Expand options in theGroup Options list (General tab) for the User or Initiator task type, use callback scripts todetermine which workflow participants must work on the corresponding User or Initiatortask. These options use a callback script to launch a sub-workflow that assigns the task tothe appropriate members of the group (depending on the option that you choose). TheMember Accept option does not use a callback script to perform its function. It leaves thetask 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.

Page 33: Live Link

Understanding Data Types in Livelink Workflow

18 Developer’s Guide for Extending Livelink Workflow

Understanding Data Types in Livelink WorkflowIn Livelink, workflows route data to workflow participants and instruct those participantsto perform a certain type of task. Because many different types of data can be routed inworkflows, the workflow data is packaged in self-sufficient objects. This means thatLivelink is not aware of the data types that flow through the workflow system and makesno 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. Forexample, a workflow can route legacy data that is stored outside of the Livelinkdatabase—as long as the data can be accessed. In this case, the data itself does not movethrough the workflow; instead, Livelink routes pointers to the data.

The self-sufficient objects which control the data that is routed in workflows know how tosave, retrieve, and display their own data. In fact, a workflow only knows two things aboutthe data it routes:

• Identifier information (type and subtype integers), which allows the workflow to callscripts contained in the self-sufficient object

• Information specified by the data type, which allows the object to access the correctdata for the workflow

Page 34: Live Link

Understanding Workflow Types in Livelink Workflow

The Livelink Workflow Module 19

Understanding Workflow Types in LivelinkWorkflow

Livelink uses two different types of maps to create and process workflows: the LivelinkWorkflow 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 theWorkflow 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 otherfields that are used for temporary storage when the workflow map is being painted andbefore it is initiated.

When you create a workflow map in Livelink, Livelink actually creates an emptydocument item. This empty document item is represented by a workflow map node inLivelink. 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 Recordthat represents the Livelink Workflow Map definition is added (in BLOB format) as a newversion to the document item in Livelink. Each time you modify and save the workflowmap, 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 theAttachments work package data type), an attachment folder is stored as a child of theworkflow map node in Livelink.

Note Because workflow maps are stored as document items in the Livelinkdatabase, they can take advantage of Livelink’s document managementfeatures, such as permissions and version control.

When a workflow map is opened in the Workflow Painter, a copy of the workflow mapnode is temporarily stored in a hidden folder. The changes that you make to a workflowmap in the Workflow Painter are stored in the temporary copy of the workflow mapnode—until you save them. When you save the changes you made in the WorkflowPainter, the temporary copy of the workflow map node is added to the original node as anew version. The temporary version of the workflow map node is stored in the Livelinkdatabase for seven days, which allows you to return to a map that is in the process ofbeing painted (but not yet saved) within seven days.

Page 35: Live Link

Understanding Workflow Types in Livelink Workflow

20 Developer’s Guide for Extending Livelink Workflow

The WAPIMAP

When a workflow map is initiated, the Livelink Workflow Map definition is used to createthe WAPIMAP. The WAPIMAP stores the routing information that is used by WAPI tocontrol the workflow route. This map definition is stored in the WMap and WMapTaskdatabase tables and is used throughout the life of the workflow.

At the time of initiation, the Livelink Workflow Map and the WAPIMAP are completelyseparate—any changes made to the painted Livelink Workflow Map do not affect theactive workflows that were initiated from the Livelink Workflow Map definition. Thismeans that if you change the route defined in a workflow map definition, the changes donot affect the route of those workflows that have already been initiated. Similarly, anychanges made to a specific workflow’s WAPIMAP after initiation do not affect theLivelink Workflow Map.

Page 36: Live Link

Understanding Callback Events in Livelink Workflow

The Livelink Workflow Module 21

Understanding Callback Events in LivelinkWorkflow

Callback events are workflow events or actions that tell WAPI to run certain scripts atspecific times in the execution of a workflow map. When you add a callback event to aLivelink workflow, you instruct the workflow to perform an operation when a specificworkflow event occurs.

Callback event data is stored in special columns in the WAPI database tables. Each ofthese columns is also associated with a specific workflow event. For example, one columnis 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 eventdata in the WAPI database column that is associated with the workflow event that youwant to initiate the callback event during the execution of the workflow. Then, when theworkflow is initiated in Livelink, Livelink checks the WAPI database columns todetermine if and when callback events should occur.

Tip You can identify the columns in the WAPI database table that storecallback event data by their names—which all end in CB.

For example, if you want a callback event to occur when a specific workflow step becomesready, you store the callback event data in the WAPI database table column that isassociated with a workflow step becoming ready. Then, when Livelink reaches that step inthe workflow, the callback event occurs.

Page 37: Live Link

Understanding Callback Events in Livelink Workflow

22 Developer’s Guide for Extending Livelink Workflow

Figure 2-5: Executing Callback Scripts

When Livelink encounters callback event data in a WAPI database column, it calls a scriptthat is stored in the WAPISESSION object. This script examines the workflow event thattriggered the callback event (for example, the step becoming ready), and then callsanother script named GenericCB().

Note The GenericCB() script is not called if the callback event was triggeredwhen a workflow participant added a row to an audit trail.

Page 38: Live Link

Understanding Callback Events in Livelink Workflow

The Livelink Workflow Module 23

The GenericCB() script examines the callback event data that is stored in thecorresponding column of the WAPI database table. This data must be formatted as a listof lists, where each sub-list contains two elements: an integer and the additional data thatis required to execute the callback event. The GenericCB() script examines the integerelement and does one of two things (based on its value). If the integer is a value between 1and 99, a standard workflow operation is performed. If the integer is a value outside ofthis range, the GenericCB() script calls another script named CustomCB(), indicating thata custom callback event is present in the Livelink workflow.

The CustomCB() script begins loading all of the custom objects that are registered inWAPI and calling their CBExecute() scripts. When the CustomCB() script calls aCBExecute() script that matches the value of the integer associated with the callbackevent, it runs that CBExecute() script and returns an Assoc. The CBExecute() scriptperforms 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 Handledis set to FALSE, the process continues until the operation can be handledor performed.

Event Trigger Scripts

If you want to use callback events to add functionality to Livelink, but you do not want tocreate a supporting interface—or if you want to add functionality that does not require aninterface—you can use special classes of callback events called event trigger scripts. Eventtrigger scripts provide an interface to callback events in Livelink. When you add eventtrigger scripts to Livelink, this interface (the Event Scripts tab) becomes available in theLivelink interface—allowing the creator of a workflow map to determine whichoperations to associate with the particular workflow events that occur in the execution ofa workflow map.

There are three types of event trigger scripts: general event trigger scripts, performer eventtrigger scripts, and submap event trigger scripts. For more information about eventtrigger scripts, see “Adding Event Trigger Scripts,” page 141.

Note All event trigger scripts are contained in a transaction, along with theworkflow event that triggered them. This means that if you create anevent trigger script that is supposed to run at workflow initiation timeand the script returns an error, the workflow initiation also fails. If anevent trigger script is attached to a step-ready event and fails, the step-ready event also fails.

Page 39: Live Link

Understanding Callback Events in Livelink Workflow

24 Developer’s Guide for Extending Livelink Workflow

Page 40: Live Link

25

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 createa custom module that stores the new functionality you are adding. After it is installed, thiscustom module works with the Livelink Workflow module to extend the workflowcapabilities of Livelink.

Always store the custom functionality that you create in a module that can be installedand uninstalled separately from all other Livelink modules. If you modify an originalLivelink module (for example, the Livelink Workflow module), future updates to themodule will automatically overwrite your changes.

Note For more information about creating a Livelink module, see the LivelinkModule Development Guide. For specific information about using theLivelink Builder, see the Livelink Builder Developer’s Guide.

In this chapter, you will learn how to:

• Create the module’s directory structure

• Create an OSpace

• Create a Configure request handler and request handler group for the module

Page 41: Live Link

Setting up the Custmod Module

26 Developer’s Guide for Extending Livelink Workflow

Setting up the Custmod ModuleBefore you can add a new task type, data type, workflow type, or callback event triggerscript to a Livelink workflow, you must create a module which stores the newfunctionality. This module acts as a shell for the new functionality and can be installed oruninstalled independently of other Livelink modules. The module that you create in thischapter is named custmod.

Creating the Module's Directory Structure

You begin setting up a module by creating the module’s directory structure in the /stagingdirectory 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 minorversion 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 theHTML 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 theOSpace that will contain the functionality of the module. All OSpaces have the .oll fileextension.

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 /ospacedirectory (for example, c:/opentext/staging/custmod_1_0_0/ospace).

Page 42: Live Link

Setting up the Custmod Module

Setting Up a Custom Module 27

Configuring the Module

Each Livelink module contains a WebModule object which stores the configurationinformation for the module. The WebModule object defines the name of the module as itappears on the Livelink administration pages, the internal name of the module, theOSpaces contained in the module, the major version number, minor version number,build level, and revision number of the module, the module’s dependencies, and thescript 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, andname 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 objectin 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 pageswhen 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 inlowercase letters and must match the name you used when you created your moduledirectory 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 yourcustom module. The major version number, minor version number, and revisionnumber must match those you used when you created the module’s directorystructure. The build level is for your personal reference only.

7. In the Custmod Globals object, run the BuildOSpace() script.

Page 43: Live Link

Setting up the Custmod Module

28 Developer’s Guide for Extending Livelink Workflow

Orphaning the Configure Request Handler Object

The Configure request handler object is used to install, uninstall, and upgrade yourcustom module. This object defines the name of your module, the schema modificationsrequired 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 ofyour 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 callsthe request handler during installation. When this is complete, you can create themodule.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 tofunc=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 savethe custmod.ini file in the /custmod_1_0_0 directory (the root of your module’sdirectory structure).

Orphaning a RequestHandlerGroup Object

The RequestHandlerGroup object keeps track of all the request handlers in your custmodOSpace. This allows the Configure request handler object that you just created to beregistered in the RequestHandlerSubsystem.

To create a RequestHandlerGroup object:

1. Orphan WebDSP:WebDspRoot:RequestHandlerGroup in the custmod OSpace ofyour 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.

Page 44: Live Link

Setting up the Custmod Module

Setting Up a Custom Module 29

You can now start your Livelink Intranet service and install your custom module inLivelink. For more information about installing modules, see the Livelink InstallationGuide.

Tip You can set the value of the changeStateOspaces variable in theWebBuilder.lxe file to CUSTMOD (all uppercase letters), to open thecustmod OSpace in an unlocked state in the Livelink Builder. TheWebBuilder.lxe file is stored in the main directory of your primaryLivelink installation (for example, c:/opentext).

Page 45: Live Link

Completing the Custmod Module

30 Developer’s Guide for Extending Livelink Workflow

Completing the Custmod ModuleNow that you have set up the custmod module, installed it, and prepared it for use inLivelink, you can begin to create the functionality required to add a new task type, datatype, 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 toyour module. In this chapter, you will add a task type that lets the creators ofworkflow maps customize the interface that is presented to workflow participantsand the operations that occur when the workflow participant works on the task inLivelink.

• Chapter Five, “Adding New Data Types,” describes how to add new types of data toyour module. In this chapter, you will add a data type that creates a series ofworkflow attributes and arranges them on custom tabs that become available toworkflow participants when they work on the corresponding tasks.

• Chapter Six, “Adding New Workflow Types,” describes how to add new types ofworkflows to your module. In this chapter, you will create a custom workflow thatappends 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 triggerscripts to your module. In this chapter, you will create three event trigger scripts thatperform different functions when different workflow events occur.

Each of these chapters provides you with general information for incorporating newworkflow functionality. This general information is supported by a specific example ofhow to apply the functionality—complete with code samples.

Tip Because the examples in each chapter can become quite long andcomplex, consider cutting and pasting the code samples into the scriptsyou create in Livelink Builder, when working on the examples. Typingthe code samples manually is very time-consuming.

Page 46: Live Link

31

Chapter Four

Adding New Task Types

New task types let Livelink users add custom steps to the workflow maps that they createin Livelink. You add a new task type to Livelink when you want to customize the interfaceand operations presented to workflow participants for particular types of tasks in aworkflow. When you add a new task type, you design the interface that is presented to theperformer 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 customizethe interface and operations that are presented to workflow participants when they workon tasks—without using the Livelink Builder. This task type could allow the creators ofworkflow maps to attach HTML templates that define an interface to the tasks. Thecreators of workflow maps could also attach callback scripts that perform customoperations. Then, different templates and scripts could be associated with different tasksof 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. TheWFMain:WFRoot:WFObjectTypes:WFTaskTypes:StandardTasks object is the API objectfor the task type. The WebFP:WebWfpRoot:WFTask object controls the informationrequired by the Workflow Painter to add the task to a workflow map. TheWebWork:WebWorkRoot:WFTask object controls the task when a workflow participant isworking 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 type’s API object, Workflow Painter information, and statusinformation

• Add a new task type to your custom module

Page 47: Live Link

Defining the Task Type’s API Object

32 Developer’s Guide for Extending Livelink Workflow

Defining the Task Type’s API ObjectYou begin creating a task type by defining its API object. This object contains features andscripts that are required for the operation of a workflow task type and is namedStandardTasks. You can find the StandardTasks object inWFMain:WFRoot:WFObjectTypes:WFTaskTypes.

Notes • The StandardTasks object acts as a class object that you can use to createmany different task types.

• You can also access the StandardTasks object using the Livelink API. Formore information, see the Livelink API Developer’s Reference.

For each task type that you create, you can modify the following features.

Table 4-1: Features Associated With the StandardTasks Object

Feature Description

fSubType Stores a unique integer value that works with the fType feature to identifythe object.

The following objects also contain fSubType features:

• The WFTask object, which is orphaned when you define the WorkflowPainter information

• The WFTask object, which is orphaned when you define the status anddisplay of the task

The values of the fSubType features for the WFTask objects must be the sameas the value of this fSubType feature.

fTaskName Stores the name of the task. By default, this value is displayed when youposition your cursor over the task’s icon in the Workflow Painter.

Note For more information about the Step Icon Palette, see “TheWorkflow Painter,” in Chapter One, “Introduction.”

fType Stores a unique integer value that works with the fSubType feature toidentify the object.

The following objects also contain fType features:

• The WFTask object, which is orphaned when you define the WorkflowPainter information

• The WFTask object, which is orphaned when you define the status anddisplay of the task

The values of the fType features for the WFTask objects must be the same asthe value of this fType feature.

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

Page 48: Live Link

Defining the Task Type’s API Object

Adding New Task Types 33

For each task type that you create, you can modify the following scripts.

Table 4-2: Scripts Associated With the StandardTasks Object

Script Description

GetPainterInfo() Retrieves the text that is displayed as a title below the task iconin the Workflow Painter. The default value is the title of thetask, as specified in the fTaskName feature.

If you do not want to display the title of the task in theWorkflow Painter, you can modify this script.

Note In most cases, you will not modify theGetPainterInfo() script.

ReadyTaskForInitiation() Stores callback scripts and any additional data that is requiredby the task type throughout the execution of a workflow. Thisscript is called when the workflow is initiated.

You can use this script to store the type, subtype, andpermission information for the task type in theMAPTASK_USERDATA column of the WMapTask table. Thetype and subtype information must be present in theWMapTask table so that Livelink can distinguish betweendifferent types of tasks.

SetTaskDefaults() Stores the default data that must be present before Livelink canrecognize the task type (that is, the type and subtype). Thisscript also stores the title, performer ID, and any otherinformation that you want to appear as default values on theStep Definition page when a task of this type is edited inLivelink.

You can also use this script to store task information that mustbe present if the creator of a workflow map does not edit thetask on the Step Definition page before the workflow isinitiated.

SetTaskRecFromMap() Converts a workflow task that has been prepared for initiationto a workflow map definition task. This script transfers datafrom the WAPIMAPTASK to the correct fields in the mapdefinition task to convert the workflow task back to a formatthat the Workflow Painter recognizes.

Note The WAPIMAPTASK data type is used to define theobject handle of a workflow map task that isstored in the WAPI database. It is used in WAPIfunctions for all task manipulation operations.

This script is called when a workflow manager attempts tomodify a workflow that has already been initiated. It undoes thechanges that were made by the ReadyTaskForInitiation()script (because the ReadyTaskForInitiation() script is calledagain when the workflow manager saves their changes and theworkflow process is resumed).

Page 49: Live Link

Defining the Workflow Painter Information

34 Developer’s Guide for Extending Livelink Workflow

Defining the Workflow Painter InformationAfter you define the task type's API object, you must provide the Workflow Painter withthe information it requires to display the task and manipulate the data provided by thecreator of the workflow map. You define the Workflow Painter information using theWFTask 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 Description

fSubType Stores a unique integer that works with the fType feature to identify thetask type.

The following objects also contain fSubType features:

• The StandardTasks object, which is orphaned when you define theAPI object for the task type

• The WFTask object, which is orphaned when you define the statusand display of the task

The values of the fSubType features for the StandardTasks object andthe WFTask object must be the same as the value of this fSubTypefeature.

fType Stores a unique integer that works with the fSubType feature to identifythe task type.

The following objects also contain fType features:

• The StandardTasks object, which is orphaned when you define theAPI object for the task

• The WFTask object, which is orphaned when you define the statusand display of the task

The values of the fType features for the StandardTasks object and theWFTask object must be the same as the value of this fType feature.

Note When adding task types, Open Text recommends setting the fSubTypevalues 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 Description

GetMapData() Tells Livelink what to display when the creator of a workflow map editsthis type of task. This script identifies the location and name of theHTML file to display for the Step Definition page for this task type.

Note You can maintain consistency with other Livelink taskpages, by using the commonedittask.html file. This file islocated in the /webwfp_8_1_x/html directory.

Page 50: Live Link

Defining the Workflow Painter Information

Adding New Task Types 35

Table 4-4: Scripts Associated With the WFTask Object

Script DescriptionPutMapData() 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 Painterinformation for a task type, you must create the HTML file for this task’s Step Definitionpage. You access the Step Definition page in Livelink by right-clicking a task in theWorkflow Painter, and then clicking Edit or by double-clicking the task in the WorkflowPainter.

Page 51: Live Link

Defining the Status and Display of the Task

36 Developer’s Guide for Extending Livelink Workflow

Defining the Status and Display of the TaskAfter you define the API object and Workflow Painter information for the task type, youmust define the information that handles the task when a workflow participant is workingon it and when it is displayed on the Detailed Status page in Livelink. You define thisinformation 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 Description

fPaletteTask Stores a Boolean value. If the value of this feature is set to TRUE, an iconrepresenting the task type can be displayed on the Step Icon Palette in theWorkflow Painter. If the value of this feature is set to FALSE, an iconrepresenting the task type cannot be displayed on the Step Icon Palette inthe Workflow Painter.

Note For more information about the Step Icon Palette, see “TheWorkflow Painter,” page 4.

fSubType Stores a unique integer that works with the fType feature to identify theobject.

The following objects also contain fSubType features:

• The StandardTasks object, which is orphaned when you define the APIobject for the task type

• The WFTask object, which is orphaned when you define the WorkflowPainter information for the task type

The values of the fSubType features for the StandardTasks object and theWFTask object must be the same as the value of this fSubType feature.

fTaskGif Stores the name of the image that you want to represent the task on theStep Icon Palette in the Workflow Painter. This image must be stored inyour module’s /support directory (for example,c:/opentext/module/custmod_1_0_0/support) and in the Livelink /supportdirectory (for example, c:/opentext/support).

fType Stores a unique integer that works with the fSubType feature to identify theobject.

The following objects also contain fType features:

• The StandardTasks object, which is orphaned when you define the APIobject for the task type

• The WFTask object, which is orphaned when you define the WorkflowPainter information for the task type

The values of the fType features for the StandardTasks object and theWFTask object must be the same as the value of this fType feature.

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

Page 52: Live Link

Defining the Status and Display of the Task

Adding New Task Types 37

For each task type that you create, you can modify the following scripts.

Table 4-6: Scripts Associated With the WFTask Object

Script Description

GetDisplayPerformerInfo() Examines the workflow and determines if the task has beenassigned to a workflow participant. If it has, this script retrievesthe name and ID of the workflow participant to whom the taskhas been assigned. This information is displayed when theworkflow participant attempts to reassign the task in Livelink.

GetPainterInfo() Defines the information (for example, the name, type, subtype,icon, or module) that the Workflow Painter needs to knowabout the task type. This includes all of the information that theWorkflow Painter requires to perform operations on the task.

GetPainterMenu() Returns a list of Assocs that define the menu commands thatappear when you right-click this task type’s icon in theWorkflow Painter.

You can set the Boolean value of the viewonly variable to TRUE ifyou want to display a standard set of menu commands that donot let the creator of a workflow map edit the task type in theWorkflow Painter. You can set the Boolean value of theviewonly variable to FALSE if you want to display a standard setof menu commands that let the creator of a workflow map editthe task type in the Workflow Painter. Whether you set theBoolean value of the viewonly variable to TRUE or FALSE, youcan use this script to override the menu commands that aredisplayed in the Livelink interface as needed.

GetStatusDisplay() Retrieves the information that is displayed when a workflowparticipant clicks the task name on the Step List tab or double-clicks the task icon on the Map View tab of the Detailed Statuspage.

This script returns Undefined (indicating that there is noinformation of this type relevant to the active workflow) or itreturns an Assoc. If the script returns an Assoc, the fields in theAssoc must contain the information that the HTML file(specified by retval.HTMLFile) needs to be displayed inLivelink. The Assoc contains OK if the data needed for display isretrieved; otherwise, it contains a string named ErrMsg.

GetTaskEditData() Retrieves the information that is displayed when a workflowparticipant clicks the task name on the Tasks page in theirPersonal Workspace.

This script returns Undefined (indicating that there is noinformation of this type relevant to the active workflow) or itreturns an Assoc. If the function returns an Assoc, the fields inthe Assoc must contain the information that the HTML file(specified by retval.HTMLFile) needs to be displayed inLivelink. The Assoc contains OK if the data needed for display isretrieved; otherwise, it contains a string named ErrMsg.

Page 53: Live Link

Defining the Status and Display of the Task

38 Developer’s Guide for Extending Livelink Workflow

Table 4-6: Scripts Associated With the WFTask Object

Script Description

NewPerformer() Updates task information if the task is reassigned in Livelink. Forexample, if the performer’s name is also the name of the task inthe Workflow Painter, this script updates the names. This scriptis called when this type of task is reassigned in Livelink so that allperformer-dependent information is updated.

Depending on the complexity of the task types that you add to Livelink, you may have toprovide additional functionality. For example, if you create a task type that allows thecreators of workflow maps to attach custom callback scripts to particular workflow eventsthat are associated with that task type, you may need to define the objects responsible forhandling those callback scripts in Livelink. For more information, see “TheWFCustomScriptPkg Object,” page 79.

If you add a complex task type to the custom module that you are creating, you may needto create custom request handlers that perform the operations associated with the tasktype. For example, the form task type (installed with the Livelink Forms™ module) usesrequest handlers to display the data in the form, to retrieve data from the form, and topass the data to the workflow. In this case, the request handlers set up and manipulate allof the form’s data correctly. For information about creating the request handlers used bythe form task type, see “Request Handlers,” page 195.

Page 54: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 39

Example 1: Adding the Custom Display Task TypeThe following example describes how to add a new type of task to Livelink. When thecreator 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 thetask. These callback scripts define the custom operations that can occur when a taskof this type becomes ready or is complete.

• Attach HTML templates to the task. These HTML templates define the interface thatis 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, butcontains two new fields: the Script to run field and the Template to use field. The creatorof a workflow map can choose a callback script to run for the task from the Script to runfield. This callback script can perform any custom operation and can run when the taskbecomes ready or when the task is complete. The creator of a workflow map can alsochoose an HTML template file to display for the task from the Template to use field. TheHTML template defines the interface that is displayed when a workflow participant workson that particular task in Livelink. The creator of a workflow map can create manydifferent tasks of this type, and can choose different callback scripts and HTML templatesfor each, so that each task looks and behaves differently. This lets the creators of workflowmaps customize the workflow interface and operations—without actually changing anycode using the Livelink Builder.

The HTML templates and callback scripts used for this task type are stored in two newfolders in your custom module directory structure: the /templates folder and the /scriptsfolder. The HTML templates that you store in the /templates folder can be selected fromthe Template to use field on a Step Definition page for this type of task. The callbackscripts that you store in the /scripts folder can be selected from the Script to run field on aStep 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 thecustmod OSpace in your custom module, and name it CustomDisplayAPI.

2. In the CustomDisplayAPI object, change the fSubType feature to an Integer/Realtype, and set its value to 1.

The fSubType feature stores a unique integer that works with the fType feature toidentify the object.

3. Ensure that the fType feature is an Integer/Real type, and set its value to 11.

Page 55: Live Link

Example 1: Adding the Custom Display Task Type

40 Developer’s Guide for Extending Livelink Workflow

The fType feature stores a unique integer that works with the fSubType feature toidentify the object.

4. Ensure that the fTaskName feature is a String type, and set its value to CustomDisplay Task.

The fTaskName feature stores the name of the task type as it is displayed in theWorkflow 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 tasktype.

CreateReviewMapRec()

The following code sample describes how to create a script that returns the workflow mapdefinition that is used when a task is sent to a Livelink user or group for review. Thisworkflow 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 expandFlagDynamic userRecordsList fieldsInteger iRecord rRecord newTaskString fieldName

Integer taskID = 1Point pos = Point( 100, 50 )

//Create a generic map record that contains all of the workflow//map information.

Record mapRec = $WFMain.WFMapPkg.CreateMapRec()

Page 56: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 41

//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.Memberselse

userRecords = { user }end

elseuserRecords = { user }

end

if ( Length( userRecords ) < 1 )newTask = user

elsenewTask = 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' }

Page 57: Live Link

Example 1: Adding the Custom Display Task Type

42 Developer’s Guide for Extending Livelink Workflow

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.TASKSif ( taskID > 1 )

$WFMain.WFMapPkg.AddLinkRecord( \mapRec.TASKS, \mapRec.LINKS, \mapRec.TASKS[ 1 ], \mapRec.TASKS[ taskID ], \0 )

endtaskID += 1

end

//Add the work package from the main workflow to the sub-//workflow.

for r in packagesRecArray.AddRecord( mapRec.WORK_PACKAGES, \RecArray.GetRecord( r ) )

endreturn( mapRec )

end

Function Record AddNewTask( \Object prgCtx, \Record mapRec , \Object taskType, \Dynamic context, \Point iconPos )

Dynamic valList dataRecord taskRec

//Add a new Record to the tasks RecArray.

Page 58: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 43

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 retValif ( IsDefined( context ) )

retVal = context.IDendreturn( retVal )

end

ReadyTaskForInitiation()

The following code sample describes how to prepare a custom display task for initiationin Livelink. It stores callback scripts and any other additional data that is required by thecustom display task type throughout the execution of a workflow.

Function Boolean ReadyTaskForInitiation( \Object prgCtx, \Record r, \RecArray workPkg )

Assoc expandInfoList cbInfoList prevCBsRecArray user

Boolean initiatorStep = FalseBoolean success = TrueObject const = $WFMain.WFConstObject uSession = prgCtx.USession()Record userData = $WFMain.WFMapPkg.CreateTaskUserDataRec()userData.TYPE = r.TYPEuserData.SUBTYPE = r.SUBTYPEuserData.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.

Page 59: Live Link

Example 1: Adding the Custom Display Task Type

44 Developer’s Guide for Extending Livelink Workflow

if ( IsDefined( r.DONECB ) )cbInfo = { @r.DONECB, { const.kCBSetTaskDoneData, Undefined } }

elsecbInfo = { { 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.READYCBprevCBs = ( IsDefined( prevCBs ) ) ? prevCBs : {}prevCBs = { @prevCBs, { 500, r.EXATTS.CustTaskScript } }r.READYCB = prevCBs

endendr.DONECB = cbInfor.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.PERFORMERCBprevCBs = ( IsDefined( prevCBs ) ) ? prevCBs : {}r.PERFORMERCB = { { const.kCBSetGrpStepPerformer, \r.PERFORMERID }, @prevCBs }

endend

//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.TYPEexpandInfo.SubType = r.SUBTYPEexpandInfo.Flag = r.EXATTS.GroupFlags

Page 60: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 45

r.SUBMAPIDCB = { { const.kCBExpandGroup, expandInfo } }end

endreturn( success )

end

SetTaskDefaults()

The following code sample describes how to specify the default information required byLivelink to recognize the custom display task type. This is also where you specify data thatmust be present if the creator of the workflow map does not edit the task before theworkflow 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 = .fTypetaskRec.SUBTYPE = .fSubTypetaskRec.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

elsetaskRec.TITLE = context.NAMEtaskRec.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

Page 61: Live Link

Example 1: Adding the Custom Display Task Type

46 Developer’s Guide for Extending Livelink Workflow

SetTaskRecFromMapTask()

The following code sample describes how to convert a custom display task that has beenprepared for initiation to a workflow map definition.

Function Void SetTaskRecFromMapTask( \WAPIMAPTASK task, \Record r, \Record taskData )

List cbInfo

Dynamic data = task.pUserDataObject const = $WFMain.WFConstInteger groupFlag = const.kWFGroupStandard

//Convert the values stored in the WAPIMAPTASK to the//corresponding fields in the map definition task.

r.TYPE = data.TYPEr.SUBTYPE = data.SUBTYPEr.USERFLAGS = data.PERMFLAGSr.SUBMAPID = task.pSubMapIDr.PERFORMERID = taskData.WORK.SUBWORKTASK_PERFORMERIDr.READYCB = task.pReadyCB

//Remove the Done callback script.

r.DONECB = task.pDoneCBr.DONECB = $WFMain.WFMapPkg.RemoveCBInfoType( r.DONECB, \const.kCBSetTaskDoneData )r.KILLCB = task.pKillCB

//Remove the Performer callback script.

r.PERFORMERCB = task.pPerformerCBr.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.pFormr.PAINTER = task.pPainterr.STARTDATE = task.pStartDater.DUEDURATION = task.pDueDurationr.DUEDATE = task.pDueDater.DUETIME = task.pDueTimer.FLAGS = task.pFlagsr.TITLE = taskData.WORK.SUBWORKTASK_TITLEr.DESCRIPTION = task.pDescriptionr.INSTRUCTIONS = task.pInstructionsr.PRIORITY = task.pPriority

Page 62: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 47

//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.pSubMapIdCBfor cbInfo in r.SUBMAPIDCB

if ( cbInfo[ 1 ] == const.kCBExpandGroup )groupFlag = cbInfo[ 2 ].Flagbreak

endend

//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.DONECBif ( cbInfo[ 1 ] == 500 )

r.EXATTS.CustTaskScript = cbInfo[ 2 ]r.DONECB = $WFMain.WFMapPkg.RemoveCBInfoType( r.DONECB, \500 )break

endend

for cbInfo in r.READYCBif ( cbInfo[ 1 ] == 500 )

r.EXATTS.CustTaskScript = cbInfo[ 2 ]r.READYCB = $WFMain.WFMapPkg.RemoveCBInfoType( \r.READYCB, 500 )break

endend

end

Page 63: Live Link

Example 1: Adding the Custom Display Task Type

48 Developer’s Guide for Extending Livelink Workflow

The CustomDisplayPaint Object

After you define the API object for the task type that you are creating, you must providethe Workflow Painter with the information it requires to display the task and handle thedata that the performer of the task provides.

To define the Workflow Painter information:

1. Orphan WebWFP:WebWFPRoot:WFTask in the custmod OSpace in your custommodule, and name it CustomDisplayPaint.

2. In the CustomDisplayPaint object, change the fSubType feature to an Integer/Realtype, and set it to 1.

The fSubType feature stores a unique integer that works with the fType feature toidentify the object. It must match the value specified for the fSubType feature in theCustomDisplayAPI 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 toidentify the object. It must match the value specified for the fType feature in theCustomDisplayAPI 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 module’s /htmldirectory (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 type’s Workflow Painterinformation. Now you must provide the code required to customize the information forthe custom display task type.

GetMapData()

The following code sample describes how to display the Step Definition page for this tasktype 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 )

Page 64: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 49

Assoc aAssoc paneDataAssoc performerInfoAssoc retValAssoc tabPaneInfoDynamic tmpInteger whichTabList tabListObject objRecord pString label

Boolean knownUser = FalseInteger i = 1Record taskInfo = mapRec.TASKS[ taskID ]String gif = 'guy.gif'String name = [WebWFP_HTMLLabel.User]String 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 = mapIDretVal.Data.TaskID = taskIDretVal.Data.NextURL = request.NextURLretVal.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 = objNametmp.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

Page 65: Live Link

Example 1: Adding the Custom Display Task Type

50 Developer’s Guide for Extending Livelink Workflow

//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 = taskInfoa.MapID = mapIDa.TaskID = taskID - 1a.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 = Truename = tmp[ 1 ].NAMEif ( tmp[ 1 ].TYPE != UAPI.USER )

gif = '2-guys.gif'end

endend

endif ( gif == '2-guys.gif' )

a.Gif = '16group.gif'else

a.Gif = '16user.gif'end

performerInfo.Name = nameperformerInfo.Gif = gifperformerInfo.KnownUser = knownUserperformerInfo.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.

Page 66: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 51

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 XLATEtmp.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_PACKAGESobj = $WebWFP.WFPackageSubsystem.GetItem( { p.TYPE, \p.SUBTYPE } )

if ( IsDefined( obj ) && IsDefined( p.USERDATA ) )a = obj.GetTabInfo( prgCtx, request, p.USERDATA, i )a.Active = FALSEif IsDefined( a.HelpKey )

a.HelpKey = objName + "." + a.HelpKeyelse

a.HelpKey = objNameend

paneData = obj.GetMapData( prgCtx, taskInfo, p.USERDATA )

if ( IsDefined( paneData ) )i += 1

tabPaneInfo.TabList = { @tabPaneInfo.TabList, a }tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, paneData }

endend

end

i = Length( tabPaneInfo.TabList ) + 1

Page 67: Live Link

Example 1: Adding the Custom Display Task Type

52 Developer’s Guide for Extending Livelink Workflow

//List the callback scripts that fire, if applicable.

AssoceventInfoList 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 = eventseventInfo.FieldNames = fieldseventInfo = $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 = FALSEtabPaneInfo.TabList = { @tabPaneInfo.TabList, tmp }a = Assoc.CreateAssoc()a.EventInfo = eventInfoa.DataRec = taskInfotmp = Assoc.CreateAssoc()tmp.ModuleName = 'webwfp'tmp.HTMLFile = 't_events.html'tmp.Data = atabPaneInfo.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 = 1end

tabPaneInfo.TabList[ whichTab ].Active = TrueretVal.Data.TabInfo = tabPaneInforetVal.Data.Tab = whichTabreturn( retVal )

end

PutMapData()

The following code sample describes how to save the data that the creator of a workflowmap enters on the Custom Display Step Definition page.

function assoc PutMapData( \Object prgCtx, \Record mapRec, \Record taskInfo, \Record r )

Page 68: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 53

Assoc paneDataAssoc retValInteger defaultDispoInteger flagsInteger iInteger permAndDispositionFlagsList dispositionsList emptyDisposObject objReal timeRecord pInteger count = 0retVal.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.RunScriptelse

taskInfo.ExAtts.CustTaskScript = Undefinedend

//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.CustTaskTemplateelse

taskInfo.CustomData.CustTaskTemplate = Undefinedend

Page 69: Live Link

Example 1: Adding the Custom Display Task Type

54 Developer’s Guide for Extending Livelink Workflow

//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 inDaysretVal.ErrMsg = \[WebWork_ErrMsg.DurationMustBeANumberOfDays]

elseretVal.ErrMsg = \[WebWork_ErrMsg.DurationMustBeANumberOfHours]

endelse

taskInfo.DueDuration = \$LLIAPI.FormatPkg.ConvertToSeconds( inDays, time )

endelse

taskInfo.DueDuration = Undefinedend

end

//Save the group options.

if RecArray.IsColumn( r, "GroupFlags" )taskInfo.EXATTS.GroupFlags = Str.StringToInteger( \r.GROUPFLAGS )

endelseif ( r.PaneIndex == 2 )

//Save the disposition types.

for i = 1 to 5if ( 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 emptyDisposif ( i < defaultDispo )

count += 1

Page 70: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 55

endend

defaultDispo -= count

If ( Length( dispositions ) < defaultDispo )retVal.OK = FALSEretVal.ErrMsg = \[WebWork_ErrMsg.DefaultDispositionMustHaveAName]

endelse

defaultDispo = 1end

if RecArray.IsColumn( r, "RequireDisposition" )flags |= $WFPDisposition

elsedispositions = { '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_PACKAGESobj = $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

elsei += 1

Page 71: Live Link

Example 1: Adding the Custom Display Task Type

56 Developer’s Guide for Extending Livelink Workflow

endend

endend

//Save any callback data.

$WEBWFP.WFContentManager.StoreCallbackData( taskInfo, \r )

endreturn retVal

end

t_user.html

The following code sample describes how to design the Web page that is displayed whenthe creator of a workflow map edits a custom display task in the Workflow Painter. Youedit a custom display task on the Custom Display Step Definition page, which is accessedby double-clicking the task’s icon or by right-clicking the task's icon, and then clickingEdit.

;;webscript t_user( Assoc data )<!-- File: custmod/t_user.html -->

;;oscript{Integer iList durationInfoString checkedString dueDurationDynamic 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] }

Page 72: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 57

;;}

//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>

Page 73: Live Link

Example 1: Adding the Custom Display Task Type

58 Developer’s Guide for Extending Livelink Workflow

<TD bgcolor="#CCCCCC" NOWRAP><FONTFACE="`[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 torun:&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.

Page 74: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 59

<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 touse:&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] )

elsedurationInfo = { TRUE, 0 }

end;;}<TR>

Page 75: Live Link

Example 1: Adding the Custom Display Task Type

60 Developer’s Guide for Extending Livelink Workflow

<TD bgcolor="#CCCCCC" NOWRAP><FONTFACE="`[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><FONTFACE="`[WebDsp_Font.SansSerif]`"size="2">&nbsp;`[WebDoc_HTMLLabel.Action_]`&nbsp;</FONT></TD>

<TD><INPUT TYPE="Submit"

VALUE="`[WebWFP_HTMLLabel.AddToWorkflowDefinitionButtonLabel]`"></TD>

</TR></TABLE>

;;end

Page 76: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 61

The CustomDisplayWork object

After you define the API object and Workflow Painter information for the custom displaytask type, you must define the information that handles the task when a workflowparticipant is working on it and when it is displayed on the Detailed Status page inLivelink.

To define the information that handles the task and displays it on the Detailed Statuspage:

1. Orphan WebWork:WebWork Root:WFTask in the custmod OSpace in your custommodule, 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 toidentify the object. It must match the value specified for the fSubType feature in theCustomDisplayAPI 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 toidentify the object. It must match the value specified for the fType feature in theCustomDisplayAPI 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 customdisplay 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 (forexample, c:/opentext/webwork).

7. Create a 16x16 pixel version of the custtask.gif file, name it 16user.gif, and copy the16user.gif file to the support directory in your custom module. This 16user.gif fileidentifies 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.

Page 77: Live Link

Example 1: Adding the Custom Display Task Type

62 Developer’s Guide for Extending Livelink Workflow

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 module’s /htmldirectory (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 mustprovide 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 workflowparticipant to which the task is assigned.

Function Dynamic GetDisplayPerformerInfo( \Object prgCtx, \Record taskRec )

Dynamic performerDynamic retValInteger performerIDObject 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_PERFORMERIDelse

performerID = Undefinedend

//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 ].IDretVal.Name = performer[ 1 ].NAME

Page 78: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 63

endelse

retVal = [WebWork_Label.User]endreturn( retVal )

end

GetPainterInfo()

The following code sample describes how to define the information that the WorkflowPainter needs to know about the custom display task type.

Function Assoc GetPainterInfo( \Object prgCtx, \Record task = Undefined )

Assoc infoAssoc linkDataAssoc retVal

String gif = .fTaskGifString 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.Titlegif = 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'

Page 79: Live Link

Example 1: Adding the Custom Display Task Type

64 Developer’s Guide for Extending Livelink Workflow

//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 appearwhen 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 )

Page 80: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 65

AssocaAssocbAssoccAssocdAssoceAssocf

//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.

Page 81: Live Link

Example 1: Adding the Custom Display Task Type

66 Developer’s Guide for Extending Livelink Workflow

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).

elseAssocaa.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 }endreturn retval

end

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

GetStatusDisplay()

The following code sample describes how to retrieve the information that is displayedwhen a workflow participant clicks the task name on the Step List page. The Step List pageis 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

Page 82: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 67

Assoc retValAssoc tabPaneInfoAssoc tmpInteger whichTabRecArray auditInfoRecArray dispositionRecArray performerString title

Record mapRec = context.MAP_PAINTERRecord 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 XLATEtmp.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 )

Page 83: Live Link

Example 1: Adding the Custom Display Task Type

68 Developer’s Guide for Extending Livelink Workflow

if ( !IsError( performer ) )a.PerformerName = performer[ 1 ].NAME

if ( performer[ 1 ].TYPE != UAPI.USER )a.Gif = '16group.gif'

endend

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

endend

//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 = 1end

tabPaneInfo.TabList[ whichTab ].Active = TRUEend

Page 84: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 69

//Set up an Assoc that returns all of the data required by//Livelink to draw the Step Detail page.

retVal.OK = tmp.OKretVal.ErrMsg = tmp.ErrMsgretVal.HTMLFile = "wwt.html"retVal.ModuleName = 'webwork'retVal.Tab = whichTabretVal.TabInfo = tabPaneInforetVal.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 displayedwhen a workflow participant clicks the task name on the Tasks page in their PersonalWorkspace.

function Assoc GetTaskEditData( \Object prgCtx, \Record taskInfo, \Record r )

Assoc aAssoc dataAssoc paneDataAssoc retValAssoc tabPaneInfoAssoc tmpDynamic statusInteger whichTabList tabListObject objRecArray packagesRecord pWAPIWORK work

Boolean ok = trueBoolean groupStep = FalseInteger flags = WAPI.STARTTASK_FLAG_REEXECUTEInteger i = 1

tmp = GetCustomData( taskinfo.subworktask_customdata )

if ( IsDefined( tmp ) && IsDefined( tmp.CustTaskTemplate ) )data.HTMLFile = 'redirect.html'data.CustTaskTemplate = tmp.CustTaskTemplatedata.ModuleName = 'custmod'

elsewhichTab = ( RecArray.IsColumn( r, 'PaneIndex' ) ) ? \Str.StringToInteger( r.PaneIndex ) : Undefined

Page 85: Live Link

Example 1: Adding the Custom Display Task Type

70 Developer’s Guide for Extending Livelink Workflow

//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 = Trueend

//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 = taskInfoa.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

Page 86: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 71

//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 += 1for 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 = FALSEpaneData = obj.GetData( \prgCtx, taskInfo, p.USERDATA, \r )if ( IsDefined( paneData ) )

i += 1paneData.IsEditable = \TruetabPaneInfo.TabList = { \@tabPaneInfo.TabList, a }tabPaneInfo.PaneList = \{@tabPaneInfo.PaneList, \paneData }

endend

end

//If the data was not retrieved correctly//(OK=FALSE), return an error message.

elseok = FalseretVal.ApiError = workretVal.ErrMsg = \[Web_ErrMsg2.CouldNotAccessWorkpackage]

end

//If the data was not retrieved correctly//(OK=FALSE), return an error message.

elseok = FalseretVal.ApiError = workretVal.ErrMsg = [Web_ErrMsg2.CouldNotAccessWork]

Page 87: Live Link

Example 1: Adding the Custom Display Task Type

72 Developer’s Guide for Extending Livelink Workflow

endWAPI.FreeWork( work )

elseok = FalseretVal.ApiError = workretVal.ErrMsg = [Web_ErrMsg2.CouldNotAllocwork]

endendif ( ok )

if ( ( whichTab < 2 ) || ( whichTab > Length( \tabPaneInfo.TabList ) ) )

whichTab = 1endtabPaneInfo.TabList[ whichTab ].Active = True

enddata.TabInfo = tabPaneInfodata.Tab = whichTab

elseok = FalseretVal.ErrMsg = \[WebWork_ErrMsg.TheArgumentPaneIndexIsRequired]

endendretVal.OK = okretVal.Data = datareturn( retVal )

end

Function Dynamic GetCustomData( Dynamic val )Dynamic retVal = valwhile ( Type( retVal ) == StringType )

retVal = Str.StringToValue( retVal )endreturn( retVal )

end

GetTaskGif()

The following code sample describes how to define the image used to represent the task inthe Workflow Painter. This image changes depending on the performer of the task.

Function String GetTaskGif( \Object prgCtx, \Record taskRec )

String retValInteger id = .GetTaskTypeObj().GetTaskPerformerId( taskRec )if ( IsDefined( id ) )

retVal = .GetDisplayInfo( prgCtx, taskRec ).Gifelse

retVal = .fTaskGifendreturn( retVal )

end

Page 88: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 73

NewPerformer()

The following code sample describes how to update task information if the task isreassigned in Livelink.

Function Assoc NewPerformer( \Object prgCtx, \Record taskRec, \Integer newID )

Assoc retValDynamic userInfoString name

//Set some default variables and locate the initiator task object//in the WebWork OSpace.

Boolean success = TrueInteger painterInfo = 0Integer performerID = 0Object 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

elseuserInfo = UAPI.GetByID( prgCtx.USession().fSession, \taskRec.PERFORMERID )if ( !IsError( userInfo ) )

userInfo = userInfo[ 1 ]name = userInfo.NAME

endend

//Determine the default title for the task based on the//new performer ID.

if ( newID == 0 ) // Initiatortitle = initTitle

elseif ( !IsDefined( newID ) ) // Generic usertitle = .GetTaskName()performerID = newID

elseperformerID = newIDpainterInfo = newID

//Determine the user name of the performer, based on their//performer ID.

userInfo = UAPI.GetByID( prgCtx.USession().fSession, newID )

Page 89: Live Link

Example 1: Adding the Custom Display Task Type

74 Developer’s Guide for Extending Livelink Workflow

if ( !IsError( userInfo ) && Length( userInfo ) )userInfo = userInfo[ 1 ]

title = userInfo.NAMEend

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 don’t change it.

if ( success )if ( taskRec.TITLE == name )

taskRec.TITLE = titleend

taskRec.PERFORMERID = performerIDtaskRec.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 youprovide to the reviewers of a task when you submit it for review. These instructions canalso include the duration and some group options.

function assoc PutReviewData( \Object prgCtx, \Record mapRec, \Record taskInfo, \Record r )Assoc retValReal timeretVal.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" )

Page 90: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 75

time = $LLIAPI.FormatPkg.StringToVal( r.Duration, RealType )

if ( Type( time ) != RealType )retVal.OK = FALSE

if inDaysretVal.ErrMsg = \[WebWork_ErrMsg.DurationMustBeANumberOfDays]

elseretVal.ErrMsg = \[WebWork_ErrMsg.DurationMustBeANumberOfHours]

endelse

taskInfo.DueDuration = \$LLIAPI.FormatPkg.ConvertToSeconds( inDays, time )

endelse

taskInfo.DueDuration = Undefinedend

end

//Save the group options.

if RecArray.IsColumn( r, "GroupFlags" )taskInfo.EXATTS.GroupFlags = Str.StringToInteger( \r.GROUPFLAGS )

end

return retValend

ReassignStep()

The following code sample describes how to create a script that lets workflow participantsreassign a task on the Step Detail page.

Function Boolean ReassignStep( \Object prgCtx, \Record taskRec, \Record user, \Record workData, \WAPIWORK work )

Assoc userInfoInteger whereList cbData

Boolean ok = FalseObject uSession = prgCtx.USession()Object wSession = prgCtx.WSession()String 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 ) )

Page 91: Live Link

Example 1: Adding the Custom Display Task Type

76 Developer’s Guide for Extending Livelink Workflow

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 don’t 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 group—not just to//the group member that originally accepted it.

if ( ok )if ( user.TYPE != UAPI.USER )

cbData = { { $WFMain.WFConst.kCBSetGrpStepPerformer, \user.ID } }

elsecbData = 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

endend

end

return( ok )end

Function Boolean UpdatePerformer( \Object prgCtx, \WAPIWORK work, \Record old, \Record new )

Boolean successList info

//Make the WAPI call that reassigns the task.

success = prgCtx.WSession().CheckRetVal( \WAPI.ReassignTask( \

work, \

Page 92: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 77

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.IDend

return( success )end

Function Boolean UpdateStepTitle( \Object prgCtx, \WAPIWORK work, \Record task, \Assoc info, \Record user )

String name

Boolean success = TrueObject 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

endend

return( success )end

Function Boolean UpdateMapTask( \Object prgCtx, \Record taskData, \List cbData = Undefined )

Boolean successWAPIMAPTASK task

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

Page 93: Live Link

Example 1: Adding the Custom Display Task Type

78 Developer’s Guide for Extending Livelink Workflow

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 workflowmap 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

Page 94: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 79

The WFCustomScriptPkg Object

After you customize the objects necessary to create the custom display task type, you mustorphan the WFCustomScriptPkg object, which is responsible for executing the callbackscripts that can be used with this task type. You can find this object inWFMain:WFRoot:WFCustomScriptPkg.

To orphan the WFCustomScriptPkg Object:

1. Orphan WFMain:WFRoot:WFCustomScriptPkg in the custmod OSpace in yourcustom 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, andset its value to the object reference number of the UtilityPkg object that youcreated 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 mustcustomize this object for the custom display task type.

Page 95: Live Link

Example 1: Adding the Custom Display Task Type

80 Developer’s Guide for Extending Livelink Workflow

CBExecute()

The following code sample describes how to handle the callback scripts that can be usedwith 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 dataDynamic retValBoolean 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 ] )

enddefault

handled = Falseend

enddata.Handled = handleddata.retVal = retValreturn( data )

end

ExecuteCustTaskScript()

The following code sample describes how to create a script that loads information aboutthe custom display task and uses that information to locate and run the appropriatecallback script.

Function Boolean ExecuteCustTaskScript( \Object prgCtx, \WAPIWORK work, \Integer workID, \Integer subWorkID, \Integer taskID, \String scriptName )Dynamic taskInfoBoolean success = FalseObject uapiCtx = prgCtx.Usession()

//Load information about the custom display task.

taskInfo = prgCtx.WSession().LoadTaskStatus( workID, subWorkID, \

Page 96: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 81

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 )

endreturn( success )

end

ExecuteScript()

The following code sample describes how to create a script that locates and runs thecallback script that is associated with this custom display task.

Function Boolean ExecuteScript( \Object prgCtx, \WAPIWORK work, \Dynamic taskInfo, \String scriptName )

Boolean success = TrueDynamic err = Undefined

String moduleDir = $Custmod.custtaskmodule.PathPrefix()String scriptsDir = moduleDir + "scripts" + File.Separator()String scriptFile = scriptsDir + scriptName

//Convert the HTML files that have OScript in them to pure HTML.

Dynamic 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

elseecho( "Error during $WebLingo.WebScript.RunFile =", result )success = false

endreturn( success )

end

Page 97: Live Link

Example 1: Adding the Custom Display Task Type

82 Developer’s Guide for Extending Livelink Workflow

ListScripts()

The following code sample describes how to create a script that returns a list of scriptsthat are stored in the /scripts directory of the custmod module (for example,c:/opentext/module/custmod_8_1_x/scripts).

function List ListScripts()String scriptPathString 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 vFileListListretList = { @retList, File.GetName( scriptPath ) }

endendreturn( retList )

end

ListTemplates()

The following code sample describes how to create a script that returns a list of HTMLtemplates 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 templatePathString 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 vFileListListretList = { @retList, File.GetName( templatePath ) }

endendreturn( retList )

end

Page 98: Live Link

Example 1: Adding the Custom Display Task Type

Adding New Task Types 83

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 tothe /scripts folder that you created in step 1.

The scripts appear in the Script to run list on the Custom Display Step Definitionpage in the Workflow Painter.

4. Add the templates that you want to make available to the Custom Display task typeto the /templates folder that you created in step 2.

The templates appear in the Template to use list on the Custom Display StepDefinition page in the Workflow Painter.

Page 99: Live Link

Example 1: Adding the Custom Display Task Type

84 Developer’s Guide for Extending Livelink Workflow

Page 100: Live Link

85

Chapter Five

Adding New Data Types

You add new data types to Livelink if you want to include new types of information in thework packages that accompany your workflows. By default, work packages can containthree data types: attachments, attributes, and comments; however, you can create a datatype 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 Packagessection of a workflow map’s Properties page.

Adding new data types to Livelink also lets you customize the Livelink interface. Forexample, if the work packages that you include with your workflows usually contain manyattributes, you can create a data type that creates the attributes and groups them on tabsin the Livelink interface. This makes it easy for workflow participants to locate and use theattributes in the work package.

You add new data types to Livelink by orphaning three objects:WFMain:WFRoot:WFObjectTypes:WFDataTypes, WebWFP:WebWFPRoot:WFPackage, andWebWork:WebWorkRoot:WFPackage. The WFMain:WFRoot:WFObjectTypes:WFDataTypesobject is the API object for the task type. The WebWFP:WebWFPRoot:WFPackage objectcontrols the information required by the Workflow Painter to add the data type to aworkflow map. The WebWork:WebWorkRoot:WFPackage object controls the operation ofthe data type in executing workflows.

In this chapter, you will learn how to:

• Define the data type’s API object

• Define the data type’s Workflow Painter information

• Define the information required to route the data type through a workflow

• Add a new data type to your custom module

Page 101: Live Link

Defining the Data Type’s API Object

86 Developer’s Guide for Extending Livelink Workflow

Defining the Data Type’s API ObjectYou 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 datatype. You can find the WFDataTypes object in WFMain:WFRoot:WFObjectTypes.

Note The WFDataTypes object acts as a class object that you can use to createmany different data types. Create a child of the WFDataTypes object foreach 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 Description

fDataName Stores the name of the custom tab that is displayed in the Livelink interfacewhen 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 inthe Workflow Painter.

• Workflow participants access the work package of the executing workflowfrom their Personal Workspaces.

fSubType Stores a unique integer that works with the fType feature to identify theobject.

The following objects also contain fSubType features:

• The WFPackage object, which is orphaned when you define the WorkflowPainter information

• The WFPackage object, which is orphaned when you define theinformation that controls the operation of the data type in executingworkflows

The values of the fSubType features for the WFPackage objects must matchthe value of this fSubType feature.

fType Stores a unique integer that works with the fSubType feature to identify theobject.

The following objects also contain fType features:

• The WFPackage object, which is orphaned when you define the WorkflowPainter information

• The WFPackage object, which is orphaned when you define theinformation that controls the operation of the data type in executingworkflows

The values of the fType features for the WFPackage objects must match thevalue of this fType feature.

Page 102: Live Link

Defining the Data Type’s API Object

Adding New Data Types 87

Note When adding data types, Open Text recommends setting the fSubTypevalues 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 Description

CheckTaskDone() Verifies that a workflow participant has provided all of the datatype’s required information before their input is saved and theworkflow is routed to the next workflow participant. For example,if you create a data type that groups workflow attributes and youindicate that entries are required for some of those attributes, thisscript verifies that a workflow participant has providedinformation for those required attributes before the workflow isrouted to the next workflow participant.

This script returns a list that can contain two items: a Booleanvalue and an error message. The Boolean value specifies whetherthe required data has been provided. If the required data has notbeen provided, the Boolean value is set to FALSE and an errormessage identifying the required values that must be specifiedbefore Livelink can route the work package to the next workflowparticipant is returned.

CreateNewInstance() Creates a new instance of the data type when the data type isattached to a workflow map in the Workflow Painter.

This script is called when the creator of a workflow map selects thecustom data type check box in the Packages section of a workflowmap’s Properties page, and then clicks the Add to WorkflowDefinition button.

CreateWorkData() Adds the data type to the work package of the specified workflow.

This script is called when the initiator of a workflow clicks theworkflow map’s name in Livelink to begin the initiation process.

DeleteWorkData() 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.

LoadStartTaskWorkData() Returns the information about the data type that must be availableto the initiator of a workflow when they work on the Start task.

This script is called when the initiator of a workflow clicks theworkflow map’s name in Livelink to begin the initiation process.The initiator of a workflow to which a custom data type has beenattached can click the custom data type tab and provideinformation for the data type before starting the workflow process.Because the initiator is the first workflow participant to providedata type information, this script does not have to retrieve anyprevious values that may have been stored for the data type.

When the initiator clicks the Initiate button to start the workflowprocess, their input is saved and routed with the work package tothe next workflow participant.

Page 103: Live Link

Defining the Data Type’s API Object

88 Developer’s Guide for Extending Livelink Workflow

Table 5-2: Scripts Associated With the WFDataTypes Object

Script DescriptionLoadTaskWorkData() 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 customdata type tab in the work package of an active workflow task. Whena workflow participant clicks the custom data type tab, theinformation that was stored for the data type when the previoustask was completed is retrieved and displayed. This script populatesthe data type’s values with the input of the previous workflowparticipant. It is called for all workflow tasks—except the Start task.

Note For information about displaying data type values forthe Start task, see LoadStartTaskWorkData().

LoadWorkData() Returns information about a data type for the specified workflow.

This script is called when a workflow manager views the detailedstatus of a workflow that contains this data type. It is also called byevent trigger scripts throughout the execution of a workflow.

RemoveWorkData() Removes the data type from the specified workflow.

This script is called when a workflow that contains the data type isdeleted from Livelink. It is also called when a workflow managermodifies an executing workflow by removing the data type.

SaveWorkData() Saves the data type information throughout the execution of aworkflow.

This script is called each time that a workflow participant updatesthe data type information.

SetReviewData() Stores the information about a data type that must be passed to thesub-workflow that is created when a workflow participant sends atask for review.

This information is used by the SetSubWorkData() andSetSubWorkReturnData() scripts.

Note For more information about sending a task forreview, see the Livelink online help.

SetSubWorkData() Passes data type information from a workflow to a sub-workflow.This script is called when a sub-workflow starts or when a task issent for review.

SetSubWorkReturnData() Passes data type information from a sub-workflow back to a mainworkflow. This script is called when a sub-workflow finishes orwhen a task that has been sent for review finishes.

This is the companion script to the SetSubWorkData() script.

Page 104: Live Link

Defining the Data Type’s Workflow Painter Information

Adding New Data Types 89

Defining the Data Type’s Workflow PainterInformation

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

For each data type that you create, you can modify the following features.

Table 5-3: Features Associated With the WFPackage Object

Feature Description

fSubType 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 objectfor the data type

• The WFPackage object, which is orphaned when you define the informationthat controls the operation of the data type in executing workflows

The values of the fSubType features for the WFDataTypes object and the otherWFPackage object must match the value of this fSubType feature.

fType Stores a unique integer that works with the fSubType feature to identify theobject.

The following objects also contain fType features:

• The WFDataTypes object, which is orphaned when you define the API objectfor the data type

• The WFPackage object, which is orphaned when you define the informationthat controls the operation of the data type in executing workflows

The values of the fType features for the WFDataTypes object and the otherWFPackage object must match the value of this fType feature.

Note When adding data types, Open Text recommends setting the fSubTypevalues 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 Description

GetMapData() Specifies the name and location of the HTML file that is displayed to thecreator of the workflow map when they define data type information for tasksin the Workflow Painter.

The HTML file called by this script is displayed when the creator of a workflowmap clicks the custom data type tab on a Step Definition page when definingtasks in the Workflow Painter.

Page 105: Live Link

Defining the Data Type’s Workflow Painter Information

90 Developer’s Guide for Extending Livelink Workflow

Table 5-4: Scripts Associated With the WFPackage Object

Script DescriptionPutMapData() Saves the information specified on the HTML page that is referenced in the

GetMapData() script. This script saves the settings that the creator of aworkflow map specifies when they define the data type information for tasks inthe Workflow Painter.

This script is called when the creator of a workflow map adds the data typeinformation that they specify on the custom data type page to the workflowdefinition (by clicking the Add to Workflow Definition button).

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

Page 106: Live Link

Defining the Data Type’s Web Object

Adding New Data Types 91

Defining the Data Type’s Web ObjectAfter you define the API object and the Workflow Painter information for the data type,you must define the data type’s Web object. The Web object provides the informationnecessary to handle the data type when workflow participants manipulate its data inexecuting workflows. You define this information using the WFPackage object. You canfind this object in WebWork:WebWorkRoot.

Note The WFPackage object acts as a class object that you can use to createmany data types. Create a child of the WFPackage object for each datatype 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 Description

fSubType Stores a unique integer that works with the fType feature to identify theobject.

The following objects also contain fSubType features:

• The WFDataTypes object, which is orphaned when you define the APIobject for the data type

• The WFPackage object, which is orphaned when you define the WorkflowPainter information for the data type

The values of the fSubType features for the WFDataTypes object and the otherWFPackage object must match the value of this fSubType feature.

fType Stores a unique integer that works with the fSubType feature to identify theobject.

The following objects also contain fType features:

• The WFDataTypes object, which is orphaned when you define the APIobject for the data type

• The WFPackage object, which is orphaned when you define the WorkflowPainter information for the data type

The values of the fType features for the WFDataTypes object and the otherWFPackage object must match the value of this fType feature.

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

Page 107: Live Link

Defining the Data Type’s Web Object

92 Developer’s Guide for Extending Livelink Workflow

For each data type that you create, you can modify the following scripts.

Table 5-6: Scripts Associated With the WFPackage Object

Script Description

GetData() Sets up the display of the HTML page that is loaded when the initiator ofa workflow provides data type information for the Start task, when aworkflow participant opens a workflow task in their Tasks list, and whena 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 aLivelink user is working with the data stored by the data type for theworkflow.

GetSubmapData() Specifies the name and location of the HTML file that is displayed to thecreator of a workflow map when they define a sub-workflow task thatcontains the custom data type in a workflow that also contains thecustom data type. This HTML file is displayed when the creator of aworkflow map clicks the custom data type tab on the Sub-Map StepDefinition page. The Sub-Map Step Definition page lets the creator of aworkflow map specify the information about the data type that the mainworkflow passes to a sub-workflow.

This page is also displayed when a workflow participant sends a task forreview.

GetTabInfo() Generates the names of the custom data type tabs that are displayedwhen workflow participants work on their tasks. This script alsogenerates the URLs that determine which HTML files are displayed wheneach tab is active. This script ensures that the correct page is displayedwhen a workflow participant clicks a custom data type tab in the workpackage of an active workflow task.

Note You can access the work package by clicking a task nameon the Tasks page in your Personal Workspace.

PutSubmapData() Saves the data type information that the creator of a workflow mapspecifies on the Sub-Map Step Definition page, when defining data typeinformation for a sub-workflow task.

This script is called when the creator of the workflow map clicks the Addto Workflow Definition button on the custom data type page (accessedby clicking the custom data type tab on the Sub-Map Step Definitionpage in the Workflow Painter).

SaveData() Saves the information that is entered on the HTML page referenced bythe GetData() script. The SaveData() script saves the information thatthe initiator of a workflow specifies for the Start task, the informationthat a workflow participant enters when working on this type of task intheir Task list, and the information specified by the workflow managerwhen viewing the detailed status for this type of task.

Page 108: Live Link

Defining the Data Type’s Web Object

Adding New Data Types 93

In addition to the features and scripts that you modify to define the information thatcontrols the operation of a data type, you must create the HTML files that are displayedwhen:

• The creator of a workflow map defines the data type information for a sub-workflowtask on the Sub-Map Step Definition page.

• A workflow participant accesses the custom data type tab in the work package of anactive workflow task.

Page 109: Live Link

Example 2: Adding a New Data Type

94 Developer’s Guide for Extending Livelink Workflow

Example 2: Adding a New Data TypeThis example describes how to create a new data type called Table Values. The TableValues data type organizes many workflow attributes into logical groups and displaysthem on a series of custom tabs. The Project tab contains workflow attributes that definethe name, code, due date, and priority of a project. The Customer tab contains workflowattributes that define the name and contact information of a customer that is related tothe 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 ofworkflows 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 workflowparticipants to locate and use the workflow attributes for which they must provide valuesthroughout the execution of a workflow.

To use the Table Values data type, the creator of a workflow map must first attach theTable Values data type to the workflow map definition, and then define whether eachworkflow 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 TableValues check box on the workflow map’s Properties page. The workflow attributes aredefined by clicking the Table Values tab on the Step Definition page for each task in theworkflow map, and then clicking Editable, Entry Required, or Read-Only in thecorresponding workflow attribute fields.

Page 110: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 95

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, theworkflow participant responsible for completing that task may input new values for theattribute. If the creator of a workflow map clicks Entry Required in a workflow attributefield, the workflow participant responsible for completing that task must input new valuesfor the attribute. If the creator of a workflow map clicks Read-Only in a workflowattribute field, the workflow participant responsible for completing that task cannot inputnew values for the attribute.

When a workflow map that contains the Table Values data type is initiated in Livelink, theTable Values tab (containing the Project and Customer tabs) is added to the workpackage and routed to each workflow participant when their tasks become ready. Asworkflow participants work on tasks, they specify values for the workflow attribute fieldson the Project and Customer pages. The information that each workflow participantprovides is saved in external database tables and updated as the work package is routedfrom one participant to another. Each workflow participant can view the informationprovided by the workflow participant who completed the preceding task—except theworkflow initiator (because there is no preceding task).

Creating the Database Tables

The values that workflow participants provide for the workflow attribute fields that arelisted on the Project and Customer pages are stored in database tables that reside outsideof the Livelink database. Before you create the Table Values data type, you must createthese external tables.

To create the Cust_Project and Cust_Customers database tables:

1. Orphan DBWizAPI:DBWizAPIRoot:ExecPkg in the custmod OSpace in your custommodule, and name it CustModExecPkg.

2. Orphan DBWizAPI:DBWizAPIRoot:ModuleDBObject in the custmod OSpace in yourcustom module, and name it ModuleDBObject.

3. Create a child of the Custmod Root object (your custom module’s root object), andname it CustModDBScripts.

Page 111: Live Link

Example 2: Adding a New Data Type

96 Developer’s Guide for Extending Livelink Workflow

4. Create three children of the CustModDBScripts object, and name them MSSQL,Oracle, and Sybase, respectively.

The names of these child objects are case-sensitive—type them exactly as they appearin step 4.

5. In the CustModDBScripts object, create three Dynamic features, and name themMSSQL, Oracle, and Sybase, respectively.

6. In the CustModDBScripts object, set the value of the MSSQL feature to the objectreference 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 Oracleobject that you created in step 4.

8. Set the value of the Sybase feature to the object reference number of the Sybaseobject 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 thetables.

• Set the value of the fEnabled feature to TRUE.

• Set the value of the fExecPkg feature to the object reference number of theCustModExecPkg 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 theCustModDBScripts 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. Formore 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 yourcustom module. For more information about the cust_drop() script see“cust_drop(),” page 98.

Page 112: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 97

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 thenreinstall 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 tablesin your database when you install your custom module.

#ifdef COMPILEME

create table %Prefix%Cust_Project(

WorkflowID %type_int% %not_null%,Project_Name %type_char_64% %null%,Priority %type_int% %null%,ID_Code %type_char_32% %null%,DueDate %type_date% %null%

)/

create index Cust_Project_Primaryon %Prefix%Cust_Project ( WorkflowID )

/

create table %Prefix%Cust_Customers(

WorkflowID %type_int% %not_null%,Name %type_char_64% %null%,Addr1 %type_char_32% %null%,Addr2 %type_char_32% %null%,City %type_char_32% %null%,State %type_char_32% %null%,Zip %type_char_32% %null%,Phone %type_char_32% %null%,Fax %type_char_32% %null%

)/

create index Cust_Customers_Primaryon %Prefix%Cust_Customers ( WorkflowID )

/

insert into %Prefix%KInivalues(

'%IniSection%','%IniKeyword%','{8,0,1}' )

/

%commit_command%/#endif

Page 113: Live Link

Example 2: Adding a New Data Type

98 Developer’s Guide for Extending Livelink Workflow

cust_drop()

The following code sample can be used as a prototype for the script that removes thetables 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 canimplement the new data type. This script can be created at any point throughout theimplementation 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.

Page 114: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 99

SetSubPaneIndexArg

The following code sample describes how to create a utility script required by the TableValues data type.

Function String SetSubPaneIndexArg( \String url, \Integer paneIndex )

List findInfoPatChange changePatFind findPatString 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 )

elseurl = Str.Format( '%1&custpaneindex=%2', \

url, \paneIndex )

endreturn( url )

end

Defining the Data Type’s API Object

You begin adding the Table Values data type to Livelink by defining the data type’s APIobject.

To define the data type’s API object:

1. Orphan WFMain:WFRoot:WFObjectTypes:WFDataTypes in the custmod OSpace inyour 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, andset its value to 1.

The fSubType feature stores a unique integer that works with the fType feature toidentify 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 toidentify the object.

5. Ensure that the fDataName feature is a String type, and set its value to TableValues.

Page 115: Live Link

Example 2: Adding a New Data Type

100 Developer’s Guide for Extending Livelink Workflow

The fDataName feature stores the text that is displayed on the custom data type tabin 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 type’s API object. Now you must provide the code required tocustomize the API object for the Table Values data type.

Page 116: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 101

CheckTaskDone()

The following code sample describes how to verify that a workflow participant hasspecified values in all of the required workflow attribute fields on the Project andCustomer tabs before the workflow is routed to the next workflow participant.

Function List CheckTaskDone( \Object prgCtx, \Dynamic data )String msgString nameList 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

Page 117: Live Link

Example 2: Adding a New Data Type

102 Developer’s Guide for Extending Livelink Workflow

CreateNewInstance()

The following code sample describes how to create a new instance of the Table Valuesdata 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 retValString 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 ) = Undefinedend

//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 ) = Undefinedend

retVal.Required = {}retVal.NonEditable = {}return( retVal )

end

Page 118: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 103

CreateWorkData()

The following code sample describes how to add the Table Values data type to the workpackage of the specified workflow.

Function Boolean CreateWorkData( \Object prgCtx, \WAPIWORK work, \List wfInfo, \Dynamic data )

Boolean successRecord 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

endend

return( success )end

DeleteWorkData()

The following code sample describes how to delete any previous data that has been storedfor the custom workflow attributes in the database tables. This script is called by theUpdateTableValues() script when Livelink updates the data in the database tables.

Function Boolean DeleteWorkData( \Object prgCtx, \Integer workID )

Page 119: Live Link

Example 2: Adding a New Data Type

104 Developer’s Guide for Extending Livelink Workflow

Boolean successDynamic sqlResultObject session = prgCtx.WSession()Object 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 )endreturn( success )

end

LoadStartTaskWorkData()

The following code sample describes how to load the information about the data type thatis required to display the Table Values tab to the initiator of the workflow when theywork on the Start task. This script does not load data from the database tables because novalues have previously been specified. Instead, it loads information about each workflowattribute 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 attributefields for each task in a workflow are editable, required, or read-onlywhen they define tasks in the Workflow Painter.

Function Dynamic LoadStartTaskWorkData( \Object prgCtx, \Record taskInfo, \Dynamic data, \Integer holderID )Dynamic taskDataDynamic retVal = datataskData = taskInfo.FORMretVal.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 ) )

Page 120: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 105

retVal.NonEditable = taskData.NONEDITABLE_TABLE_VALUESend

//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

endendreturn( retVal )

end

LoadTableValues()

The following code sample describes how to create a script that loads information aboutthe Table Values data type that is stored in the database tables. This script is called by theLoadTaskWorkData() script to retrieve the previously specified values for the workflowattribute fields on the Project and Customer pages on the Table Values tab in the workpackage for a particular workflow. This ensures that the most recent values are displayedon the Project and Customer pages throughout the execution of the workflow.

Function Assoc LoadTableValues( \Object prgCtx, \Integer workID )Assoc projectAssoc retValDynamic sqlResult

Object session = prgCtx.WSession()Object 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.

Page 121: Live Link

Example 2: Adding a New Data Type

106 Developer’s Guide for Extending Livelink Workflow

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 = projectretVal.NonEditable = {}retVal.Required = {}retVal.WorkflowID = workID

else

//If unsuccessful, return an error.

retVal = sqlResultend

else

//If unsuccessful, return an error.

retVal = sqlResultend

return( retVal )end

LoadTaskWorkData()

The following code sample describes how to load information about the Table Valuesdata type from the database tables. This script calls another script namedLoadTableValues(), which populates the fields on the Project and Customer pages(based on the workflow map’s ID) throughout the execution of a workflow. For moreinformation 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

Page 122: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 107

//Store the read-only values.

if ( IsDefined( taskData ) )if ( IsDefined( taskData.NONEDITABLE_TABLE_VALUES ) )

retVal.NonEditable = taskData.NONEDITABLE_TABLE_VALUESend

//Store the required values.

if ( IsDefined( taskData.REQUIRED_TABLE_VALUES ) )retVal.Required = taskData.REQUIRED_TABLE_VALUES

endend

endreturn( retVal )

end

LoadWorkData()

This code sample describes how to return information about the Table Values data typefor 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 specifiedworkflow.

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 )

endreturn( success )

end

Page 123: Live Link

Example 2: Adding a New Data Type

108 Developer’s Guide for Extending Livelink Workflow

SaveTableValues()

This code sample describes how to create a script that saves the values that a workflowparticipant specifies for the workflow attribute fields on the Customer and Project pages.This script is called by the UpdateTableValues() script after the previous values havebeen 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 successDynamic sqlResult

Assoc fields = data.FieldsAssoc customer = data.Fields.CustomerObject 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, \

Page 124: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 109

customer.Name, \customer.Addr1, \customer.Addr2, \customer.City, \customer.State, \customer.Zip, \customer.Phone, \customer.Fax )

success = session.CheckRetVal( sqlResult )endreturn( success )

end

SaveWorkData()

The following code sample describes how to update the information stored in thedatabase tables for the Table Values data type. This script calls the UpdateTableValues()script which then calls the DeleteWorkData() script (to remove the previousinformation) and the SaveTableValues() script (to add the new information).

Function Boolean SaveWorkData( \Object prgCtx, \WAPIWORK work, \Record taskRec, \Dynamic data )Boolean successRecord rObject session = prgCtx.WSession()success = session.StartTrans()

//Call the UpdateTableValues() script.

if ( success )success = .UpdateTableValues( prgCtx, data )if ( !session.EndTrans( success ) )

success = Falseend

endreturn( success )

end

SetReviewData()

The following code sample describes how to store information about the Table Valuesdata type that must be passed to the sub-workflow that is created when a workflowparticipant 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 retValreturn( retVal )

end

Page 125: Live Link

Example 2: Adding a New Data Type

110 Developer’s Guide for Extending Livelink Workflow

SetSubWorkData()

The following code sample describes how to retrieve the information about the TableValues 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 mainAttribNamesList subAttribNames

Record rBoolean 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 pkgsif ( ( r.TYPE == .fType ) && ( r.SUBTYPE == .fSubType ) )

success = .UpdateSubWorkData( \prgCtx, \r.USERDATA, \returnID, \data )

breakend

endreturn( success )

end

SetSubWorkReturnData()

The following code sample describes how to determine what type of information must bepassed 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 r

Boolean success = True

Page 126: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 111

//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 pkgsif ( ( r.TYPE == .fType ) && ( r.SUBTYPE == .fSubType ) )

success = .UpdateSubWorkData( \prgCtx, \returnWorkID, \r.USERDATA, \data )

breakend

end

return( success )end

UpdateSubWorkData()

The following code sample describes how to create a script that passes data from the TableValues 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 dataAssoc subWorkDataString name

Boolean success = TrueBoolean 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 = Falsebreak

endend

end

Page 127: Live Link

Example 2: Adding a New Data Type

112 Developer’s Guide for Extending Livelink Workflow

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 = subWorkDataend

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

endreturn( success )

end

UpdateTableValues()

The following code sample describes how to create a script that updates the values thatworkflow participants specify for each workflow attribute field on the Project andCustomer pages. This script is called throughout the execution of a workflow to updatethe 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.

Page 128: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 113

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

endreturn( success )

end

Defining the Data Type’s Workflow Painter Information

After you define the API object for the data type that you are creating, you must providethe Workflow Painter with the information it requires to add the data type to a workflowmap.

To define the data type’s Workflow Painter information:

1. Orphan WebWFP:WebWFPRoot:WFPackage in the custmod OSpace in your custommodule, and name it WFPTableValues.

2. In the WFPTableValues object, change the fSubType feature to an Integer/Real typeand set its value to 1.

The fSubType feature stores a unique integer that works with the fType feature toidentify 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 toidentify 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 type’s Workflow Painterinformation. Now you must provide the code required to customize the object for theTable Values data type.

Page 129: Live Link

Example 2: Adding a New Data Type

114 Developer’s Guide for Extending Livelink Workflow

GetMapData()

The following code sample specifies the name and location of the HTML file that isdisplayed to the creator of a workflow map when they define the Table Values data typeinformation for each task in a workflow map.

function Assoc GetMapData( \Object prgCtx, \Dynamic context, \Dynamic data )Dynamic retValRecArray 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 = contextretVal.Data.Data = data

endreturn( retVal )

end

PutMapData()

The following code sample describes how to save the information specified on the HTMLpage that is referenced in the GetMapData() script (that is, t_tablevalues.html). Thisscript saves the settings that the creator of a workflow map specifies when they definewhether each workflow attribute field is editable, required, or read-only for a particularworkflow task.

function Assoc PutMapData( \Object prgCtx, \Dynamic context, \Dynamic data, \Record r )

Assoc retValList nonEditableList requiredString nameString key

//Determine whether each workflow attribute field is editable,//required, or read-only.

Page 130: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 115

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

endend

//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 = nonEditablecontext.Form.REQUIRED_TABLE_VALUES = required

retVal.OK = TRUEreturn retVal

end

t_tablevalues.html

The following HTML file is displayed to the creator of a workflow map when they definetasks in a workflow map that contains the Table Values data type. This HTML page letsthe 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 aString selectedInteger i = 1Record theTask = data.taskInfoAssoc 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>

Page 131: Live Link

Example 2: Adding a New Data Type

116 Developer’s Guide for Extending Livelink Workflow

<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 intheForm.REQUIRED_TABLE_VALUES ) && !( a intheForm.NONEDITABLE_TABLE_VALUES ) ) ? " SELECTED" : ""

<OPTIONVALUE="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 intheForm.REQUIRED_TABLE_VALUES ) ) ? " SELECTED" : ""

<OPTIONVALUE="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 intheForm.NONEDITABLE_TABLE_VALUES ) ) ? " SELECTED" : ""

<OPTIONVALUE="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><FONTFACE="`[WebDsp_Font.SansSerif]`"size="2">&nbsp;`[WebDoc_HTMLLabel.Action_]`&nbsp;</FONT></TD>

<TD><INPUT TYPE="Submit"

VALUE="`[WebWFP_HTMLLabel.AddToWorkflowDefinitionButtonLabel]`"></TD></TR>

</TABLE>;;end

Page 132: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 117

Defining the Data Type’s Web Object

After you define the API object and the Workflow Painter information for the data typethat you are creating, you must define the data type’s Web object.

To define the data type’s Web object:

1. Orphan WebWork:WebWorkRoot:WFPackage in the custmod OSpace in your custommodule, and name it WorkTableValues.

2. In the WorkTableValues object, change the fSubType feature to an Integer/Realtype, and set it to 1.

The fSubType feature stores a unique integer that works with the fType feature toidentify 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 toidentify 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,” page123.

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 aworkflow by workflow participants. Now you must provide the code required tocustomize the object for the Table Values data type.

Page 133: Live Link

Example 2: Adding a New Data Type

118 Developer’s Guide for Extending Livelink Workflow

GetData()

The following code sample describes how to set up the HTML page that is loaded whenthe initiator of a workflow specifies values for the workflow attributes on the Project andCustomer pages just before the first task is routed to the corresponding workflowparticipant.

function Assoc GetData( \Object prgCtx, \Dynamic context, \Dynamic data, \Record request, \Boolean forStatus = False )

Assoc paneDataAssoc tabPaneInfoAssoc tmpDynamic retValInteger whichTabRecArray array

if ( IsDefined( data ) && Length( data ) )retVal = Assoc.CreateAssoc()

retVal.Data = dataretVal.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()

Page 134: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 119

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 = 1endtabPaneInfo.TabList[ whichTab ].Active = TrueretVal.TabInfo = tabPaneInforetVal.Tab = whichTab

end

return( retVal )end

GetSubMapData()

The following code sample describes how to specify the name and location of the HTMLfile that is displayed to the creator of a workflow map when they define the sub-workflowtask that contains the Table Values data type in a workflow that also contains the TableValues data type.

function Assoc GetSubmapData( \Object prgCtx, \Dynamic context, \Dynamic data, \Boolean loadData = True )

DAPINODE nodeDynamic retValRecord mapRecRecord rString mapName

Boolean found = FalseList 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, { \

Page 135: Live Link

Example 2: Adding a New Data Type

120 Developer’s Guide for Extending Livelink Workflow

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 = Truebreak

endend

endend

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

endbreak

endend

endendretVal = Assoc.CreateAssoc()retVal.Data = Assoc.CreateAssoc()retVal.Data.taskInfo = contextretVal.Data.Data = dataretVal.Data.Found = foundretVal.Data.Title = mapNameretVal.Data.PassedValues = passedValuesretVal.HTMLFile = 'submap_tablevalues.html'retVal.ModuleName = 'custmod'

endreturn( retVal )

end

GetTabInfo()

The following code sample describes how to generate the names of the custom data typetabs (Table Values, Project, and Customer) and the URLs required to display the correctHTML pages when workflow participants work on their tasks.

Function Assoc GetTabInfo( \Object prgCtx, \Record request, \Dynamic data, \Integer paneIndex )

Page 136: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 121

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().fDataNameretVal.URL = urlretVal.HelpKey = .GetDataTypeObj().OSNameretVal.Active = FALSEreturn( retVal )

end

PutSubMapData()

The following code sample describes how to save the information that the creator of aworkflow map specified when defining data type information for a sub-workflow task (onthe Sub-Map Step Definition page).

function Assoc PutSubMapData( \Object prgCtx, \Dynamic context, \Dynamic data, \Record r )

Assoc commentDataAssoc retValList valsToPassRecord pkgRecRecord recString name

//Locate the Table Values data type in the RecArray of data types//associated with a workflow.

for rec in context.WorkPkgInfoif ( ( rec.Type == .fType ) && ( rec.SubType == .fSubtype ) )

pkgRec = recbreak

endend

//If information about the Table Values data type has not yet//been passed to the sub-workflow task, add a new Record to the//RecArray. This Record contains the list of workflow attributes//that should be passed from the main workflow to the sub-//workflow.

Page 137: Live Link

Example 2: Adding a New Data Type

122 Developer’s Guide for Extending Livelink Workflow

if ( !IsDefined( pkgRec ) )pkgRec = $LLIAPI.RecArrayPkg.NewRecord( context.WORKPKGINFO )pkgRec.Type = .fTypepkgRec.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 }

endendpkgRec.USERDATA = valsToPassretVal.OK = TRUEreturn( retVal )

end

SaveData()

The following code sample describes how to save the information that the initiator of aworkflow specifies on the Project and Customer pages for the Start task before routing theworkflow to the first workflow participant.

function Dynamic SaveData( \Object prgCtx, \Record request, \Assoc data )

Assoc fieldsAssoc retValString nameInteger pane = Str.StringToInteger( request.CustPaneIndex )retVal.OK = True

//Save the values of the fields on the Project tab.

if ( pane == 1 )fields = data.Fieldsfields.Project_Name = request.project_namefields.ID_Code = request.ID_Codefields.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.Customerfor name in Assoc.Keys( fields )

if ( IsFeature( request, name ) )fields.( name ) = Request.( name )

endend

endretVal.Data = datareturn( retVal )

end

Page 138: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 123

submap_tablevalues.html

The following HTML file is displayed when the creator of a workflow map defines a sub-workflow task that contains the Table Values data type in a workflow that also containsthe Table Values data type (by clicking the Table Values tab on the Sub-Map StepDefinition page). It lets the creator of a workflow map specify the type of data that ispassed 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;TableValues:&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>

Page 139: Live Link

Example 2: Adding a New Data Type

124 Developer’s Guide for Extending Livelink Workflow

</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

Page 140: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 125

//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><FONTFACE="`[WebDsp_Font.SansSerif]`"size="2">&nbsp;`[WebDoc_HTMLLabel.Action_]`&nbsp;</FONT></TD>

<TD><INPUT TYPE="Submit"

VALUE="`[WebWFP_HTMLLabel.AddToWorkflowDefinitionButtonLabel]`"></TD>

</TR></TABLE>

;;end

Page 141: Live Link

Example 2: Adding a New Data Type

126 Developer’s Guide for Extending Livelink Workflow

tablevalues.html

The following HTML file sets up the tabs that are displayed when a workflow participantclicks the Table Values tab in the work package of an executing workflow. This HTMLpage sets up the Project and Customer pages.

;;webscript tablevalues( Assoc a )<!-- File: tablevalues.html -->

;Assoc tabInfo = a.TabInfo;Assoc paneInfo = tabInfo.PaneList[ a.Tab ];String 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 pagewhen a workflow participant clicks the Project tab on the Table Values page in the workpackage 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;ProjectName:&nbsp;</FONT></TD>

Page 142: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 127

;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;ProjectCode:&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;DueDate:&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

Page 143: Live Link

Example 2: Adding a New Data Type

128 Developer’s Guide for Extending Livelink Workflow

;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 2Medium

; end

; defaultLow

; 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

Page 144: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 129

customerpane.html

The following HTML file sets up the information required to display the Customer pagewhen a workflow participant clicks the Customer tab on the Table Values page in thework 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;CustomerName:&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 )

Page 145: Live Link

Example 2: Adding a New Data Type

130 Developer’s Guide for Extending Livelink Workflow

<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;ZipCode:&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>

Page 146: Live Link

Example 2: Adding a New Data Type

Adding New Data Types 131

//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><FONTFACE="`[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

Page 147: Live Link

Example 2: Adding a New Data Type

132 Developer’s Guide for Extending Livelink Workflow

Page 148: Live Link

133

Chapter Six

Adding New Workflow Types

You add new workflow types to Livelink when you want to apply a particular set ofcustom operations to different workflow map definitions. Adding new workflow types letsyou define the set of custom operations that are applied to a workflow map definitionwhen the creator of a workflow map chooses to create that particular type of workflow inLivelink. These custom operations can include adding workflow steps, modifying theduration 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 yourworkflow requirements. For example, you can create a workflow type that appends thecurrent date to the title of a workflow in Livelink or you can create a workflow type thatruns a callback script when the workflow is complete. You can also create many differenttypes of workflows—which each perform different custom operations. The customoperations that you define for a workflow type are applied to a workflow’s map definitionjust 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 onthe General tab of a workflow map’s Properties page. The Workflow Type list lets thecreators 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 beforethe workflow is initiated).

In this chapter, you will learn how to:

• Create workflow types

• Add a workflow type to your custom module

Page 149: Live Link

Creating Workflow Types

134 Developer’s Guide for Extending Livelink Workflow

Creating Workflow TypesYou begin creating a new workflow type by orphaning the WFCustomScriptPkg object inan OSpace in your custom module. You can find the WFCustomScriptPkg object inWFMain:WFRoot.

Note The WFCustomScriptPkg object acts as a superclass object that you canuse to create new callback scripts, workflow types, and customized codethat is executed before a workflow is initiated.

Because the WFCustomScriptPkg object is a superclass object that can be used for manydifferent workflow customizations, you will create a child of the WFCustomScriptPkgobject 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 Description

GetWFTypeName() Retrieves the name of the custom workflow type defined by the integervalues of the wfType and wfSubType variables.

If the values of the wfType and wfSubType variables correspond to thevalues of the wfType and wfSubType variables in the GetWFTypes() script,the corresponding workflow name is assigned to the workflow type and avariable named handled is set to TRUE. If the values of the wfType andwfSubType variables do not correspond to the values of the wfType andwfSubType variables in the GetWFTypes() script, a name is not assigned tothe workflow type and the handled variable is set to FALSE.

GetWFTypes() Defines the type, subtype, and name of the custom workflow types thatyou create.

This script returns a list of Assocs. Each Assoc contains an integer thatspecifies the type (wfType), an integer that specifies the subtype(wfSubType), and a string that specifies the name of the workflow type.

StartWF() Defines the changes that you want to make to a particular workflow’s mapdefinition. This script runs each time a workflow is initiated in Livelink. Itdoes not apply to sub-workflows.

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

Page 150: Live Link

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

Adding New Workflow Types 135

Example 3: Adding a Custom Workflow Type to thecustmod Module

This example describes how to create a workflow type that adds the current date to theend of a workflow name in the Livelink Title bar and runs a callback script before thecorresponding 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 yourcustom module, and name it WFCustomScriptPkg.

Note If you have already performed workflow customizations that require theWFCustomScriptPkg, you can proceed directly to step 2.

2. Create a child of the WFCustomScriptPkg object, and name itCustomWorkflowTypes.

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 mustprovide the code required to perform the custom operations.

Note Because the WFCustomScriptPkg object is a superclass object, it maycontain many child objects. If you want a particular custom operation tobe performed by only one of those child objects, you can use the handledvariable. When a custom operation needs to be performed, the childobjects of the WFCustomScriptPkg object are passed the information inorder. When the handled variable is set to TRUE, the operation wasperformed successfully; otherwise, the information is passed to theremaining child objects.

Page 151: Live Link

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

136 Developer’s Guide for Extending Livelink Workflow

CBExecute()

The following code sample defines the callback script that this workflow type calls beforethe workflow is initiated in Livelink. This callback script displays workflow information inthe Debug window when you are running Livelink Builder. The workflow information istaken from the WWork table in the Livelink database. For more information about theWWork 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 setDebug to 1, 2, or 11 in the opentext.ini file, workflow informationfrom the WWork table in the Livelink database is displayed in the threadlogs. Thread logs are stored in the /logs directory of your primaryLivelink 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 dataDynamic retVal

Boolean handled = True

//Perform a switch statement on the callback script’s//identifier. If it is handled by this script, the handled field//returns TRUE and Livelink stops searching for a script to//handle the callback script’s operation. Then the workflow//information is echoed to the Debug window in the Livelink//Builder.

switch( info[ 1 ] )case 100

List workStatusRecArray item

workStatus = prgCtx.WSession().LoadWorkStatus( workID, \subWorkID )

for item in workStatus$LLIAPI.RecArrayPkg.DumpRecArray( item )echo()echo()

end

retVal = True

Page 152: Live Link

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

Adding New Workflow Types 137

end

defaulthandled = False

endend

data.Handled = handleddata.retVal = retVal

return( data )end

GetWFTypeName()

Before you modify the GetWFTypeName() script, you must define the workflow types inthe GetWFTypes() script. For more information about defining workflow types, see“GetWFTypes(),” page 138. The following code sample describes how to retrieve thenames of the workflow types that you define in the GetWFTypes() script:

Function Assoc GetWFTypeName( \Integer wfType, \Integer wfSubType )

Assoc retVal

Boolean handled = FalseString 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 = handledretVal.Name = name

return( retVal )end

Page 153: Live Link

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

138 Developer’s Guide for Extending Livelink Workflow

GetWFTypes()

The following code sample describes how to define the different types of customworkflows in Livelink:

Function List GetWFTypes()Assoc dataList 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 = 1data.SubType = 100data.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 = 1data.SubType = 101data.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 associatewith each workflow type you create. This script tells Livelink to append the current dateto the name of a Custom Workflow Type workflow in the Livelink Title bar and to run acallback script before a Custom Workflow Type workflow is initiated in Livelink.

Function Assoc StartWF( \Object prgCtx, \Record mapInfo, \String name, \List additions )

Assoc retValList cbInfo

Page 154: Live Link

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

Adding New Workflow Types 139

//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 = cbInfoend

//Populate the variables in the retVal Assoc, and then return//retVal.

retVal.Name = nameretVal.Additions = additionsretVal.OK = Truereturn( retVal )

end

Page 155: Live Link

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

140 Developer’s Guide for Extending Livelink Workflow

Page 156: Live Link

141

Chapter Seven

Adding Event Trigger Scripts

Callback events are workflow events or actions that tell WAPI to run certain scripts atspecific times in the execution of a workflow map. When you add a callback event to aLivelink workflow, you instruct the workflow to perform an operation when a specificworkflow event occurs. For example, you can add a callback event that instructs theworkflow to send an e-mail notification to a workflow participant each time a Milestonestep becomes ready.

Event trigger scripts provide an interface to callback events in Livelink. When you addevent trigger scripts to Livelink, this interface (the Event Scripts tab) becomes available inthe Livelink interface—allowing the creator of a workflow map to determine whichoperations to associate with the particular workflow events that occur in the execution ofa workflow map. There are three types of event trigger scripts that you can add: generalevent trigger scripts, performer event trigger scripts, and submap event trigger scripts. Formore information about these event trigger scripts, see “Choosing Event Trigger Scripts,”page 142.

Note For information about callback events and their role in the LivelinkWorkflow module, see “Understanding Callback Events in LivelinkWorkflow,” page 21.

In this chapter, you will learn about:

• General event trigger scripts

• Performer event trigger scripts

• Submap event trigger scripts

Page 157: Live Link

Choosing Event Trigger Scripts

142 Developer’s Guide for Extending Livelink Workflow

Choosing Event Trigger ScriptsThere are three types of event trigger scripts: general event trigger scripts, performer eventtrigger scripts, and submap event trigger scripts.

Table 7-1: Livelink Workflow Event Trigger Scripts

Event Trigger Script Description

General Does not return any specific type of data—it simply verifies that thedata returned is not an error.

Performer Returns an integer. This integer represents the ID of the Livelink userto whom a particular workflow task should be assigned when thecorresponding step becomes ready.

Submap Returns an integer which represents the ID of a WAPIMAP definition.This WAPIMAP definition initiates a sub-workflow process as thenext step in the workflow. A submap event trigger script is powerfulbecause it allows Livelink to analyze data and paint a sub-workflowon-the-fly.

To create an event trigger script, you determine which type of event trigger script canhandle the operation that you want to perform, and then orphan one of the followingevent 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 areregistered in $WFMain:WFCBGeneralSubsystem. Performer event trigger objects areregistered in $WFMain:WFCBPerformerSubsystem. Submap event trigger objects areregistered in $WFMain:WFCBSubmapSubsystem.

You determine the custom operations that can be performed throughout the execution ofa 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 object—each ofwhich extends the functionality of a Livelink workflow in different ways. Livelinkbecomes 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 Livelinkinterface when you register the event trigger objects in their subsystems. The event triggerinterface lets the creator of a workflow map associate an event trigger script with a specificworkflow event. Behind the scenes, the creator of the workflow map specifies the data thatis stored in the correct callback column in the WAPI database table. For moreinformation about how Livelink handles callback events, see “Understanding CallbackEvents in Livelink Workflow,” page 21.

Note After you install the custom module in which the event triggerfunctionality is contained, the creator of a workflow map can access the

Page 158: Live Link

Choosing Event Trigger Scripts

Adding Event Trigger Scripts 143

Event Scripts tab when editing a workflow step definition in Livelink.The Event Scripts tab can also be accessed from the workflow’sProperties page.

Use event trigger scripts for operations that require more data than is contained in thework 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.

Page 159: Live Link

Creating General Event Trigger Scripts

144 Developer’s Guide for Extending Livelink Workflow

Creating General Event Trigger ScriptsBefore you can create an event trigger script for the General callback event, you mustorphan the GeneralCallbackScripts object in your custom module. You can find theGeneralCallbackScripts object in WFMain:WFRoot:CallbackEventsScripts. TheGeneralCallbackScripts object will contain the scripts that can be attached to generalworkflow 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 takenbecause it is part of an Evaluate step. A workflow step is resurrected if theworkflow loops back to a step that precedes the Evaluate step, making itpossible for the workflow to take the route that contains the killed stepagain. For more information about Evaluate steps, see the Livelink onlinehelp.

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 Description

fEnabled Contains a Boolean value. When set to TRUE, the fEnabled feature lets youregister the object with which it is associated inWFMain:WFCBGeneralSubsystem.

fScriptName Contains a String value. This feature stores the name that you want torepresent the event trigger script on the Event Scripts tab in the Livelinkinterface.

By default, the name that you give to the script is used to name the option onthe Event Scripts tab in the Livelink interface; however, you can customize thename that is displayed by creating this String feature.

Page 160: Live Link

Creating General Event Trigger Scripts

Adding Event Trigger Scripts 145

Notes • If the names of the scripts that you add to the GeneralCallbackScriptsobject do not begin with a 0 or an underscore (_), they are displayed onthe Event Scripts tab in the Livelink interface as options for Livelink usersto attach to general workflow events. If the names of your scripts beginwith a 0 or an underscore (_), they are not displayed in the Livelinkinterface, but can be used as utility scripts that are referenced by the eventtrigger scripts.

• If you attach a general event trigger script to a Step Ready event and theevent trigger script returns a value of TRUE, the step is automaticallycompleted.

After you set the features associated with the GeneralCallbackScripts object, you cancreate the general event trigger scripts. A prototype for the general event trigger scripts isstored in the ODocumentation() script which is contained in theGeneralCallbackScripts object. General event trigger scripts must return a Booleanvalue—TRUE for success or FALSE for failure.

Table 7-3: Script Associated With the GeneralCallbackScripts Object

Script DescriptionScriptName() Defines the operation that you want to perform

After you orphan the general event trigger object, set the fEnabled feature, and create thegeneral event trigger scripts, you must run the BuildOSpace() script in the Globalsobject of your custom OSpace and restart the Livelink Builder to register your changes.The BuildOSpace() script registers the GeneralCallbackScripts object inWFMain:WFCBGeneralSubsystem and exposes the Event Scripts tab in the Livelinkinterface.

When you install the custom module in which the general event trigger functionality iscontained, you can view your changes in the Livelink interface. The names that youspecified in the fScriptName features of the GeneralCallbackScripts object aredisplayed as options in the lists associated with general workflow events on the EventScripts tab. This lets you add general event trigger scripts to general workflow events.

Note For more information about creating workflows in Livelink, see theLivelink online help.

Page 161: Live Link

Example 4: Updating Workflow Attributes

146 Developer’s Guide for Extending Livelink Workflow

Example 4: Updating Workflow AttributesThis example describes how to create a general event trigger script that is used to update aworkflow attribute. This general event trigger script walks through all of the differenttypes of data that are flowing through the workflow (for example, comments,attachments, forms, or attributes) and locates the attributes RecArray. It then walksthrough 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 thecustmod OSpace in your custom module, and name it GeneralCallbackScripts.

2. In the GeneralCallbackScripts object, set the value of the fEnabled feature toTRUE.

3. In the GeneralCallbackScripts object, create a String feature, and name itfChangeAttribute.

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, andthe Event Scripts tab is exposed in the Livelink interface.

You have provided the information necessary to display the Update Workflow Attributeoption on the Event Scripts tab in the Livelink interface. Now, you must provide the codethat 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

Page 162: Live Link

Example 4: Updating Workflow Attributes

Adding Event Trigger Scripts 147

//custom module.

RecArray attribsRecord r

Boolean found = FalseBoolean 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 = Truebreak

endend

//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.USERDATAfound = False

//Walk through the attributes and locate the one that you//want to update. Then update its value.

for r in attribsif ( r.Name == 'desired attribute' )

r.Value = 'Some new value'found = Truebreak

endend

//When the attribute is updated, save the new workflow data.

if ( found )success = obj.SaveWorkData( prgCtx, work, Undefined, \attribs )

Page 163: Live Link

Example 4: Updating Workflow Attributes

148 Developer’s Guide for Extending Livelink Workflow

endend

endreturn( success )

end

Notes • You can use breakpoints to debug event trigger scripts in the same waythat you use breakpoints to debug other scripts in the Livelink Builder;however, placing a breakpoint in an event trigger script always returns aworkflow error. Some calls in the event trigger scripts (especially thosethat use WAPIWORK) will fail if you have added breakpoints. When youfinish debugging event trigger scripts, you must remove the breakpointsthat you have added.

• Another way to debug callback scripts is to send messages to the Builder’sDebug window using the Echo function. For more information about theEcho function, see the Livelink Builder Developer’s Guide.

Page 164: Live Link

Creating Performer Event Trigger Scripts

Adding Event Trigger Scripts 149

Creating Performer Event Trigger ScriptsBefore you can create the event trigger scripts for the Performer callback event, you mustorphan the PerformerCallbackScripts object in your custom module. You can find thePerformerCallbackScripts object in WFMain:WFRoot:CallbackEventScripts. ThePerformerCallbackScripts object will contain the scripts that can be attached to theAssign 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 Description

fEnabled Contains a Boolean value. When set to TRUE, the fEnabled feature lets youregister the object with which it is associated inWFMain:WFCBPerformerSubsystem.

fScriptName Contains a String value. This feature stores the name that you want torepresent the event trigger script on the Event Scripts tab in the Livelinkinterface.

By default, the name that you give to the script is used to name the option onthe Event Scripts tab in the Livelink interface; however, you can customizethe name that is displayed by creating this String feature.

Note If the names of the scripts that you add to thePerformerCallbackScripts object do not begin with a 0 or anunderscore (_), they are displayed on the Event Scripts tab in the Livelinkinterface as options for Livelink users to attach to the Assign StepPerformer workflow event. If the names of your scripts begin with a 0 oran underscore (_), they are not displayed in the Livelink interface, butcan be used as utility scripts that are referenced by the event triggerscripts.

After you set the features associated with the PerformerCallbackScripts object, youcan create the performer event trigger scripts. A prototype for the performer event triggerscripts is stored in the ODocumentation() script which is contained in thePerformerCallbackScripts object. If successful, these scripts return the ID of a Livelinkuser 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 DescriptionScriptName() Defines the operation that you want to perform

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

Page 165: Live Link

Creating Performer Event Trigger Scripts

150 Developer’s Guide for Extending Livelink Workflow

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

When you install the custom module in which the performer event trigger functionality iscontained, you can view your changes in the Livelink interface. The names that youspecified in the fScriptName features of the PerformerCallbackScripts object aredisplayed as options in the Assign Step Performer list on the Events Script tab. This letsyou add performer event trigger scripts to Assign Step Performer workflow events.

Note For more information about creating workflows in Livelink, see theLivelink online help.

Page 166: Live Link

Example 5: Assigning Steps to Workflow Participants

Adding Event Trigger Scripts 151

Example 5: Assigning Steps to WorkflowParticipants

This example describes how to create a performer event trigger script that is used to locatea workflow attribute named username. This performer event trigger script locates the userassociated with this attribute and assigns the next workflow step to them.

To create the performer event trigger script:

1. Orphan WFMain:WFRoot:CallbackEventScripts:PerformerCallbackScripts inthe custmod OSpace in your custom module, and name itPerformerCallbackScripts.

2. In the PerformerCallbackScripts object, set the value of the fEnabled feature toTRUE.

3. In the PerformerCallbackScripts object, create a String feature and name itfChooseUser.

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 theEvent Scripts tab in the Livelink interface. Now you must provide the code that theChooseUser() script requires to perform the operation.

ChooseUser()

The following code sample describes how to identify the user to which the next step in aworkflow 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

Page 167: Live Link

Example 5: Assigning Steps to Workflow Participants

152 Developer’s Guide for Extending Livelink Workflow

//with the PerformerCallbackScripts object in your custom module.

Dynamic userIDRecArray attribsRecArray performerRecord rString userNameBoolean found = FalseBoolean 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 = Truebreak

endend

//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.USERDATAfound = False

//Walk through the attributes RecArray looking for the//UserName attribute.

for r in attribsif ( r.Name == 'UserName' )

userName = r.Valuefound = Truebreak

endend

//When the UserName attribute is found, retrieve the UserID

Page 168: Live Link

Example 5: Assigning Steps to Workflow Participants

Adding Event Trigger Scripts 153

//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

elseuserID = 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 waythat you use breakpoints to debug other scripts in the Livelink Builder;however, placing a breakpoint in an event trigger script always returns aworkflow error. Some calls in the event trigger scripts (especially thosethat use WAPIWORK) will fail if you have added breakpoints. When youfinish debugging event trigger scripts, you must remove the breakpointsthat you have added.

• Another way to debug callback scripts is to send messages to the Builder’sDebug window using the Echo function. For more information about theEcho function, see the Livelink Builder Developer’s Guide.

Page 169: Live Link

Creating Submap Event Trigger Scripts

154 Developer’s Guide for Extending Livelink Workflow

Creating Submap Event Trigger ScriptsBefore you can create the event trigger scripts for the Submap callback event, you mustorphan the SubmapCallbackScripts object in your custom module. You can find theSubmapCallbackScripts object in WFMain:WFRoot:CallbackEventScripts. TheSubmapCallbackScripts object will contain the scripts that can be attached to theDetermine 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 Description

fEnabled Contains a Boolean value. When set to TRUE, the fEnabled feature lets youregister the object with which it is associated in theWFMain:WFCBSubmapSubsystem.

fScriptName Contains a String value. This feature stores the name that you want to representthe 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 onthe Event Scripts tab in the Livelink interface; however, you can customize thename that is displayed by creating this String feature.

Note If the names of the scripts that you add to the SubmapCallbackScriptsobject do not begin with a 0 or an underscore (_), they are displayed onthe Event Scripts tab in the Livelink interface as options for Livelink usersto attach to the Determine Sub-Map To Use workflow event. If the namesof your scripts begin with a 0 or an underscore (_), they are not displayedin the Livelink interface, but can be used as utility scripts that arereferenced by the event trigger scripts.

After you set the features associated with the SubmapCallbackScripts object, you cancreate the submap event trigger scripts. A prototype for the submap event trigger scripts isstored in the ODocumentation() script associated with the SubmapCallbackScriptsobject. If successful, these scripts return an integer representing the ID of a workflow mapdefinition (as stored in WAPI) that is initiated as a sub-workflow when the correspondingstep becomes ready; otherwise these scripts return errors.

Table 7-7: Script Associated With the SubmapCallbackScripts Object

Script Description

ScriptName() Defines the operation that you want to perform

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

Page 170: Live Link

Creating Submap Event Trigger Scripts

Adding Event Trigger Scripts 155

When you install the custom module in which the submap event trigger functionality iscontained, you can view your changes in the Livelink interface.The names that youspecified in the fScriptName features of the SubmapCallbackScripts object aredisplayed as options in the Determine Sub-Map To Use list on the Event Scripts tab. Thislets you add submap event trigger scripts to Determine Sub-Map To Use workflow events.

Note For more information about creating workflows in Livelink, see theLivelink online help.

Page 171: Live Link

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

156 Developer’s Guide for Extending Livelink Workflow

Example 6: Defining Sub-Workflows On-The-FlyThis example describes how to create a submap event trigger script that is used to define asub-workflow map and save the map definition in WAPI. This map definition instructsLivelink 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 thecustmod OSpace in your custom module, and name it SubmapCallbackScripts.

2. In the SubmapCallbackScripts object, set the value of the fEnabled feature toTRUE.

3. In the SubmapCallbackScripts object, create a String feature and name itfsubworkflow.

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 theEvent Scripts tab is exposed in the Livelink interface.

You have provided the information necessary to display the Generate Sub-Workflowoption on the Event Scripts tab in the Livelink interface. Now you must provide the codethat the subworkflow() script requires to perform the operation.

subworkflow()

The following code sample describes how to define a sub-workflow map on-the-fly andsave 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

Page 172: Live Link

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

Adding Event Trigger Scripts 157

Dynamic userDynamic mapIDInteger flagsList dispositionsRecArray attrRecRecord mapRecRecord newTask

Boolean ok = TrueObject mapPkg = $WFMain.WFMapPkgObject pkgSubSystem = $WFMain.WFPackageSubsystemObject recArrayPkg = $LLIAPI.RecArrayPkgObject taskSubSystem = $WFMain.WFTaskSubsystemObject uSession = prgCtx.USession()Object 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.

Page 173: Live Link

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

158 Developer’s Guide for Extending Livelink Workflow

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 |= $WFPCommentsflags |= $WFPReviewflags |= $WFPDelegateflags |= $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.

Page 174: Live Link

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

Adding Event Trigger Scripts 159

//USERFLAGS[2][2] contains the ordinal number of the//defaulted disposition.

//Specify the attribute data.

Assoc formDataList visibleAttrs = { 'Name', 'Due Date', 'Country', \'Critical Report?' }List requiredAttrs = { 'Name', 'Country' }List nonEditableAttrs = {}

formData.VISIBLE_ATTRIBS = visibleAttrsformData.REQUIRED_ATTRIBS = requiredAttrsformData.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 )

elseok = 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.

Page 175: Live Link

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

160 Developer’s Guide for Extending Livelink Workflow

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 )

Page 176: Live Link

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

Adding Event Trigger Scripts 161

//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, \

Page 177: Live Link

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

162 Developer’s Guide for Extending Livelink Workflow

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 )endif ( !ok )

mapID = Error.Get( 600 )endreturn( mapID )

end

Function Void SetTaskData( \Record task, \Assoc data )String keyfor key in Assoc.Keys( data )

task.( key ) = data.( key )end

end

Notes • You can use breakpoints to debug event trigger scripts in the same waythat you use breakpoints to debug other scripts in the Livelink Builder;however, placing a breakpoint in an event trigger script always returns aworkflow error. Some calls in the event trigger scripts (especially thosethat use WAPIWORK) will fail if you have added breakpoints. When youfinish debugging event trigger scripts, you must remove the breakpointsthat you have added.

• Another way to debug callback scripts is to send messages to the Builder’sDebug window using the Echo function. For more information about theEcho function, see the Livelink Builder Developer’s Guide.

Page 178: Live Link

Restricting Access to Event Trigger Scripts

Adding Event Trigger Scripts 163

Restricting Access to Event Trigger ScriptsCreating event trigger scripts lets the creators of workflows access the Event Scripts tab inthe Livelink interface, where they can add custom operations to specific workflow events.This means that the creators of workflows can determine which operation to performwhen 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 onlycertain Livelink users can attach the event trigger script to specific workflow events, youcan modify the fObjectFactory feature and the FactoryName() script associated withthe corresponding event trigger object. For example, if you want to specify which Livelinkusers can attach a general event script to specific workflow events, you can modify thefObjectFactory feature and the FactoryName() script contained in the general eventtrigger object in your custom module. Similarly, if you want to specify which Livelinkusers can attach a performer event trigger script to the Assign Step Performer workflowevent, you can modify the fObjectFactory feature and the FactoryName() scriptcontained in the performer event trigger object in your custom module. If you want tospecify which Livelink users can attach a submap event trigger script to the DetermineSub-Map To Use workflow event, you can modify the fObjectFactory feature and theFactoryName() script contained in the submap event trigger object.

fObjectFactory Contains a Boolean value. When set to TRUE, this feature workswith the FactoryName() script to allow the Admin user (LivelinkAdministrator) to restrict access to event trigger scripts.

FactoryName() Returns a string that identifies the event trigger scripts to whichyou want to restrict access

To restrict access to event trigger scripts:

1. Locate the event trigger object that contains the event trigger script to which youwant 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 Objectand Usage Privileges page.

Tip You can use the name that you specified in the fScriptName feature byreferencing 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, andsubmap event trigger scripts individually by setting different return values for theFactoryName() scripts for each event trigger object. You can restrict access to all general

Page 179: Live Link

Restricting Access to Event Trigger Scripts

164 Developer’s Guide for Extending Livelink Workflow

event trigger scripts, performer event trigger scripts, and submap event trigger scriptstogether by setting the same return values for the FactoryName() scripts for each eventtrigger object.

After you modify the fObjectFactory features and the FactoryName() scripts containedin the event trigger objects for the event trigger scripts to which you want to restrictaccess, you must run the BuildOSpace() script in the Globals object of your customOSpace. Then, the Admin user can go to the Administer Privileges section of the LivelinkIntranet Administration page and specify which Livelink users can attach the event triggerscripts to workflow events.

Suppose that you create general, performer, and submap event trigger scripts in yourcustom module and you want to restrict access to the performer and submap event triggerscripts. 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 performerand submap event trigger scripts to workflow events. In this case, certain Livelink userscan attach the performer and submap event trigger scripts to workflow events and allLivelink users can attach the general event trigger scripts to workflow events.

Page 180: Live Link

165

Appendix A

Creating the Form Task Type

The following example describes how to create a more complex task type that isdependent on the Livelink Forms module. The forms task type prompts workflowparticipants to complete a form and submit data to the workflow. It displays a form-typeinterface to the performer of the task, prompts the performer to complete the form, androutes the work package based on the performer’s input.

Notes • This section documents the task type that is created for you when youinstall the Livelink Forms Workflow and Livelink Forms WorkflowPainter modules.

• You must install the Livelink Forms module before you can use the tasktype that you create in this chapter. This task type is dependent on thecomponents of the Livelink Forms module.

Page 181: Live Link

The FormTaskAPI Object

166 Developer’s Guide for Extending Livelink Workflow

The FormTaskAPI ObjectYou begin adding the form task type to Livelink by defining the form task type’s APIobject.

To define the form task type’s API object:

1. Orphan WFMain:WFRoot:WFObjectTypes:WFTaskTypes:StandardTasks in anOSpace in your custom module, and name it FormTaskAPI.

2. In the FormTaskAPI object, change the fSubType feature to an Integer/Real type, andset 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 FormTask.

The fTaskName feature holds the text that is displayed in the Workflow Painter forthe 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 type’s API. Now you must providethe 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

Page 182: Live Link

The FormTaskAPI Object

Creating the Form Task Type 167

ReadyTaskForInitiation()

The following code sample describes how to prepare the task for initiation in Livelink. Itstores callback scripts and any other additional data that is required by the form task typethroughout the execution of the workflow.

Function Boolean ReadyTaskForInitiation( \Object prgCtx, \Record r, \RecArray workPkg )

Assoc expandInfoList cbInfoList prevCBsRecArray userBoolean success = TrueObject const = $WFMain.WFConstObject uSession = prgCtx.USession()Record userData = $WFMain.WFMapPkg.CreateTaskUserDataRec()userData.TYPE = r.TYPEuserData.SUBTYPE = r.SUBTYPEuserData.PERMFLAGS = r.USERFLAGSr.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 } }

elsecbInfo = { { 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.PERFORMERCBprevCBs = ( IsDefined( prevCBs ) ) ? prevCBs : {}r.PERFORMERCB = { { const.kCBSetGrpStepPerformer,

r.PERFORMERID }, @prevCBs }end

end

Page 183: Live Link

The FormTaskAPI Object

168 Developer’s Guide for Extending Livelink Workflow

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

endend

//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.TYPEexpandInfo.SubType = r.SUBTYPEexpandInfo.Flag = r.EXATTS.GroupFlagsr.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 }

elsesuccess = False

endreturn( success )

end

SetTaskDefaults()

The following code sample describes how to specify the default information required byLivelink to recognize the form task type. This is also where you specify data that must bepresent if the creator of the workflow does not edit the task before the workflow isinitiated.

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 = .fTypetaskRec.SUBTYPE = .fSubTypetaskRec.EXATTS = Assoc.CreateAssoc( Assoc.NotSetValue() )taskRec.CUSTOMDATA = Assoc.CreateAssoc( Assoc.NotSetValue() )

//Specify the name of the task and the performer ID associated

Page 184: Live Link

The FormTaskAPI Object

Creating the Form Task Type 169

//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 preparedfor initiation to a workflow map definition task.

Function Void SetTaskRecFromMapTask( \WAPIMAPTASK task, \Record r, \Record taskData )

List cbInfo

Dynamic data = task.pUserDataObject const = $WFMain.WFConstInteger groupFlag = const.kWFGroupStandard

//Convert the values stored in the WAPIMAPTASK to the//corresponding fields in the map definition task.

r.TYPE = data.TYPEr.SUBTYPE = data.SUBTYPEr.USERFLAGS = data.PERMFLAGSr.SUBMAPID = task.pSubMapIDr.PERFORMERID = taskData.WORK.SUBWORKTASK_PERFORMERIDr.READYCB = task.pReadyCB

//Remove the Done callback script.

r.DONECB = task.pDoneCBr.DONECB = $WFMain.WFMapPkg.RemoveCBInfoType( r.DONECB, \const.kCBSetTaskDoneData )r.KILLCB = task.pKillCB

//Remove the Performer callback script.

r.PERFORMERCB = task.pPerformerCBr.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.pFormr.PAINTER = task.pPainterr.STARTDATE = task.pStartDater.DUEDURATION = task.pDueDurationr.DUEDATE = task.pDueDater.DUETIME = task.pDueTime

Page 185: Live Link

The FormTaskAPI Object

170 Developer’s Guide for Extending Livelink Workflow

r.FLAGS = task.pFlagsr.TITLE = taskData.WORK.SUBWORKTASK_TITLEr.DESCRIPTION = task.pDescriptionr.INSTRUCTIONS = task.pInstructionsr.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.pSubMapIdCBfor cbInfo in r.SUBMAPIDCB

if ( cbInfo[ 1 ] == const.kCBExpandGroup )groupFlag = cbInfo[ 2 ].Flagbreak

endend

//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

Page 186: Live Link

The FormTaskPaint Object

Creating the Form Task Type 171

The FormTaskPaint ObjectAfter you define the API for the task type that you are creating, you must provide theWorkflow Painter with the information it requires to display the form and handle thedata 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, andname 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 toidentify the object. It must match the value specified for the fSubType feature in theFormTaskAPI 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 toidentify the object. It must match the value specified for fType in the FormTaskAPIobject.

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 type’s Workflow Painterinformation. Now you must provide the code required to customize the information forthe form task type.

GetMapData()

The following code sample describes how to display the Step Definition page for this tasktype 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 aAssoc paneDataAssoc performerInfo

Page 187: Live Link

The FormTaskPaint Object

172 Developer’s Guide for Extending Livelink Workflow

Assoc retValAssoc tabPaneInfoDynamic tmpInteger whichTabList tabListObject objRecord p

Boolean knownUser = FalseInteger i = 1Record taskInfo = mapRec.TASKS[ taskID ]String gif = 'guy.gif'String name = [WebWFP_HTMLLabel.User]String 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 = mapIDretVal.Data.TaskID = taskIDretVal.Data.NextURL = request.NextURLretVal.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 = objNametmp.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.

Page 188: Live Link

The FormTaskPaint Object

Creating the Form Task Type 173

tabPaneInfo.TabList = { tmp }

a = Assoc.CreateAssoc()a.TaskInfo = taskInfoa.Gif = '16form.gif'a.MapID = mapIDa.TaskID = taskID - 1a.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 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 = Truename = tmp[ 1 ].NAMEif ( tmp[ 1 ].TYPE != UAPI.USER )

gif = '2-guys.gif'end

endend

end

performerInfo.Name = nameperformerInfo.Gif = gifperformerInfo.KnownUser = knownUserperformerInfo.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 eventInfoList fields = { 'PERFORMERCB', 'READYCB', 'DONECB', 'KILLCB', \'RESURRECTCB' }List events = { [WebWFP_HTMLLabel.AssignStepPerformer], \

[WebWFP_HTMLLabel.StepBecomesReady], \[WebWFP_HTMLLabel.StepIsDone], \[WebWFP_HTMLLabel.StepIsKilled], \[WebWFP_HTMLLabel.StepIsResurrected] }

Page 189: Live Link

The FormTaskPaint Object

174 Developer’s Guide for Extending Livelink Workflow

eventInfo.Events = eventseventInfo.FieldNames = fieldseventInfo = $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 = eventInfoa.DataRec = taskInfotmp = Assoc.CreateAssoc()tmp.ModuleName = 'webwfp'tmp.HTMLFile = 't_events.html'tmp.Data = atabPaneInfo.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 = 1end

tabPaneInfo.TabList[ whichTab ].Active = True

retVal.Data.TabInfo = tabPaneInforetVal.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 tmpList retValRecord rObject 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

Page 190: Live Link

The FormTaskPaint Object

Creating the Form Task Type 175

breakend

endend

//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 workflowmap enters on the Form Step Definition page.

function assoc PutMapData( \Object prgCtx, \Record mapRec, \Record taskInfo, \Record r )

Assoc paneDataAssoc retValInteger defaultDispoInteger flagsInteger iInteger permAndDispositionFlagsList dispositionsList emptyDisposObject objReal timeRecord pInteger count = 0retVal.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

Page 191: Live Link

The FormTaskPaint Object

176 Developer’s Guide for Extending Livelink Workflow

//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 inDaysretVal.ErrMsg =

[WebWork_ErrMsg.DurationMustBeANumberOfDays]else

retVal.ErrMsg =[WebWork_ErrMsg.DurationMustBeANumberOfHours]

endelse

taskInfo.DueDuration =$LLIAPI.FormatPkg.ConvertToSeconds( inDays, time )

endelse

taskInfo.DueDuration = Undefinedend

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.FormNameend

else

//Save the callback script information.

$WEBWFP.WFContentManager.StoreCallbackData( taskInfo, r )end

return retValend

Page 192: Live Link

The FormTaskPaint Object

Creating the Form Task Type 177

formtask.html

The following code sample describes how to design the Web page that is displayed whenthe creator of a workflow map edits a form task in the Workflow Painter. You edit a formtask on the Form Step Definition page, which is accessed by double-clicking the task'sicon 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 iList durationInfoString checkedString dueDurationDynamic taskInfo = data.TaskInfoString 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>

Page 193: Live Link

The FormTaskPaint Object

178 Developer’s Guide for Extending Livelink Workflow

<TD bgcolor="#CCCCCC" NOWRAP><FONTFACE="`[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>

Page 194: Live Link

The FormTaskPaint Object

Creating the Form Task Type 179

</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></TD>

;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></TD>

<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] )

elsedurationInfo = { 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();">

Page 195: Live Link

The FormTaskPaint Object

180 Developer’s Guide for Extending Livelink Workflow

;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><FONTFACE="`[WebDsp_Font.SansSerif]`"size="2">&nbsp;`[WebDoc_HTMLLabel.Action_]`&nbsp;</FONT></TD>

<TD><INPUT TYPE="Submit"

VALUE="`[WebWFP_HTMLLabel.AddToWorkflowDefinitionButtonLabel]`"></TD>

</TR></TABLE>

;;end

Page 196: Live Link

The FormTaskWork Object

Creating the Form Task Type 181

The FormTaskWork ObjectAfter you define the API and Workflow Painter information for the form task type, youmust define the information that handles the task when a workflow participant is workingon 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 Statuspage:

1. Orphan WebWork:WebWork Root:WFTask in an OSpace in your custom module, andname 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 matchthe value specified for fSubType in the FormTaskAPI object and the FormTaskPaintobject.

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 toidentify the object. It must match the value specified for fType in the FormTaskAPIobject 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 formtask in the Step Icon Palette. This image must be placed in your module’s /supportdirectory (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(),” page192.

You have created the objects necessary to handle the task operations. Now you mustprovide the code required to customize the information for the form task type.

Page 197: Live Link

The FormTaskWork Object

182 Developer’s Guide for Extending Livelink Workflow

GetDisplayPerformerInfo()

The following code sample describes how to retrieve the name and ID of the Livelink userto which the task is assigned.

Function Dynamic GetDisplayPerformerInfo( \Object prgCtx, \Record taskRec )Dynamic performerDynamic retValInteger performerIDObject 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

elseperformerID = 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 ].IDretVal.Name = performer[ 1 ].NAME

endelse

retVal = [WebWork_Label.User]endreturn( retVal )

end

GetPainterInfo()

The following code sample describes how to define the information that the WorkflowPainter needs to know about the form task type.

Function Assoc GetPainterInfo( \Object prgCtx, \Record task = Undefined )Assoc infoAssoc linkDataAssoc retVal

Page 198: Live Link

The FormTaskWork Object

Creating the Form Task Type 183

String gif = .fTaskGifString 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.Titlegif = 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'

Page 199: Live Link

The FormTaskWork Object

184 Developer’s Guide for Extending Livelink Workflow

//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 appearwhen you right-click the form task type’s icon in the Workflow Painter:

Function List GetPainterMenu( Boolean viewonly )

//If the menu commands are not set to viewonly, populate the //following Assocs.

List retvalif ( !viewonly )

AssocaAssocbAssoccAssocdAssoceAssocf

//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"

Page 200: Live Link

The FormTaskWork Object

Creating the Form Task Type 185

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 }

Page 201: Live Link

The FormTaskWork Object

186 Developer’s Guide for Extending Livelink Workflow

//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).

elseAssoca

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 retvalend

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

GetStatusDisplay()

The following code sample describes how to retrieve the information that is displayedwhen a workflow participant clicks the task name on the Step List page. The Step List pageis accessed by clicking the Step List tab on the Detailed Status page.

function Assoc GetStatusDisplay( \Object prgCtx, \Dynamic context, \Dynamic data = Undefined )

Assoc aAssoc retValAssoc tabPaneInfoAssoc tmpInteger whichTabRecArray auditInfoRecArray dispositionRecArray performerString title

Record mapRec = context.MAP_PAINTERRecord 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

Page 202: Live Link

The FormTaskWork Object

Creating the Form Task Type 187

//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 = Undefinedend

end

Page 203: Live Link

The FormTaskWork Object

188 Developer’s Guide for Extending Livelink Workflow

//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 = 1end

tabPaneInfo.TabList[ whichTab ].Active = TRUEend

//Set up an Assoc that returns all of the data required by//Livelink to draw the Step Detail page.

retVal.OK = tmp.OKretVal.ErrMsg = tmp.ErrMsgretVal.HTMLFile = "wwt.html"retVal.ModuleName = 'webwork'retVal.Tab = whichTabretVal.TabInfo = tabPaneInforetVal.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

Page 204: Live Link

The FormTaskWork Object

Creating the Form Task Type 189

GetTaskEditData()

The following code sample describes how to retrieve the information that is displayedwhen a workflow participant clicks the task name on the Tasks page in their PersonalWorkspace.

function Assoc GetTaskEditData( \Object prgCtx, \Record taskInfo, \Record r )

Assoc aAssoc dataAssoc paneDataAssoc retValAssoc tabPaneInfoAssoc tmpDynamic statusList tabListObject objRecArray packagesRecord pString nextURLString urlWAPIWORK work

Boolean ok = trueBoolean groupStep = FalseInteger flags = WAPI.STARTTASK_FLAG_REEXECUTEString formName = Undefined

//Determine whether the task has been assigned to a Livelink user//or a group.

if !( $WFMain.WAPIPkg.CheckTaskAssignedToUser( prgCtx, taskInfo \) )

groupStep = Trueend

//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.

Page 205: Live Link

The FormTaskWork Object

190 Developer’s Guide for Extending Livelink Workflow

tmp.Label = [WebWork_HTMLLabel.General]tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \$WebDSP.HTMLPkg.ArgsToURL( r ), 1 )tmp.Active = FALSE

tabPaneInfo.TabList = { tmp }

a.TaskInfo = taskInfoa.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 = tabPaneInfodata.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_DISPLAYFORMend

if ( IsDefined( formName ) )

Page 206: Live Link

The FormTaskWork Object

Creating the Form Task Type 191

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.

elseok = FalseretVal.ErrMsg = \

[WebFormWF_ErrMsg.CouldNotFindFormContactWorkflowManager]end

//If the data was not retrieved correctly (OK=FALSE),//return an error message.

elseok = FalseretVal.ApiError = workretVal.ErrMsg = [Web_ErrMsg2.CouldNotAccessWorkpackage]

end

//If the data was not retrieved correctly (OK=FALSE),//return an error message.

elseok = FalseretVal.ApiError = workretVal.ErrMsg = [Web_ErrMsg2.CouldNotAccessWork]

end

//Free the memory held by the WAPIWORK object.

WAPI.FreeWork( work )else

ok = FalseretVal.ApiError = workretVal.ErrMsg = [Web_ErrMsg2.CouldNotAllocwork]

endend

retVal.OK = okretVal.Data = data

return( retVal )end

Page 207: Live Link

The FormTaskWork Object

192 Developer’s Guide for Extending Livelink Workflow

NewPerformer()

The following code sample describes how to update task information if the task isreassigned 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 retValBoolean success = True

taskRec.PERFORMERID = newIDretVal.OK = success

return( retVal )end

ReassignStep()

The following code sample describes how to create a script that lets workflow participantsreassign a task on the Step Detail page.

Function Boolean ReassignStep( \Object prgCtx, \Record taskRec, \Record user, \Record workData, \WAPIWORK work )

Assoc userInfoInteger whereList cbDataBoolean ok = FalseObject 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 )

Page 208: Live Link

The FormTaskWork Object

Creating the Form Task Type 193

//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 } }

elsecbData = Undefined

end

ok = UpdateMapTask( prgCtx, taskRec, cbData )end

if ( !wSession.EndTrans( ok ) )ok = False

endend

end

return( ok )end

Function Boolean UpdatePerformer( \Object prgCtx, \WAPIWORK work, \Record old, \Record new )

Boolean successList 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.IDend

return( success )end

Function Boolean UpdateMapTask( \Object prgCtx, \Record taskData, \List cbData = Undefined )

Boolean success

Page 209: Live Link

The FormTaskWork Object

194 Developer’s Guide for Extending Livelink Workflow

WAPIMAPTASK task

Object session = prgCtx.WSession()WAPIMAP 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

Page 210: Live Link

Request Handlers

Creating the Form Task Type 195

Request HandlersNow that you have modified the scripts associated with the FormTaskWork object, youmust create the custom request handlers that perform the operations requested when aworkflow participant executes the new task type. The request handlers that you create arebased 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 nameit CustModuleLLRequestHandler.

2. Create a child object of the CustModuleLLRequestHandler object, and name itFormEdit.

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 arequest 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 handlerarguments for the FormEdit request handler. This prototype is stored in thefPrototype 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“FormEdit—Execute(),” page 197.

9. In the FormEditStart object, edit the SetPrototype() script to define how tovalidate a request handler argument.

For more information about the SetPrototype() script for the FormEditStartobject, see “FormEditStart—SetPrototype(),” page 201.

10. Run the SetPrototype() script.

This script defines how to create a prototype that validates request handlerarguments for the FormEditStart request handler. This prototype is stored in thefPrototype feature.

Page 211: Live Link

Request Handlers

196 Developer’s Guide for Extending Livelink Workflow

11. Edit the Execute() script.

For more information about the Execute() script for the FormEditStart object, see“FormEditStart—Execute(),” 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 nameit SaveForm.

This custom request handler will be used to save the form when a workflowparticipant 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 handlerargument.

For more information about the SetPrototype() script for the SaveForm object, see“SaveForm—SetPrototype(),” 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 custommodule, and name it CustModuleRequestHandlerGroup.

2. In the CustModuleRequestHandlerGroup object, set the value of the fEnabledfeature to TRUE.

3. Run the SetRequestHandlers() script.

This script creates a list of the request handlers in the OSpace, and stores this list inthe fRequestHandlers feature.

4. In the Globals object, run the BuildOSpace() script.

Page 212: Live Link

Request Handlers

Creating the Form Task Type 197

FormEdit—SetPrototype()

The following code sample describes how to validate request handler arguments for theFormEdit request handler. After you edit this script, you must run it to set thefPrototype 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

FormEdit—Execute()

The following code sample defines the operation of the FormEdit request handler for theform task type.

function Dynamic Execute( Dynamic ctxIn, Dynamic ctxOut, Record r )

Assoc extendedDataAssoc formAssoc responseAssoc saveDataAssoc tmpDynamic formsDynamic statusDynamic taskInfoObject objString mapName

Object prgCtx = .prgSession()Object subSystem = $WebForm.WebFormSubsystemString pageType = 'WORK'

//Find the controller object for the forms task type (that is, the//form task’s API object).

obj = $WFMain.WFPackageSubsystem.GetItemByName( 'Form' )

if ( IsDefined( obj ) )taskInfo = prgCtx.WSession().LoadTaskStatus( \

Page 213: Live Link

Request Handlers

198 Developer’s Guide for Extending Livelink Workflow

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 )

elseforms = obj.GetTaskStatusWork( prgCtx, taskInfo[ 1 ], \r.SubWorkID )

endelse

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 )

endend

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.SubworkIDsaveData.LL_FormName = r.FormNamesaveData.LL_NextURL = Web.Escape( r.NextURL )

end

if ( Str.Locate( Str.Upper( r.NextURL ), 'WORK.STATUSTASKS' ) )

Page 214: Live Link

Request Handlers

Creating the Form Task Type 199

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 = ctxOutr = 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

endend

endend

return Undefinedend

function Assoc GetForm( \List forms, \String formName )

Assoc formAssoc formData

Page 215: Live Link

Request Handlers

200 Developer’s Guide for Extending Livelink Workflow

Assoc statusInteger templateIDInteger where

Assoc retVal = UndefinedBoolean found = FalseObject 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 formsif ( form.Name == formName )

templateID = form.TemplateIDformData = form.Data

found = Trueelse

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 = Trueend

end

if ( found )retVal = Assoc.CreateAssoc()

retVal.TemplateID = templateIDretVal.Data = formData

breakend

end

return( retVal )end

Page 216: Live Link

Request Handlers

Creating the Form Task Type 201

FormEditStart—SetPrototype()

The following code sample describes how to validate request handler arguments for theFormEditStart request handler. After you edit this script, you must run it to set thefPrototype 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

FormEditStart—Execute()

The following code sample defines the operation of the FormEditStart request handlerfor the form task type.

function Dynamic Execute( Dynamic ctxIn, Dynamic ctxOut, Record r )

Assoc extendedDataAssoc formAssoc mapDataAssoc responseAssoc saveDataAssoc tmpDynamic statusString mapName

Object prgCtx = .prgSession()Object 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

Page 217: Live Link

Request Handlers

202 Developer’s Guide for Extending Livelink Workflow

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.IDsaveData.LL_FormName = r.FormNamesaveData.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 = ctxOutr = 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

endend

endend

return Undefinedend

Page 218: Live Link

Request Handlers

Creating the Form Task Type 203

function Assoc GetForm( \RecArray work_packages, \String formName )

Assoc formAssoc statusDynamic formDataDynamic retValInteger templateIDInteger whereList formsList keyRecord r

Boolean found = FalseObject objType = $WFMain.WFPackageSubsystem.GetItemByName( \'Form' )Object prgCtx = .prgSession()

//Find the form data type in the workflow’s work package and//prepare it for use.

if ( IsDefined( objType ) )key = { objType.fType, objType.fSubType }

for r in work_packagesif ( key == { r.TYPE, r.SUBTYPE } )

forms = objType.ReadyForModification( prgCtx, r.USERDATA )break

endend

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 formsif ( form.Name == formName )

templateID = form.TemplateIDformData = form.Data

found = Trueelse

where = formName in form.SubForms

if ( IsDefined( where ) && ( where > 0 ) )templateID = form.SubFormTemplateIDs[ where ]formData = form.Data

found = Trueend

end

if ( found )retVal = Assoc.CreateAssoc()

retVal.TemplateID = templateIDretVal.Data = formData

Page 219: Live Link

Request Handlers

204 Developer’s Guide for Extending Livelink Workflow

breakend

end

return( retVal )end

SaveForm—SetPrototype()

The following code sample describes how to validate request handler arguments for theSaveForm request handler. After you edit this script, you must run it to set thefPrototype 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

SaveForm—Execute()

The following code sample defines the operation of the SaveForm request handler for theform task type.

function Dynamic Execute( Dynamic ctxIn, Dynamic ctxOut, Record r )

Assoc formAssoc formDataAssoc tmpDynamic formsDynamic statusDAPINODE templateInteger indexObject objString name

Object prgCtx = .prgSession()

//Find the controller object for the forms task type (that is,//the form task’s 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 )

Page 220: Live Link

Request Handlers

Creating the Form Task Type 205

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.Indextemplate = form.Templateform = form.Form

else.fError = Str.Format( [WebFormWF_ErrMsg.CouldNotFindForm1],

r.LL_FormName )end

endelse

.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 workflow’s 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_NextURLr = Assoc.ToRecord( tmp )

$WebForm.WebFormSubsystem.RedirectAfterSave( template, ctxOut,this, r, status )

end

Page 221: Live Link

Request Handlers

206 Developer’s Guide for Extending Livelink Workflow

return Undefinedend

function Assoc GetForm( \List forms, \String formName )

Assoc formAssoc statusInteger templateIDInteger where

Assoc retVal = UndefinedBoolean found = FalseInteger index = 1Object prgCtx = .prgSession()

for form in formsif ( form.Name == formName )

templateID = form.TemplateID

found = Trueelse

where = formName in form.SubForms

if ( IsDefined( where ) && ( where > 0 ) )templateID = form.SubFormTemplateIDs[ where ]

found = Trueend

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 = formretVal.Index = indexretVal.Template = status.Template

elseecho( 'Could not find template for ', formName )

end

breakend

index += 1end

return( retVal )end

Page 222: Live Link

Request Handlers

Creating the Form Task Type 207

SaveFormStart—Execute()

The following code sample describes how to validate request handler arguments for theSaveFormStart request handler. After you edit this script, you must run it to set thefPrototype feature for the request handler.

function Dynamic Execute( Dynamic ctxIn, Dynamic ctxOut, Record r )

Assoc formAssoc formDataAssoc mapDataAssoc statusAssoc tmpDAPINODE templateString 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.Formtemplate = 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.

Page 223: Live Link

Request Handlers

208 Developer’s Guide for Extending Livelink Workflow

formData.Record.USERDATA[ formData.Index ] = form

//Save the workflow map definition.

if ( !$WebWFP.WFPkg.SaveMap( prgCtx, mapData.MapInfo,mapData.MapHolder, True ) )

status.OK = Falsestatus.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_NextURLr = 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 Undefinedend

function Assoc GetForm( \RecArray work_packages, \String formName )

Assoc formAssoc statusInteger templateIDInteger whereList formsList keyRecord r

Assoc retVal = UndefinedBoolean found = FalseInteger index = 1Object objType = $WFMain.WFPackageSubsystem.GetItemByName(

'Form' ) // No XLATEObject prgCtx = .prgSession()

//Find the form’s data type in the workflow’s work package and//prepare it for use.

if ( IsDefined( objType ) )key = { objType.fType, objType.fSubType }

for r in work_packages

Page 224: Live Link

Request Handlers

Creating the Form Task Type 209

if ( key == { r.TYPE, r.SUBTYPE } )forms = r.USERDATA = objType.ReadyForModification( \prgCtx, r.USERDATA )break

endend

end

for form in formsif ( form.Name == formName )

templateID = form.TemplateID

found = Trueelse

where = formName in form.SubForms

if ( IsDefined( where ) && ( where > 0 ) )templateID = form.SubFormTemplateIDs[ where ]

found = Trueend

end

//Look through all the forms and sub-forms that are//available to the workflow and see if the specified//form exists. If the form is found, store the information//about the form in an Assoc and return it so that it can be//used to display the form.

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 = formretVal.Record = rretVal.Index = index

elseecho( 'Could not find template for ', formName )

end

breakend

index += 1end

return( retVal )end

Page 225: Live Link

Request Handlers

210 Developer’s Guide for Extending Livelink Workflow

Page 226: Live Link

211

Index

Aaccess

restricting access to event trigger scripts, 163API object

adding data types, 86, 99adding task types, 32

architecture, 12attachments, work packages, 2attributes, work packages, 2

Ccallback events, 21callback scripts

event trigger scripts, 141CBExecute() script

WFCustomScriptPkg object, 80, 136ChangeAttribute() script, 146CheckTaskDone() script, 87, 101ChooseUser() script, 151comments, work packages, 2Configure request handler object, 28CreateNewInstance() script, 87, 102CreateReviewMapRec() script, 40CreateWorkData() script, 87, 103creator, workflow relationship, 3cust_drop() script, 98cust_sql() script, 97custmod module, 26

Configure request handler, 28configuring, 27directory structure, 26

OSpace, 26packaging, 30query string, 28request handler group, 28

Custom Display taskexample 1, 39

custom moduleadding data types, 85adding event trigger scripts, 141adding task types, 31adding the form task type, 165adding workflow types, 133

CustomDisplayAPI object, 39CustomDisplayPaint object, 48CustomDisplayWork object, 61customerpane.html, 129

Ddata types

adding new, 85adding schema, 95adding the Table Values data type, 94API object, 86, 99creating database tables, 95defining the Web object, 117example 2, 94Livelink Workflow architecture, 18overview, 9Web object, 91Workflow Painter information, 89, 113

data types, routing legacy data, 18database storage

workflow management information, 14DeleteWorkData() script, 87, 103

Page 227: Live Link

Request Handlers

212 Developer’s Guide for Extending Livelink Workflow

directory structuremodule, 26

EEvaluate step, 5, 17event trigger scripts

adding new, 141architecture, 23choosing, 142example 4, 146example 5, 151general, 142, 144Livelink Workflow architecture, 21overview, 10performer, 142, 149restricting access, 163submap, 142, 154subsystems, 142

ExecuteCustTaskScript() script, 80ExecuteScript() script, 81

FFactoryName() script, 163Figure 1-1, The Workflow Painter, 4Figure 1-2, Function Overview window, 6Figure 1-3, Map Overview Window, 6Figure 2-1, Workflow Management Group

Permissions, 14Figure 2-2, The WWork Table, 14Figure 2-3, The WSubWork Table, 15Figure 2-4, The WMapTask Table, 16Figure 2-5, Executing Callback Scripts, 22fObjectFactory feature, 163forms

creating the form task type, 165Function Window, 6

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

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

HHTML files

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

Iicons

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

Llegacy data, routing, 18ListScripts() script, 82ListTemplates() script, 82Livelink Workflow, 1

architecture, 11, 12Livelink Workflow Map, 19LoadStartTaskWorkData() script, 87, 104LoadTableValues() script, 105LoadTaskWorkData() script, 88, 106LoadWorkData() script, 88, 107

Mmanager

permissions, 4, 13workflow relationship, 3

Map Editor page. See Workflow PainterMap Overview Window, 6Milestone step, 5, 17module architecture, 25module directory structure, 26module.ini file, 28

NNewPerformer() script, 38, 73

Page 228: Live Link

Request Handlers

Index 213

OOScript

Livelink Workflow architecture, 12workflow status, 13

OSpacecreating new, 26

overview, 1extending Livelink Workflow, 9Map Overview window, 6

Ppackage types, 85packages, work, 2Painter. See Workflow PainterPalette. See Step Icon Paletteparticipant, workflow relationship, 3paths, workflow, 2performer event trigger scripts, 149PerformerCallbackScripts object, 149pFlags, 16projectpane.html, 126properties

Function Overview window, 6PutMapData() script, 35, 52, 90, 114PutReviewData() script, 74PutSubmapData() script, 92PutSubMapData() script, 121

Qquery string

setting up a custom module, 28

RReadyTaskForInitiation() script, 33, 43ReassignStep() script, 75redirect.html, 78relationships, workflow, 3RemoveWorkData() script, 88, 107request handlers

Configure, 28RequestHandlerGroup object, 28

RequestHandlerGroup object, 28roles. See workflow relationshipsroutes, workflow, 2

SSaveData() script, 92, 122SaveTableValues() script, 108

SaveWorkData() script, 88, 109schema, adding, 95scripts

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

Page 229: Live Link

Request Handlers

214 Developer’s Guide for Extending Livelink Workflow

UpdateSubWorkData(), 111UpdateTableValues(), 112

SetReviewData() script, 88, 109SetSubWorkData() script, 88, 110SetSubWorkReturnData() script, 88, 110SetTaskDefaults() script, 33, 45SetTaskRecFromMap() script, 33SetTaskRecFromMapTask() script, 46StandardTasks object

adding task types, 32Start step, 5, 16StartWF() script, 134, 138status

Detailed Status page, 7Workflow Status page, 7

status and displayadding task types, 36

Step Icon Palette, 5storage

workflow management information, 14submap event trigger scripts, 154, 156Submap step, 5, 17submap_tablevalues.html, 123SubmapCallbackScripts object, 154, 156subsystems

event trigger scripts, 142subworkflow() script, 156sub-workflows on the fly

example 6, 156

Tt_tablevalues.html, 115t_user.html, 56Table Values data type

example 2, 94tablevalues.html, 126task types

adding custom scripts and templates, 83adding the Custom Display task, 39API object, 32creating the form task type, 165CustomDisplayPaint object, 48CustomDisplayWork object, 61example 1, 39Livelink Workflow architecture, 16overview, 9, 31status and display, 36Step Icon Palette, 5WFCustomScriptPkg object, 79Workflow Painter information, 34

terminology, 2

UUpdateSubWorkData() script, 111UpdateTableValues() script, 112User step, 5, 17

WWAPI

Livelink Workflow architecture, 12workflow status, 13

WAPI.MAPTASK_FLAG_AUTODONE, 16WAPI.MAPTASK_FLAG_MILESTONE, 16WAPIMAP, 20WebModule object, 27WFCustomScriptPkg object, 79, 134WFDataTypes object

adding data types, 86WFPackage object

defining the Web information, 91defining Workflow Painter information, 89

WFPTableValues objectdefining the data type’s Workflow Painter

information, 113WFTask object

defining status and display, 36defining Workflow Painter information, 34

work packageadding data types, 85components, 6definition, 2

workflowAPI, 12definition, 1, 2saving, 6terminology, 2

workflow attributesexample 4, 146

workflow events, 144workflow maps

definition, 2saving, 6storage, 19

Workflow Painter, 4Function Window, 6Map Overview Window, 6Step Icon Palette, 5

workflow paths. See workflow routesworkflow properties, 6workflow relationships, 3workflow roles. See workflow relationshipsworkflow routes, 2workflow status

Detailed Status page, 7WAPI and OScript, 13

Page 230: Live Link

Request Handlers

Index 215

Workflow Status page, 7workflow types

adding new, 133CBExecute(), 136

example 3, 135Livelink Workflow architecture, 19overview, 9, 133

WorkTableValues object, 117

Page 231: Live Link

Request Handlers

216 Developer’s Guide for Extending Livelink Workflow