Developer S Guide For Extending Livelink Workflow
Developer S Guide For Extending Livelink Workflow
describes. For information about these warranties and limitations, refer to pages iii to vi.
Livelink SDK
Copyright 1999 by Open Text Corporation. The copyright to these materials and any accompanying software is owned, without reservation, by Open Text. These materials and any accompanying software may not be copied in whole or part without the express, written permission of Open Text. Open Text Corporation is the owner of the trademarks BASIS, Change Agents, Livelink, Livelink Change Agents, Livelink Channels, Livelink Collaboration, Livelink Discussions, Livelink Enterprise Activator, Livelink Explorer, Livelink Forms, Livelink Intranet, Livelink Library, Livelink LiveReports, Livelink OnTime, Livelink Project Collaboration, Livelink Prospectors, Livelink SDK, Livelink Search, Livelink Spider, Livelink Tasks, Livelink Workflow, OnTime, and Open Text. Other trademarks and trade names in the documentation are owned by other companies and are used for product and company identification and information only. Written by Stephanie Wunder. Code samples supplied by Jeff Lang.
Contacting Us
Open Text Corporation, 185 Columbia Street West, Waterloo, Ontario N2L 5Z5 Canada (519) 888-7111 If you subscribe to our Customer Assistance Program or would like more information about the support program, contact Open Text Customer Support at support@opentext.com or (800) 540-7292 or (519) 888-9933. Our support hours are Monday through Friday, 8:30 a.m. to 8:00 p.m., EST. If you have suggestions for this publication, contact the Open Text Publications Group at pubs@opentext.com. Visit our home page at http://www.opentext.com.
ii
Warranty and Special Provisions for Australia, New Zealand, or Papua New Guinea
Express Limited Warranty
LICENSEE RIGHTS. Licensee may have the benefit of certain rights or remedies pursuant to the Trade Practices Act and similar state and territory laws in Australia or the Consumer Guarantees Act in New Zealand, in respect of which certain liability may not be excluded. LIMITED EXPRESS WARRANTY. Licensor warrants that (i) the media on which the SOFTWARE is distributed will be free from defects in materials and workmanship under normal use for a period of thirty (30) days from the date of delivery of the SOFTWARE; and (ii) unless altered or changed by someone other than Licensor, the SOFTWARE will perform substantially in accordance with the Licensors accompanying documentation for a period of thirty (30) days from the date of delivery of the SOFTWARE. LICENSEE REMEDIES. To the maximum extent permitted under applicable law, Licensors entire liability and your sole and exclusive remedy under the express limited warranty for media shall be the replacement of any defective media without charge. To the maximum extent permitted under applicable law, Licensors entire liability and your sole and exclusive remedy under the express limited warranty for performance of the SOFTWARE shall be, at Licensors option, either to (i) correct the error, (ii) help you work around or avoid the error, or (iii) refund the purchase price for the SOFTWARE which is returned to Licensor with a copy of your receipt. LIMITATION OF LIABILITY. To the maximum extent permitted by applicable law, any conditions or warranties imposed or implied by law are hereby excluded. Licensee may nevertheless have the benefit of certain rights or remedies pursuant to the Trade Practices Act and similar state and territory laws in Australia or the Consumer Guarantees Act in New Zealand in respect of which liability may not be excluded. Insofar as such liability may not be excluded then to the maximum extent permitted by law, such liability is limited, at the exclusive option of the Licensor, to either (a) replacement of the SOFTWARE; or (b) correction of defects in the SOFTWARE; or (c) payment of the cost of having defects in SOFTWARE repaired. EXCLUSION OF LIABILITY/DAMAGES. The following is without prejudice to any rights you may have at law which cannot legally be excluded or restricted. You acknowledge that no promise, representation, warranty or undertaking has been made or given by Licensor (or related company of Licensor) to any person or company on its behalf in relation to the profitability of or any other consequences or benefits to be obtained from the delivery or use of the SOFTWARE and any accompanying manuals or written materials. You have relied upon your own skill and judgment in deciding to acquire the SOFTWARE and any accompanying manuals and written materials for use by you. Except as and to the extent provided in this Agreement, Licensor (or related company of Licensor) will not in any circumstances be liable for any other damages whosoever (including, without limitation, damages for loss of business, business interruption, loss of business information, or other indirect or consequential loss) arising out of the use or inability to use or supply or non-supply of the SOFTWARE and any accompanying written materials. Licensors (or related companys) total liability under any provisions of this Agreement is in any case limited to the amount actually paid by you for the SOFTWARE. This Agreement is governed by the laws of New South Wales, Australia or, where supplies are made in New Zealand, by the laws of New Zealand.
iii
11/97
11/97
iv
Special Provisions
Reverse Engineering: If you acquired the SOFTWARE in the European Community, you may not reverse engineer, decompile, or disassemble the SOFTWARE except to the extent and for the express purposes authorized by applicable law. This EULA is governed by the laws of England and Wales.
11/97
Warranty and Special Provisions for the United States of America or Any Other Country
Limited Warranty
LIMITED WARRANTY. Licensor warrants that (i) the media on which the SOFTWARE is distributed will be free from defects in materials and workmanship under normal use for a period of thirty (30) days from the date of delivery of the SOFTWARE; and (ii) unless altered or changed by someone other than Licensor, the SOFTWARE will perform substantially in accordance with the Licensors accompanying documentation for a period of thirty (30) days from the date of delivery of the SOFTWARE. Any implied warranties on the SOFTWARE are limited to thirty (30) days. Some states/jurisdictions do not allow limitations on duration of an implied warranty, so the above limitation may not apply to you. LICENSEE REMEDIES. Licensors entire liability and your sole and exclusive remedy for media warranty shall be the replacement of any defective media without charge. Licensors entire liability and your sole and exclusive remedy for performance of the SOFTWARE shall be, at the Licensors option, either to (i) correct the error, (ii) help you work around or avoid the error, or (iii) refund the purchase price for the SOFTWARE which is returned to Licensor with a copy of your receipt. This Limited Warranty is void if failure of the SOFTWARE has resulted from accident, abuse, or misapplication. Any replacement SOFTWARE will be warranted for the remainder of the original warranty period. NO OTHER WARRANTIES. To the maximum extent permitted by applicable law, Licensor and its suppliers disclaim all other warranties, either express or implied, including, but not limited to implied warranties of merchantability and fitness for a particular purpose, with regard to the SOFTWARE and the accompanying written materials. This limited warranty gives you specific legal rights. You may have others which vary from state/jurisdiction to state/jurisdiction. NO LIABILITY FOR CONSEQUENTIAL DAMAGES. To the maximum extent permitted by applicable law, in no event shall Licensors (or related companies) be liable for any damages whatsoever (including without limitation, special, incidental, consequential, or indirect damages for personal injury, loss of business profits, business interruption, loss of business information, or any other pecuniary loss) arising out of the use of or inability to use this product, even if Licensor has been advised of the possibility of such damages. In any case, the Licensors (or related companys) entire liability under any provision of this Agreement shall be limited to the amount actually paid by you for the SOFTWARE. Because some states/jurisdictions do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you.
Special Provisions
Export Restrictions
The Export Administration Regulations of the United States prohibit the export from the United States or Canada of technical data relating to certain commodities. Licensee hereby agrees to comply with the Export Administration Regulations of the United States as and hereby gives to Licensor the assurances required in the Export Administration Regulations.
11/97
vi
vii
Items
Convention
These items appear in quotation marks. References to chapters and sections of Examples: documents, and citations For more information, see Chapter Three, Projects, in the of messages displayed to Livelink QuickStart guide. users For more information, see Item Types in Chapter Five, Livelink Items. For more information, see Item Types, page 150. If the import completes successfully, Oracle displays the message Database import completed without errors. These items appear in monospaced font. Examples: In the User Name field, type Admin. At the operating system prompt, type start-llserver, and then press ENTER. When searching for users, you can set the maximum number of users displayed per page by setting the value (default is 30) of the MaxUsersToListPerPage parameter in the [general] section of the opentext.ini file. Key names appear in ALL CAPS. Examples: Press ENTER to start a new line when typing in this field. To select multiple items, hold down the CTRL key while you click the items that you want to select.
Text typed by users, operating system commands, code examples, feature names, method names, and object names
Key names
viii
Table of Contents
Chapter One .................................................................................................................................................................................... ....................................................................................................................................................1 Introduction .................................................................................................................................................................................... 1 Livelink Workflow Terminology ......................................................................................................................................2 Workflows, Workflow Routes, and Work Packages .........................................................................................2 Workflow Relationships.............................................................................................................................................3 The Workflow Painter.................................................................................................................................................4 Workflow Status ...........................................................................................................................................................7 Extending Livelink Workflow ...........................................................................................................................................9
Chapter Two Module.............................................................................................................................................. ................................................................................................................................ The Livelink Workflow Module .............................................................................................................................................. 11 Livelink Workflow Architecture .................................................................................................................................... 12 WAPI .............................................................................................................................................................................. 12 OScript .......................................................................................................................................................................... 12 Using WAPI and OScript for Workflow Management and Status............................................................ 13 Understanding Task Types in Livelink Workflow ................................................................................................... 16 Understanding Data Types in Livelink Workflow................................................................................................... 18 Understanding Workflow Types in Livelink Workflow......................................................................................... 19 Understanding Callback Events in Livelink Workflow.......................................................................................... 21 Event Trigger Scripts................................................................................................................................................ 23
Chapter Three Module................................................................................................................................................. ................................................................................................................................ Setting Up a Custom Module................................................................................................................................................. 25 Setting up the Custmod Module ................................................................................................................................. 26 Creating the Module's Directory Structure ..................................................................................................... 26 Creating an OSpace ................................................................................................................................................. 26 Configuring the Module......................................................................................................................................... 27 Orphaning the Configure Request Handler Object...................................................................................... 28 Setting Up the Query String and the module.ini File .................................................................................. 28 Orphaning a RequestHandlerGroup Object ................................................................................................... 28 Completing the Custmod Module .............................................................................................................................. 30
ix
Chapter Four Types........................................................................................................................................................... ................................................................................................................................ Adding New Task Types ........................................................................................................................................................... 31 Defining the Task Types API Object........................................................................................................................... 32 Defining the Workflow Painter Information ............................................................................................................ 34 Defining the Status and Display of the Task............................................................................................................ 36 Example 1: Adding the Custom Display Task Type ............................................................................................... 39 The CustomDisplayAPI Object ............................................................................................................................. 39 CreateReviewMapRec().................................................................................................................................. 40 GetPainterInfo() ................................................................................................................................................ 43 ReadyTaskForInitiation()................................................................................................................................ 43 SetTaskDefaults().............................................................................................................................................. 45 SetTaskRecFromMapTask()........................................................................................................................... 46 The CustomDisplayPaint Object ......................................................................................................................... 48 GetMapData() .................................................................................................................................................... 48 PutMapData() .................................................................................................................................................... 52 t_user.html ......................................................................................................................................................... 56 The CustomDisplayWork object.......................................................................................................................... 61 GetDisplayPerformerInfo()............................................................................................................................ 62 GetPainterInfo() ................................................................................................................................................ 63 GetPainterMenu()............................................................................................................................................. 64 GetStatusDisplay() ........................................................................................................................................... 66 GetTaskEditData() ............................................................................................................................................ 69 GetTaskGif()........................................................................................................................................................ 72 NewPerformer() ................................................................................................................................................ 73 PutReviewData()............................................................................................................................................... 74 ReassignStep()................................................................................................................................................... 75 redirect.html ...................................................................................................................................................... 78 The WFCustomScriptPkg Object......................................................................................................................... 79 CBExecute() ........................................................................................................................................................ 80 ExecuteCustTaskScript() ................................................................................................................................ 80 ExecuteScript() .................................................................................................................................................. 81 ListScripts() ......................................................................................................................................................... 82 ListTemplates().................................................................................................................................................. 82 Adding Custom Scripts and Templates ............................................................................................................ 83
Chapter Five ................................................................................................................................ .......................................................................................................................... Adding New Data Types .......................................................................................................................................................... 85 Defining the Data Types API Object .......................................................................................................................... 86 Defining the Data Types Workflow Painter Information.................................................................................... 89 Defining the Data Types Web Object........................................................................................................................ 91 Example 2: Adding a New Data Type ......................................................................................................................... 94 Creating the Database Tables .............................................................................................................................. 95 cust_sql() ............................................................................................................................................................. 97 cust_drop() ......................................................................................................................................................... 98 Creating a Utility Script........................................................................................................................................... 98 SetSubPaneIndexArg...................................................................................................................................... 99 Defining the Data Types API Object.................................................................................................................. 99 CheckTaskDone() ...........................................................................................................................................101 CreateNewInstance() ....................................................................................................................................102 CreateWorkData() ..........................................................................................................................................103 DeleteWorkData() ..........................................................................................................................................103 LoadStartTaskWorkData() ...........................................................................................................................104 LoadTableValues() .........................................................................................................................................105 LoadTaskWorkData().....................................................................................................................................106 LoadWorkData()..............................................................................................................................................107 RemoveWorkData() .......................................................................................................................................107 SaveTableValues() ..........................................................................................................................................108 SaveWorkData() ..............................................................................................................................................109 SetReviewData() .............................................................................................................................................109 SetSubWorkData() .........................................................................................................................................110 SetSubWorkReturnData()............................................................................................................................110 UpdateSubWorkData().................................................................................................................................111 UpdateTableValues().....................................................................................................................................112 Defining the Data Types Workflow Painter Information .........................................................................113 GetMapData() ..................................................................................................................................................114 PutMapData() ..................................................................................................................................................114 t_tablevalues.html.........................................................................................................................................115 Defining the Data Types Web Object.............................................................................................................117 GetData() ...........................................................................................................................................................118 GetSubMapData() ..........................................................................................................................................119 GetTabInfo() .....................................................................................................................................................120 PutSubMapData()...........................................................................................................................................121 SaveData().........................................................................................................................................................122
xi
Chapter Six ..............................................................................................................................................133 .............................................................................................................. Adding New Workflow Types .............................................................................................................................................. 133 Creating Workflow Types .............................................................................................................................................134 Example 3: Adding a Custom Workflow Type to the custmod Module ......................................................135 CBExecute() ...............................................................................................................................................................136 GetWFTypeName().................................................................................................................................................137 GetWFTypes() ...........................................................................................................................................................138 StartWF() ....................................................................................................................................................................138
Chapter Seven ...............................................................................................................................................141 ............................................................................................................... Adding Event Trigger Scripts ............................................................................................................................................... 141 Choosing Event Trigger Scripts ..................................................................................................................................142 Creating General Event Trigger Scripts ...................................................................................................................144 Example 4: Updating Workflow Attributes ............................................................................................................146 ChangeAttribute() ..................................................................................................................................................146 Creating Performer Event Trigger Scripts...............................................................................................................149 Example 5: Assigning Steps to Workflow Participants.......................................................................................151 ChooseUser() ............................................................................................................................................................151 Creating Submap Event Trigger Scripts ..................................................................................................................154 Example 6: Defining Sub-Workflows On-The-Fly..................................................................................................156 subworkflow() ..........................................................................................................................................................156 Restricting Access to Event Trigger Scripts............................................................................................................163
Appendix A ...............................................................................................................................................165 ............................................................................................................... Creating the Form Task Type ............................................................................................................................................... 165 The FormTaskAPI Object...............................................................................................................................................166 GetPainterInfo().......................................................................................................................................................166 ReadyTaskForInitiation() ......................................................................................................................................167 SetTaskDefaults() ....................................................................................................................................................168 SetTaskRecFromMapTask() .................................................................................................................................169 The FormTaskPaint Object...........................................................................................................................................171 GetMapData()...........................................................................................................................................................171 PutMapData() ...........................................................................................................................................................175
xii
formtask.html...........................................................................................................................................................177 The FormTaskWork Object...........................................................................................................................................181 GetDisplayPerformerInfo() ..................................................................................................................................182 GetPainterInfo().......................................................................................................................................................182 GetPainterMenu() ...................................................................................................................................................184 GetStatusDisplay()..................................................................................................................................................186 GetTaskEditData()...................................................................................................................................................189 NewPerformer().......................................................................................................................................................192 ReassignStep() .........................................................................................................................................................192 Request Handlers ............................................................................................................................................................195 FormEditSetPrototype()...................................................................................................................................197 FormEditExecute() .............................................................................................................................................197 FormEditStartSetPrototype() .........................................................................................................................201 FormEditStartExecute()....................................................................................................................................201 SaveFormSetPrototype().................................................................................................................................204 SaveFormExecute()............................................................................................................................................204 SaveFormStartExecute() ..................................................................................................................................207 ............................................................................................................................................................................................. .............................................................................................................................................................211 Index ............................................................................................................................................................................................. 211
xiii
xiv
Chapter One
Introduction
Livelink Workflow is a preinstalled, optional Livelink module that lets you create and manage workflows in Livelink. Workflows are business processes that follow sequential pathsfrom start to completion. You create workflows to automate procedures that have a series of defined steps. For example, you can create a workflow that runs each time you submit an expense report. In this case, Livelink can route the document to a manager or supervisor for approval, and then to your companys Accounting department for payment. The workflows you create can be simple or complex, depending on the tasks that you want to automate. You can extend Livelinks workflow functionality by incorporating new task types, data types, workflow types, and event trigger scripts with the existing Livelink Workflow module. When you extend Livelink's workflow functionality in this way, the new task types, data types, workflow types, and event trigger scripts are stored in a separate, custom module. This simplifies installation and reduces the possibility of conflict when you upgrade Livelink or the Livelink Workflow module. For information about creating a separate, custom module, see Setting Up a Custom Module, page 25. Note Do not extend Livelink's workflow functionality by altering the original Livelink Workflow module. If you do, future upgrades to Livelink or the Livelink Workflow module will overwrite your changes. In this chapter, you will learn about: Workflows, workflow maps, and work packages Workflow relationships, tasks, and responsibilities The Workflow Painter
Workflow Relationships
Workflow relationships define the responsibilities and capabilities that particular Livelink users and groups have in a workflow. There are four workflow relationships that Livelink users have: creator, initiator, participant, and manager.
Table 1-1: Workflow Relationships Relationship Creator Initiator Participant Manager Description Adds the workflow map to Livelink, paints it, and defines its work package Initiates the workflow map, starting a workflow process Works on the components of the work package and completes tasks Monitors the status of a workflow and usually has the authority to modify, suspend, resume, or stop a workflow after it is initiated
The creator of a workflow map assigns tasks to other participants by defining the work package and work route. Workflow tasks are displayed on the Tasks page in a workflow participants Personal Workspace. If you are responsible for completing the first task in a workflow, the task is displayed on your Tasks page when the workflow is initiated. If you are responsible for completing a task that occurs later in the workflow, the task is displayed on your Tasks page when the task that precedes it in the workflow route is complete. The initiator of a workflow is the Livelink user that starts the workflow process (usually by clicking the workflow name in Livelink). If the creator of the workflow map selects the Display at Initiation check box on the Start Step Definition page when creating the workflow map, the workflows work package is routed to the initiator's task list before the first task is displayed on the first workflow participants Tasks page. This lets the initiator of the workflow modify the work package (for example, add attachments or edit attributes) before routing the first task to the assigned participant. If the creator of the workflow map does not select the Display at Initiation check box on the Start Step Definition page when creating the workflow map, the first task in the workflow is routed to the assigned participant immediately. Workflow participants are responsible for reviewing the work package and completing the tasks that are listed on the Tasks page in their Personal Workspaces. When a workflow participant completes a task, Livelink routes the work package to the participant who is responsible for completing the next task in the sequence. The manager of a workflow monitors the workflows progress, confirms that milestones are met, and makes adjustments to the work package when necessary. If you are the manager of an active workflow, you can click Workflow Status on the Go to menu to monitor the workflows status. The Workflow Status page displays the titles of all the active workflows for which you have responsibility, the status of the active workflows (OK, Step Late, Workflow Late, Suspended, Stopped, or Completed), the name of the
Introduction
workflow participant that is assigned to the current task, the workflows due date and time, and the relationship you have in the workflow. You can only specify one Livelink user or group as the manager of a workflow; however, you can establish different permission levels within a single management group. This means that the creator of a workflow map can specify a group of Livelink users as the manager of the workflow, and can then expand that group and assign different permission levels to the base group members and the workflow initiator. This means that some members of the workflow management group may view the detailed workflow status while others may not. Other members may have permissions to suspend, stop, or delete workflows but may not have permission to change the data in the work package. Note For more information about the Workflow Status page, see Workflow Status, page 7.
Note For more information about the icons in the Step Icon Palette and the tasks that are associated with those icons, see the Livelink online help.
Introduction
Function Window
By default, the Function window lets you edit the properties of a workflow map, save the workflow map, or initiate the workflow map.
When you create a workflow map in the Workflow Painter, you define a work package that contains the data that the workflow participants require to complete their task assignments. The work package can include attachments, attributes, comments, or forms. You can define the work package by setting the properties of the workflow map. You can: Define general information associated with the workflow map, such as its title, description, and due date. Define the manager or management group responsible for monitoring and updating the status and execution of the workflow map. Select documents and other Livelink items (for example, queries, URLs, forms, and spreadsheets) to include as part of the work package. Define attributes that store information as a means of tracking and managing the work process and that prompt for user input.
The Function Overview window also lets you save and initiate workflows. If you want to reuse a workflow map that you create in the Workflow Painter, you can save it to a Livelink location for which you have the Modify permission. If you do not want to reuse a workflow map, you can initiate it and then discard it by closing the Workflow Painter (that is, leaving the Map Editor page).
The modifications that you make to a workflow map in the Workflow Painter are dynamically updated in the Map Overview window. This window is especially useful for large maps that cannot be displayed on a single screen in the Workflow Painter. Note For more information about creating and using workflows in Livelink, see the Livelink online help.
Workflow Status
If you are the manager or initiator of a workflow, you can monitor the status of the workflow after it is initiated in Livelink. The Workflow Status page displays the title, status, step information, due date, and relationship associated with the active workflows in Livelink. The Workflow Status page is displayed when you click Workflow Status in the Go to menu.
Table 1-3: The Workflow Status Page Status Title Info/Status Step Due Date Relationship Description The name assigned to the workflow at initiation. You can click this title to display detailed workflow status. Status information about the workflow. Valid values include OK, Step Late, Workflow Late, Suspended, Stopped, or Completed. The name of the Livelink user or group that is assigned to the active task in the workflow The date and time by which the active task should be complete. If a due date is not specified for a workflow, this column is set to No Due Date. Your relationship to the active workflow. This value is set to Manage or Initiated, depending on the option you click in the Show list.
You can adjust the display of workflows on the Workflow Status page by clicking All, Initiated, or Managed in the Show list. If you click All, all of the active workflows in Livelink are displayed. If you click Initiated, only the workflows that you have initiated are displayed on the Workflow Status page. If you click Managed, only the active workflows that you manage are displayed on the Workflow Status page. You can also choose to display workflows that are archived, not archived, or currently executing. You can view more detailed workflow status by clicking the workflow title on the Workflow Status page. The Detailed Status page contains five tabs, each describing the status of the active workflow in more detail.
Table 1-4: The Detailed Status Page Status Tab General Description Displays the workflow title, the date on which the workflow was initiated, the Livelink user name of the initiator, the due date, the status, and the date on which the workflow was completed. The General tab also displays the Stop and Suspend buttons, which let you stop or suspend the execution of the workflow.
Introduction
Table 1-4: The Detailed Status Page Status Tab Map View Step List Description Displays a non-editable, graphical representation of the workflow map. For more information about the Map View tab, see the Livelink online help. Displays the names of the steps in the workflow, the name of the performer of each step, the due date for each step, and the date on which the step is completed. If a step has not yet been completed, it is set to <Awaiting Completion>. Note You can view detailed information about each step by clicking the step name. This displays the Step Detail page which lets you reassign a task to another Livelink user or group, if appropriate. Lists all events that occurred during the execution of the workflow, including the date, time, and name of the Livelink user or group that performed the events Displays links to all of the items that are attached to the workflow. You can also add attachments to the workflow from the Attachments page. Displays all of the attributes that are defined for the workflow. If you have not defined attributes for the workflow, this tab is not displayed on the Detailed Status page.
Audit
Attachments Attributes
One way to extend the functionality of Livelink workflows is to add new task types to the Step Icon Palette. New task types let Livelink users add custom tasks to the workflow maps that they create in Livelink. You add a new task type to Livelink when you want to customize the interface and operations presented to workflow participants for particular types of tasks in a workflow. When you add a new task type, you design the interface that is presented to the performer of a workflow task and you define the custom functionality that the new interface provides. You can create task types that require workflow participants to perform automated tasks, such as filling out a form or sending a file to the printer or fax machine. Then, when the new task becomes ready in an active workflow, a custom task is displayed on the corresponding workflow participants Tasks page. For more information about adding new task types, see Adding New Task Types, page 31. You add new data types to Livelink if you want to include new types of information in the work packages that accompany your workflows. By default, work packages can contain three data types: attachments, attributes, and comments; however, you can create a data type for any new type of information that you want to add to a work package. Adding new data types to Livelink also lets you customize the Livelink interface. For example, if the work packages that you include with your workflows usually contain many attributes, you can create a data type that creates the attributes and groups them on tabs in the Livelink interface. This makes it easy for workflow participants to locate and use the attributes in the work package. For more information about data types, see Adding New Data Types, page 85. You can also extend the functionality of Livelink workflows by adding new workflow types to Livelink. You add new workflow types when you want to apply a particular set of custom operations to different workflow map definitions. Adding new workflow types lets you define the set of custom operations that are applied to a workflow map definition when the creator of a workflow map chooses to create that particular type of workflow in Livelink. These custom operations can include adding workflow tasks, modifying the duration or name of existing workflow tasks, adding callback scripts to workflow events, or any other modification that you want to make to a workflow map definition. For more information about adding new types of workflows, see Adding New Workflow Types, page 133.
Introduction
Another way to extend the functionality of Livelink workflows is to create custom event trigger scripts. Event trigger scripts automatically perform an operation when a specific workflow event occurs. For example, if you want to save a workflow attachment as a new version of an existing Livelink document when the workflow is complete, you can create an event trigger script that automatically saves the item for you. You can also create event trigger scripts that monitor the work load of all workflow participants and assign a particular workflow task to the participant with the least amount of tasks. In fact, you can create event trigger scripts that perform any operation when a specific workflow event occurs. For more information about adding event trigger scripts, see Adding Event Trigger Scripts, page 141.
10
Chapter Two
11
WAPI
WAPI is an application programming interface that maintains a database of workflow maps (maps) and work packages (work). Workflow maps define specific tasks and the links that join those tasks in a workflow. Work is created when a workflow map is initiated in Livelink and defines the work package that is routed through the workflow. WAPI also provides functions that you can use to query the database of workflow maps and work packages. WAPI is responsible for most of the database interaction involved in the creation and execution of Livelink workflows, including the maintenance of basic workflow information such as titles, due dates, status, and to-do lists. WAPI is also responsible for maintaining a complete audit trail.
OScript
The OScript programming language is an object-oriented scripting language that lets you develop and customize applications using the Livelink Builder. In Livelink, OScript is used to control the graphical user interface (GUI) for painting workflow maps and displaying to-do lists and task information to workflow participants. OScript is used along with Livelinks document management functionality to store workflow maps. It stores workflow maps as document nodes that can manipulate Livelinks extensive document management functionality (including permissions, version control, and attributes). Additionally, OScript almost completely handles the flow of data as it is routed through a workflow. Note For information about the Workflow API (WAPI) built-in package which is available in OScript, see the Livelink Developers Documentation.
12
13
Whether the manager of a workflow is an individual Livelink user or a group of Livelink users, they are concerned with the due dates and status of the workflows that they manage. In Livelink, WAPI is responsible for calculating and maintaining all of the due dates and completed dates for a workflow; however, WAPI is only aware of workflow status in terms of Suspended, Stopped, Archived, and so on. OScript is responsible for defining the more detailed workflow status that is presented to workflow managers. Detailed status levels are determined by analyzing the due dates for a workflow and the tasks it contains. Tip Because detailed status levels are determined by OScript, OScript programmers can change the definition of the status levels or add new levels just by overriding the script that defines them.
The rows in the WWork table contain specific workflow management information, such as due dates, milestone dates, permission settings, and status. For example, the WORK_DATECOMPLETED row, contains the date on which the workflow was completed.
14
Workflow management and status information for sub-workflows is stored in a table named WSubWork in the Livelink database.
The rows in the WSubWork table contain specific workflow management information, such as due dates, milestone dates, permission settings, and status for sub-workflows. For example, the SUBWORK_DATECOMPLETED row, contains the date on which the subworkflow was completed.
15
The WAPI.MAPTASK_FLAG_AUTODONE flag is a constant value that tells WAPI that the current task does not require user interaction and can be marked finished as soon as it becomes ready. The WAPI.MAPTASK_FLAG_MILESTONE flag is a constant value that indicates to WAPI that the task should be marked as a Milestone step and that milestone dates should be calculated and stored. This flag also stores the milestone date (that is, the date by which the task is scheduled to be complete).
Table 2-1: Standard Workflow Task Types Task Type Start Description A preliminary task type which lets the initiator of a workflow add data to the work package or cancel the workflow before it is routed to the first workflow participant. The Start task type is not included in the WAPIMAP database table as a step in the workflow route. Note For more information about the WAPIMAP database table, see Understanding Workflow Types in Livelink Workflow, page 19.
16
Table 2-1: Standard Workflow Task Types Task Type User and Initiator Description Workflow task types which provide the main user interface into the data in the workflow. The only difference between the User and Initiator task types is that Initiator tasks use Performer event trigger scripts to assign the task to the Livelink user that initiated the workflow when the step becomes ready. The creator of a workflow map determines which Livelink user is assigned to a User task. Note The User and Initiator task types do not set the WAPI.MAPTASK_FLAG_AUTODONE or WAPI.MAPTASK_FLAG_MILESTONE flags. A workflow task type which sets the WAPI.MAPTASK_FLAG_AUTODONE flag and enables a special callback script. This callback script tells WAPI whether Livelink will take the TRUE route, the FALSE route, or the LOOPBACK route to continue the workflow. The Evaluate task type asks the workflow's data types to return their data, and then compares the data with the evaluation criteria stored for the task type to determine which route to take. A workflow task type which sets the WAPI.MAPTASK_FLAG_AUTODONE and WAPI.MAPTASK_FLAG_MILESTONE flags. When the workflow reaches a Milestone task type, the task is marked finished as soon as it becomes ready. This task tells WAPI to store and calculate a milestone date, by which the task is scheduled to be complete. A workflow task type which uses a Submap callback script to start a subworkflow. The task type lets the creator of a workflow map choose a second workflow map from the Livelink database. This workflow map is initiated as a sub-workflow of the main workflow map. When a Submap task is reached, a Submap callback script locates the selected sub-workflow map node in Livelink, reads the sub-workflow map definition, stores the definition in WAPI, and initiates it as a subworkflow process. Callback scripts are also used to set up the data that is passed into the sub-workflow from the main workflow and the data that is returned to the main workflow when the sub-workflow finishes. Note This task type does not set the WAPI.MAPTASK_FLAG_AUTODONE or the WAPI.MAPTASK_FLAG_MILESTONE flags.
Evaluate
Milestone
Submap
Many of the task types that you define in a workflow map use callback scripts to perform their operations. For example, the One Level Expand and Full Expand options in the Group Options list (General tab) for the User or Initiator task type, use callback scripts to determine which workflow participants must work on the corresponding User or Initiator task. These options use a callback script to launch a sub-workflow that assigns the task to the appropriate members of the group (depending on the option that you choose). The Member Accept option does not use a callback script to perform its function. It leaves the task assigned to a group. Livelink knows that when a task is assigned to an entire group, one member of the group must accept the task before work on the task can begin.
17
18
19
The WAPIMAP
When a workflow map is initiated, the Livelink Workflow Map definition is used to create the WAPIMAP. The WAPIMAP stores the routing information that is used by WAPI to control the workflow route. This map definition is stored in the WMap and WMapTask database tables and is used throughout the life of the workflow. At the time of initiation, the Livelink Workflow Map and the WAPIMAP are completely separateany changes made to the painted Livelink Workflow Map do not affect the active workflows that were initiated from the Livelink Workflow Map definition. This means that if you change the route defined in a workflow map definition, the changes do not affect the route of those workflows that have already been initiated. Similarly, any changes made to a specific workflows WAPIMAP after initiation do not affect the Livelink Workflow Map.
20
21
When Livelink encounters callback event data in a WAPI database column, it calls a script that is stored in the WAPISESSION object. This script examines the workflow event that triggered the callback event (for example, the step becoming ready), and then calls another script named GenericCB(). Note The GenericCB() script is not called if the callback event was triggered when a workflow participant added a row to an audit trail.
22
The GenericCB() script examines the callback event data that is stored in the corresponding column of the WAPI database table. This data must be formatted as a list of lists, where each sub-list contains two elements: an integer and the additional data that is required to execute the callback event. The GenericCB() script examines the integer element and does one of two things (based on its value). If the integer is a value between 1 and 99, a standard workflow operation is performed. If the integer is a value outside of this range, the GenericCB() script calls another script named CustomCB(), indicating that a custom callback event is present in the Livelink workflow. The CustomCB() script begins loading all of the custom objects that are registered in WAPI and calling their CBExecute() scripts. When the CustomCB() script calls a CBExecute() script that matches the value of the integer associated with the callback event, it runs that CBExecute() script and returns an Assoc. The CBExecute() script performs the custom operations associated with the workflow step. Note One of the fields in the Assoc is called Handled. If Handled is set to TRUE, it indicates that the operation is being handled or performed. If Handled is set to FALSE, the process continues until the operation can be handled or performed.
23
24
Chapter Three
25
Note You can also create an optional directory named help in your /custmod_1_0_0 directory. The /help directory is used to store the HTML help files you may want to include with your custom module.
Creating an OSpace
After you create the directory structure for your custom module, you can create the OSpace that will contain the functionality of the module. All OSpaces have the .oll file extension. To create the custmod OSpace: 1. In the Livelink Builder, click New OSpace on the OSpace menu. 2. Name the OSpace custmod.oll, and save it in your custom module's /ospace directory (for example, c:/opentext/staging/custmod_1_0_0/ospace).
26
27
28
You can now start your Livelink Intranet service and install your custom module in Livelink. For more information about installing modules, see the Livelink Installation Guide. Tip You can set the value of the changeStateOspaces variable in the WebBuilder.lxe file to CUSTMOD (all uppercase letters), to open the custmod OSpace in an unlocked state in the Livelink Builder. The WebBuilder.lxe file is stored in the main directory of your primary Livelink installation (for example, c:/opentext).
29
Each of these chapters provides you with general information for incorporating new workflow functionality. This general information is supported by a specific example of how to apply the functionalitycomplete with code samples. Tip Because the examples in each chapter can become quite long and complex, consider cutting and pasting the code samples into the scripts you create in Livelink Builder, when working on the examples. Typing the code samples manually is very time-consuming.
30
Chapter Four
required by the Workflow Painter to add the task to a workflow map. The WebWork:WebWorkRoot:WFTask object controls the task when a workflow participant is working on it and when it is displayed on the Detailed Status page in Livelink. In this chapter, you will learn how to: Define a task types API object, Workflow Painter information, and status information Add a new task type to your custom module
31
Description Stores a unique integer value that works with the fType feature to identify the object. The following objects also contain fSubType features: The WFTask object, which is orphaned when you define the Workflow Painter information The WFTask object, which is orphaned when you define the status and display of the task The values of the fSubType features for the WFTask objects must be the same as the value of this fSubType feature. Stores the name of the task. By default, this value is displayed when you position your cursor over the tasks icon in the Workflow Painter. Note For more information about the Step Icon Palette, see The Workflow Painter, in Chapter One, Introduction. Stores a unique integer value that works with the fSubType feature to identify the object. The following objects also contain fType features: The WFTask object, which is orphaned when you define the Workflow Painter information The WFTask object, which is orphaned when you define the status and display of the task The values of the fType features for the WFTask objects must be the same as the value of this fType feature.
fTaskName
fType
Note When adding task types, Open Text recommends setting the fSubType values to 1 and the fType values to an integer higher than 10.
32
For each task type that you create, you can modify the following scripts.
Table 4-2: Scripts Associated With the StandardTasks Object Script
GetPainterInfo()
Description Retrieves the text that is displayed as a title below the task icon in the Workflow Painter. The default value is the title of the task, as specified in the fTaskName feature. If you do not want to display the title of the task in the Workflow Painter, you can modify this script. Note In most cases, you will not modify the GetPainterInfo() script. Stores callback scripts and any additional data that is required by the task type throughout the execution of a workflow. This script is called when the workflow is initiated. You can use this script to store the type, subtype, and permission information for the task type in the MAPTASK_USERDATA column of the WMapTask table. The type and subtype information must be present in the WMapTask table so that Livelink can distinguish between different types of tasks. Stores the default data that must be present before Livelink can recognize the task type (that is, the type and subtype). This script also stores the title, performer ID, and any other information that you want to appear as default values on the Step Definition page when a task of this type is edited in Livelink. You can also use this script to store task information that must be present if the creator of a workflow map does not edit the task on the Step Definition page before the workflow is initiated. Converts a workflow task that has been prepared for initiation to a workflow map definition task. This script transfers data from the WAPIMAPTASK to the correct fields in the map definition task to convert the workflow task back to a format that the Workflow Painter recognizes. Note The WAPIMAPTASK data type is used to define the object handle of a workflow map task that is stored in the WAPI database. It is used in WAPI functions for all task manipulation operations. This script is called when a workflow manager attempts to modify a workflow that has already been initiated. It undoes the changes that were made by the ReadyTaskForInitiation() script (because the ReadyTaskForInitiation() script is called again when the workflow manager saves their changes and the workflow process is resumed).
ReadyTaskForInitiation()
SetTaskDefaults()
SetTaskRecFromMap()
33
Description Stores a unique integer that works with the fType feature to identify the task type. The following objects also contain fSubType features: The StandardTasks object, which is orphaned when you define the API object for the task type The WFTask object, which is orphaned when you define the status and display of the task The values of the fSubType features for the StandardTasks object and the WFTask object must be the same as the value of this fSubType feature. Stores a unique integer that works with the fSubType feature to identify the task type. The following objects also contain fType features: The StandardTasks object, which is orphaned when you define the API object for the task The WFTask object, which is orphaned when you define the status and display of the task The values of the fType features for the StandardTasks object and the WFTask object must be the same as the value of this fType feature.
fType
Note When adding task types, Open Text recommends setting the fSubType values to 1 and the fType values to an integer higher than 10. For each task type that you create, you can modify the following scripts.
Table 4-4: Scripts Associated With the WFTask Object Script
GetMapData()
Description Tells Livelink what to display when the creator of a workflow map edits this type of task. This script identifies the location and name of the HTML file to display for the Step Definition page for this task type. Note You can maintain consistency with other Livelink task pages, by using the commonedittask.html file. This file is located in the /webwfp_8_1_x/html directory.
34
Description Saves the information that the creator of a workflow map enters on the Step Definition page when editing the task in the Workflow Painter. This data is saved in the workflow map definition.
In addition to the features and scripts that you modify to define the Workflow Painter information for a task type, you must create the HTML file for this tasks Step Definition page. You access the Step Definition page in Livelink by right-clicking a task in the Workflow Painter, and then clicking Edit or by double-clicking the task in the Workflow Painter.
35
Description Stores a Boolean value. If the value of this feature is set to TRUE, an icon representing the task type can be displayed on the Step Icon Palette in the Workflow Painter. If the value of this feature is set to FALSE, an icon representing the task type cannot be displayed on the Step Icon Palette in the Workflow Painter. Note For more information about the Step Icon Palette, see The Workflow Painter, page 4. Stores a unique integer that works with the fType feature to identify the object. The following objects also contain fSubType features: The StandardTasks object, which is orphaned when you define the API object for the task type The WFTask object, which is orphaned when you define the Workflow Painter information for the task type The values of the fSubType features for the StandardTasks object and the WFTask object must be the same as the value of this fSubType feature. Stores the name of the image that you want to represent the task on the Step Icon Palette in the Workflow Painter. This image must be stored in your modules /support directory (for example, c:/opentext/module/custmod_1_0_0/support) and in the Livelink /support directory (for example, c:/opentext/support). Stores a unique integer that works with the fSubType feature to identify the object. The following objects also contain fType features: The StandardTasks object, which is orphaned when you define the API object for the task type The WFTask object, which is orphaned when you define the Workflow Painter information for the task type The values of the fType features for the StandardTasks object and the WFTask object must be the same as the value of this fType feature.
fSubType
fTaskGif
fType
Note When adding task types, Open Text recommends setting the fSubType values to 1 and the fType values to an integer higher than 10.
36
For each task type that you create, you can modify the following scripts.
Table 4-6: Scripts Associated With the WFTask Object Script
GetDisplayPerformerInfo()
Description Examines the workflow and determines if the task has been assigned to a workflow participant. If it has, this script retrieves the name and ID of the workflow participant to whom the task has been assigned. This information is displayed when the workflow participant attempts to reassign the task in Livelink. Defines the information (for example, the name, type, subtype, icon, or module) that the Workflow Painter needs to know about the task type. This includes all of the information that the Workflow Painter requires to perform operations on the task. Returns a list of Assocs that define the menu commands that appear when you right-click this task types icon in the Workflow Painter. You can set the Boolean value of the viewonly variable to TRUE if you want to display a standard set of menu commands that do not let the creator of a workflow map edit the task type in the Workflow Painter. You can set the Boolean value of the viewonly variable to FALSE if you want to display a standard set of menu commands that let the creator of a workflow map edit the task type in the Workflow Painter. Whether you set the Boolean value of the viewonly variable to TRUE or FALSE, you can use this script to override the menu commands that are displayed in the Livelink interface as needed. Retrieves the information that is displayed when a workflow participant clicks the task name on the Step List tab or doubleclicks the task icon on the Map View tab of the Detailed Status page. This script returns Undefined (indicating that there is no information of this type relevant to the active workflow) or it returns an Assoc. If the script returns an Assoc, the fields in the Assoc must contain the information that the HTML file (specified by retval.HTMLFile) needs to be displayed in Livelink. The Assoc contains OK if the data needed for display is retrieved; otherwise, it contains a string named ErrMsg. Retrieves the information that is displayed when a workflow participant clicks the task name on the Tasks page in their Personal Workspace. This script returns Undefined (indicating that there is no information of this type relevant to the active workflow) or it returns an Assoc. If the function returns an Assoc, the fields in the Assoc must contain the information that the HTML file (specified by retval.HTMLFile) needs to be displayed in Livelink. The Assoc contains OK if the data needed for display is retrieved; otherwise, it contains a string named ErrMsg.
GetPainterInfo()
GetPainterMenu()
GetStatusDisplay()
GetTaskEditData()
37
Description Updates task information if the task is reassigned in Livelink. For example, if the performers name is also the name of the task in the Workflow Painter, this script updates the names. This script is called when this type of task is reassigned in Livelink so that all performer-dependent information is updated.
Depending on the complexity of the task types that you add to Livelink, you may have to provide additional functionality. For example, if you create a task type that allows the creators of workflow maps to attach custom callback scripts to particular workflow events that are associated with that task type, you may need to define the objects responsible for handling those callback scripts in Livelink. For more information, see The WFCustomScriptPkg Object, page 79. If you add a complex task type to the custom module that you are creating, you may need to create custom request handlers that perform the operations associated with the task type. For example, the form task type (installed with the Livelink Forms module) uses request handlers to display the data in the form, to retrieve data from the form, and to pass the data to the workflow. In this case, the request handlers set up and manipulate all of the forms data correctly. For information about creating the request handlers used by the form task type, see Request Handlers, page 195.
38
The Step Definition page for this task looks just like the User Step Definition page, but contains two new fields: the Script to run field and the Template to use field. The creator of a workflow map can choose a callback script to run for the task from the Script to run field. This callback script can perform any custom operation and can run when the task becomes ready or when the task is complete. The creator of a workflow map can also choose an HTML template file to display for the task from the Template to use field. The HTML template defines the interface that is displayed when a workflow participant works on that particular task in Livelink. The creator of a workflow map can create many different tasks of this type, and can choose different callback scripts and HTML templates for each, so that each task looks and behaves differently. This lets the creators of workflow maps customize the workflow interface and operationswithout actually changing any code using the Livelink Builder. The HTML templates and callback scripts used for this task type are stored in two new folders in your custom module directory structure: the /templates folder and the /scripts folder. The HTML templates that you store in the /templates folder can be selected from the Template to use field on a Step Definition page for this type of task. The callback scripts that you store in the /scripts folder can be selected from the Script to run field on a Step Definition page for this type of task.
39
The fType feature stores a unique integer that works with the fSubType feature to identify the object. 4. Ensure that the fTaskName feature is a String type, and set its value to Custom
Display Task.
The fTaskName feature stores the name of the task type as it is displayed in the Workflow Painter. 5. Create a script, and name it CreateReviewMapRec(). For more information about the CreateReviewMapRec() script, see CreateReviewMapRec(), on this page. 6. Override the following scripts:
GetPainterInfo() ReadyTaskForInitiation() SetTaskDefaults() SetTaskRecFromMapTask()
For more information about these scripts, see the code samples that follow. You have created the API object. Now you must customize it for the custom display task type.
CreateReviewMapRec()
The following code sample describes how to create a script that returns the workflow map definition that is used when a task is sent to a Livelink user or group for review. This workflow map definition is created on-the-fly and is initiated as a sub-workflow task.
Function Record CreateReviewMapRec( \ Object prgCtx, \ Record user, \ Integer groupFlags, \ Assoc taskData, \ RecArray packages ) Boolean expandFlag Dynamic userRecords List fields Integer i Record r Record newTask String fieldName Integer taskID = 1 Point pos = Point( 100, 50 ) //Create a generic map record that contains all of the workflow //map information. Record mapRec = $WFMain.WFMapPkg.CreateMapRec()
40
//Add a Start task and a User task to the workflow map //definition. AddNewTask( \ prgCtx, \ mapRec, \ $WFMain.WFTaskSubsystem.GetItemByName( 'WFStartTask' ), \ Undefined, \ Point( 20, 50 ) ) //Determine whether this task is assigned to a group. If it is, //determine whether the task should be assigned to each member of //the group or to the group as a whole. If the task is assigned //to the group as a whole, the first group member to accept the //task is responsible for completing it. if ( ( user.TYPE != UAPI.USER ) && ( IsSet( groupFlags ) ) && \ ( IsDefined( groupFlags ) ) && ( groupFlags != \ $WFMain.WFConst.kWFGroupStandard ) ) expandFlag = ( groupFlags == \ $WFMain.WFConst.kWFGroupExpandFull ) userRecords = $LLIAPI.UsersPkg.ExpandGroup( prgCtx, user, \ expandFlag ) if ( userRecords.OK ) userRecords = userRecords.Members else userRecords = { user } end else userRecords = { user } end if ( Length( userRecords ) < 1 ) newTask = user else newTask = userRecords[ 1 ] end //Assign a custom display task to each workflow participant to //which the information must be sent for review. newTask = AddNewTask( \ prgCtx, \ mapRec, \ $WFMain.WFTaskSubsystem.GetItemByname( \ 'CustomDisplay' ), \ newTask, \ pos ) //Set the Group Options value to Member Accept (standard). taskData.EXATTS.GroupFlags = $WFMain.WFConst.kWFGroupStandard //Copy the data fields from the original task to the new sub//workflow task. The allows the sub-workflow task to be able to //modify the data in the same way that the original task could //modify the data. fields = { 'PAINTER', 'USERDATA' }
41
for fieldName in Assoc.Keys( taskData ) if ( RecArray.IsColumn( newTask, fieldName ) && !( Str.Upper( \ fieldName ) in fields ) ) newTask.( fieldName ) = taskData.( fieldName ) end end newTask.PERFORMERID = userRecords[ 1 ].ID for i = 2 to Length( userRecords ) newTask = $LLIAPI.RecArrayPkg.CopyRec( mapRec.TASKS[ 2 ] ) //Generate a new position for the next task. pos += Point( 0, 75 ) newTask.PAINTER = { pos, newTask.PAINTER[ 2 ] } newTask.PERFORMERID = userRecords[ i ].ID RecArray.AddRecord( mapRec.TASKS, \ RecArray.GetRecord( newTask ) ) end //Add a link between the two tasks. for r in mapRec.TASKS if ( taskID > 1 ) $WFMain.WFMapPkg.AddLinkRecord( \ mapRec.TASKS, \ mapRec.LINKS, \ mapRec.TASKS[ 1 ], \ mapRec.TASKS[ taskID ], \ 0 ) end taskID += 1 end //Add the work package from the main workflow to the sub//workflow. for r in packages RecArray.AddRecord( mapRec.WORK_PACKAGES, \ RecArray.GetRecord( r ) ) end return( mapRec ) end Function Record AddNewTask( \ Object prgCtx, \ Record mapRec , \ Object taskType, \ Dynamic context, \ Point iconPos ) Dynamic val List data Record taskRec //Add a new Record to the tasks RecArray.
42
taskRec = $LLIAPI.RecArrayPkg.NewRecord( mapRec.TASKS ) taskType.SetTaskDefaults( prgCtx, taskRec, context ) val = taskType.GetPainterInfo( prgCtx, taskRec, context ) taskRec.PAINTER = { iconPos, val } return( taskRec ) end
GetPainterInfo()
The following code sample describes the default values of the GetPainterInfo() script. This is the information that the Workflow Painter needs to know about the task type.
Function Dynamic GetPainterInfo( \ Object prgCtx, \ Record task, \ Dynamic context = Undefined ) Dynamic retVal if ( IsDefined( context ) ) retVal = context.ID end return( retVal ) end
ReadyTaskForInitiation()
The following code sample describes how to prepare a custom display task for initiation in Livelink. It stores callback scripts and any other additional data that is required by the custom display task type throughout the execution of a workflow.
Function Boolean ReadyTaskForInitiation( \ Object prgCtx, \ Record r, \ RecArray workPkg ) Assoc expandInfo List cbInfo List prevCBs RecArray user Boolean initiatorStep = False Boolean success = True Object const = $WFMain.WFConst Object uSession = prgCtx.USession() Record userData = $WFMain.WFMapPkg.CreateTaskUserDataRec() userData.TYPE = r.TYPE userData.SUBTYPE = r.SUBTYPE userData.PERMFLAGS = r.USERFLAGS //Add a standard Done callback script. This callback script is //called when the task is completed and the workflow is routed to //the next workflow participant. The Done callback script //instructs all of the data types in the workflow to make a copy //of the information that was entered for the task. The data type //stores the information in a separate table so that each version //is available at the end of a workflow.
43
if ( IsDefined( r.DONECB ) ) cbInfo = { @r.DONECB, { const.kCBSetTaskDoneData, Undefined } } else cbInfo = { { const.kCBSetTaskDoneData, Undefined } } end //If the creator of the workflow map has selected a callback //script from the Script to run field on the Custom Display Step //Definition page for this task, add the callback script to the //appropriate workflow event (Step Becomes Ready or Step Is Done). if ( IsDefined( r.EXATTS.CustTaskScript ) ) if ( r.EXATTS.RunScript == 'DoneCB' ) cbInfo = { @r.DONECB, { 500, r.EXATTS.CustTaskScript } } else prevCBs = r.READYCB prevCBs = ( IsDefined( prevCBs ) ) ? prevCBs : {} prevCBs = { @prevCBs, { 500, r.EXATTS.CustTaskScript } } r.READYCB = prevCBs end end r.DONECB = cbInfo r.USERDATA = userData //If the performer of the task is a group, add a callback script //that identifies the group name. Then, if the group task is //part of a loopback, the task is reassigned to the whole group //when the route loops back (and not to the individual group //member who initially accepted the task). if ( IsDefined( r.PERFORMERID ) && ( r.PERFORMERID > 0 ) ) user = UAPI.GetByID( uSession.fSession, r.PERFORMERID ) if ( !IsError( user ) ) if ( user[ 1 ].TYPE != UAPI.USER ) prevCBs = r.PERFORMERCB prevCBs = ( IsDefined( prevCBs ) ) ? prevCBs : {} r.PERFORMERCB = { { const.kCBSetGrpStepPerformer, \ r.PERFORMERID }, @prevCBs } end end //If the performer is Undefined, add a callback script that //assigns the task to the initiator of the workflow. elseif ( r.PERFORMERID == 0 ) r.PERFORMERCB = { { const.kCBGetInitiator, Undefined } } initiatorStep = True end //If the performer of the task is a group, and that group should //be expanded so that the task is assigned to all members of the //group, add a Submap callback script. This Submap callback script //creates a sub-workflow that expands the members of the group. if ( !initiatorStep ) if ( IsSet( r.EXATTS.GroupFlags ) && \ ( r.EXATTS.GroupFlags != const.kWFGroupStandard ) ) expandInfo.Type = r.TYPE expandInfo.SubType = r.SUBTYPE expandInfo.Flag = r.EXATTS.GroupFlags
44
SetTaskDefaults()
The following code sample describes how to specify the default information required by Livelink to recognize the custom display task type. This is also where you specify data that must be present if the creator of the workflow map does not edit the task before the workflow is initiated.
Function Void SetTaskDefaults( \ Object prgCtx, \ Record taskRec, \ Dynamic context = Undefined ) //Set values for the default information required by Livelink to //recognize the custom display task type. The fType and fSubType //values store unique integers that identify the task type. taskRec.TYPE = .fType taskRec.SUBTYPE = .fSubType taskRec.EXATTS = Assoc.CreateAssoc( Assoc.NotSetValue() ) taskRec.CUSTOMDATA = Assoc.CreateAssoc( Assoc.NotSetValue() ) taskRec.FLAGS = 0 //Specify the name of the task and the performer ID associated //with the task. These values are displayed as default values on //the Custom Display Step Definition page when a custom display //task is edited for the first time. if ( !IsDefined( context ) ) taskRec.TITLE = Str.String( .fTaskName ) taskRec.PERFORMERID = Undefined else taskRec.TITLE = context.NAME taskRec.PERFORMERID = context.ID end taskRec.EXATTS.GroupFlags = $WFMain.WFConst.kWFGroupStandard //If the creator of the workflow map chose a script from the //Script to run field on the Custom Display Step Definition page //for this task, set the script to run when the task is ready, by //default. taskRec.EXATTS.RunScript = 'ReadyCB' end
45
SetTaskRecFromMapTask()
The following code sample describes how to convert a custom display task that has been prepared for initiation to a workflow map definition.
Function Void SetTaskRecFromMapTask( \ WAPIMAPTASK task, \ Record r, \ Record taskData ) List Dynamic Object Integer cbInfo data = task.pUserData const = $WFMain.WFConst groupFlag = const.kWFGroupStandard
//Convert the values stored in the WAPIMAPTASK to the //corresponding fields in the map definition task. r.TYPE = data.TYPE r.SUBTYPE = data.SUBTYPE r.USERFLAGS = data.PERMFLAGS r.SUBMAPID = task.pSubMapID r.PERFORMERID = taskData.WORK.SUBWORKTASK_PERFORMERID r.READYCB = task.pReadyCB //Remove the Done callback script. r.DONECB = task.pDoneCB r.DONECB = $WFMain.WFMapPkg.RemoveCBInfoType( r.DONECB, \ const.kCBSetTaskDoneData ) r.KILLCB = task.pKillCB //Remove the Performer callback script. r.PERFORMERCB = task.pPerformerCB r.PERFORMERCB = $WFMain.WFMapPkg.RemoveCBInfoType( \ r.PERFORMERCB, const.kCBSetGrpStepPerformer ) //If present, remove the callback script that assigns the //custom display task to the initiator of the workflow. r.PERFORMERCB = $WFMain.WFMapPkg.RemoveCBInfoType( \ r.PERFORMERCB, const.kCBGetInitiator ) r.CONDITIONCB = task.pConditionCB r.FORM = task.pForm r.PAINTER = task.pPainter r.STARTDATE = task.pStartDate r.DUEDURATION = task.pDueDuration r.DUEDATE = task.pDueDate r.DUETIME = task.pDueTime r.FLAGS = task.pFlags r.TITLE = taskData.WORK.SUBWORKTASK_TITLE r.DESCRIPTION = task.pDescription r.INSTRUCTIONS = task.pInstructions r.PRIORITY = task.pPriority
46
//Walk through the Submap callback scripts and search for a //callback script that expands group members. If this type of //callback script is found, determine whether the callback script //expands the group and its subgroups or whether the callback //script expands only the first level of group members. r.SUBMAPIDCB = task.pSubMapIdCB for cbInfo in r.SUBMAPIDCB if ( cbInfo[ 1 ] == const.kCBExpandGroup ) groupFlag = cbInfo[ 2 ].Flag break end end //Remove the callback script that expands group members and store //the expand group flag value in the appropriate field of the //r. EXATTS Assoc. r.SUBMAPIDCB = $WFMain.WFMapPkg.RemoveCBInfoType( \ r.SUBMAPIDCB, const.kCBExpandGroup ) r.EXATTS = Assoc.CreateAssoc( Assoc.NotSetValue() ) r.EXATTS.GroupFlags = groupFlag //Remove the callback scripts that were added to the Step Becomes //Ready or Step Is Done workflow events. for cbInfo in r.DONECB if ( cbInfo[ 1 ] == 500 ) r.EXATTS.CustTaskScript = cbInfo[ 2 ] r.DONECB = $WFMain.WFMapPkg.RemoveCBInfoType( r.DONECB, \ 500 ) break end end for cbInfo in r.READYCB if ( cbInfo[ 1 ] == 500 ) r.EXATTS.CustTaskScript = cbInfo[ 2 ] r.READYCB = $WFMain.WFMapPkg.RemoveCBInfoType( \ r.READYCB, 500 ) break end end end
47
For more information about these scripts, see the code samples that follow. 5. Create an HTML file, name it t_user.html, and store it in your modules /html directory (for example, c:/opentext/module/custmod_1_0_0/html). For more information about this HTML file, see t_user.html, page 56. You have created the object necessary to define a task types Workflow Painter information. Now you must provide the code required to customize the information for the custom display task type.
GetMapData()
The following code sample describes how to display the Step Definition page for this task type when the creator of a workflow map edits the task in the Workflow Painter.
function Assoc GetMapData( \ Object prgCtx, \ Integer mapID, \ Integer taskID, \ Record mapRec, \ Record request )
48
Assoc a Assoc paneData Assoc performerInfo Assoc retVal Assoc tabPaneInfo Dynamic tmp Integer whichTab List tabList Object obj Record p String label Boolean Integer Record String String String knownUser = False i = 1 taskInfo = mapRec.TASKS[ taskID ] gif = 'guy.gif' name = [WebWFP_HTMLLabel.User] objName = .OSName
whichTab = ( RecArray.IsColumn( request, 'PaneIndex' ) ) ? \ Str.StringToInteger( request.PaneIndex ) : 1 //Specify the commonedittask.html file as the HTML file to //display when the creator of a workflow map edits a custom //display task in the Workflow Painter. Specify the location of //the commonedittask.html file (that is, the webwfp module). retVal.HTMLFile = "commonedittask.html" retVal.ModuleName = 'webwfp' //Create an Assoc named retVal.Data and populate it with the task //and map information, including the ID of the workflow map, the //ID of the task, the URL of the next page to display, and the //header information for the task. retVal.Data = Assoc.CreateAssoc() retVal.Data.MapID = mapID retVal.Data.TaskID = taskID retVal.Data.NextURL = request.NextURL retVal.Data.HeaderArgs = $WebWork.WFPkg.GetHeaderTypeArgs( \ 'PAINT', $WebWork.WFPkg.GetMapName( prgCtx, mapID, \ mapRec ) ) retVal.Data.HeaderLabel = [WebWFP_HTMLLabel.StartStepDefinition] //Create an Assoc named tmp that stores all of the data required //to paint the first tab that appears when the creator of a //workflow map edits the custom display task in the Workflow //Painter (that is, the General tab). tmp = Assoc.CreateAssoc() tmp.Label = [WebWork_HTMLLabel.General] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( request ), i ) tmp.HelpKey = objName tmp.Active = FALSE //This task type uses the commonedittask.html file. This HTML //file expects to be passed a list of tab names, along with a //list of the data to be displayed on each tab. TabList is a list
49
//of Assocs that identifies each tab to be displayed. There is //another list of Assocs that lists the panes to be displayed //with each tab. This second list of Assocs contains the HTML //information and all other information that the pane needs to //draw itself. tabPaneInfo.TabList = { tmp } a = Assoc.CreateAssoc() a.TaskInfo = taskInfo a.MapID = mapID a.TaskID = taskID - 1 a.NextURL = request.NextURL //Retrieves the name of the performer of the task and a .gif file //that represents the performer type (that is, a user or a //group). If the step is assigned to the initiator of the //workflow, then <Initiator> is returned as the performer's name. if ( IsDefined( taskInfo.PERFORMERID ) ) if ( taskInfo.PERFORMERID == 0 ) name = [WebWFP_Label.LtInitiatorGt] else tmp = UAPI.GetByID( prgCtx.USession().fSession, \ taskInfo.PERFORMERID ) if ( !IsError( tmp ) ) knownUser = True name = tmp[ 1 ].NAME if ( tmp[ 1 ].TYPE != UAPI.USER ) gif = '2-guys.gif' end end end end if ( gif == '2-guys.gif' ) a.Gif = '16group.gif' else a.Gif = '16user.gif' end performerInfo.Name = name performerInfo.Gif = gif performerInfo.KnownUser = knownUser performerInfo.ID = taskInfo.PERFORMERID a.PerformerInfo = performerInfo //Create an Assoc named tmp that stores the name of your custom //module, the HTML file to display, and the data that appears on //the General tab. tmp = Assoc.CreateAssoc() tmp.ModuleName = 'custmod' tmp.HTMLFile = 't_user.html' tmp.Data = a //Set the name of the Step Definition page for this task type. //This page is displayed when the creator of a workflow map edits //a custom display task in the Workflow Painter.
50
retVal.Data.HeaderLabel = 'Custom Display Step Definition' tabPaneInfo.PaneList = { tmp } i += 1 //Create the Permissions tab that appears on the Custom Display //Step Definition page for this task type. tmp = Assoc.CreateAssoc() tmp.Label = [WebWFP_Label.Permissions] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( request ), i ) tmp.HelpKey = objName + "." + 'Permissions' // do not XLATE tmp.Active = FALSE tabPaneInfo.TabList = { @tabPaneInfo.TabList, tmp } a = Assoc.CreateAssoc() a.Permissions = taskInfo.USERFLAGS //Create an Assoc named tmp that stores the name of your custom //module, the HTML file to display, and the data that appears on //the Permissions tab. tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwfp' tmp.HTMLFile = 't_perms.html' tmp.Data = a tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, tmp } i += 1 //Set up any data type information that is required for this type //of task. for p in mapRec.WORK_PACKAGES obj = $WebWFP.WFPackageSubsystem.GetItem( { p.TYPE, \ p.SUBTYPE } ) if ( IsDefined( obj ) && IsDefined( p.USERDATA ) ) a = obj.GetTabInfo( prgCtx, request, p.USERDATA, i ) a.Active = FALSE if IsDefined( a.HelpKey ) a.HelpKey = objName + "." + a.HelpKey else a.HelpKey = objName end paneData = obj.GetMapData( prgCtx, taskInfo, p.USERDATA ) if ( IsDefined( paneData ) ) i += 1 tabPaneInfo.TabList = { @tabPaneInfo.TabList, a } tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, paneData } end end end i = Length( tabPaneInfo.TabList ) + 1
51
//List the callback scripts that fire, if applicable. AssoceventInfo List fields = { 'PERFORMERCB', 'READYCB', 'DONECB', 'KILLCB', \ 'RESURRECTCB' } List events = { [WebWFP_HTMLLabel.AssignStepPerformer], \ [WebWFP_HTMLLabel.StepBecomesReady], \ [WebWFP_HTMLLabel.StepIsDone], \ [WebWFP_HTMLLabel.StepIsKilled], \ [WebWFP_HTMLLabel.StepIsResurrected] } eventInfo.Events = events eventInfo.FieldNames = fields eventInfo = $WFMain.WFMapPkg.GetValidEvents( prgCtx, eventInfo ) if ( eventInfo.NumberOfEvents > 0 ) tmp = Assoc.CreateAssoc() tmp.Label = [WebWFP_HTMLLabel.EventScripts] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( request ), i ) tmp.HelpKey = objName + "." + 'EventScripts' tmp.Active = FALSE tabPaneInfo.TabList = { @tabPaneInfo.TabList, tmp } a = Assoc.CreateAssoc() a.EventInfo = eventInfo a.DataRec = taskInfo tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwfp' tmp.HTMLFile = 't_events.html' tmp.Data = a tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, tmp } end //Store the pane information for the tabs in the data Assoc. Then //set the active tab. By default, the active tab is 1 (or //whichever tab was originally passed into the script). if ( ( whichTab < 2 ) || ( whichTab > Length( \ tabPaneInfo.TabList ) ) ) whichTab = 1 end tabPaneInfo.TabList[ whichTab ].Active = True retVal.Data.TabInfo = tabPaneInfo retVal.Data.Tab = whichTab return( retVal ) end
PutMapData()
The following code sample describes how to save the data that the creator of a workflow map enters on the Custom Display Step Definition page.
function assoc PutMapData( \ Object prgCtx, \ Record mapRec, \ Record taskInfo, \ Record r )
52
Assoc paneData Assoc retVal Integer defaultDispo Integer flags Integer i Integer permAndDispositionFlags List dispositions List emptyDispos Object obj Real time Record p Integer count = 0 retVal.OK = TRUE if ( ( r.PaneIndex == 1 ) || ( r.PaneIndex == 0 ) ) //Save the step name. if ( RecArray.IsColumn( r, 'Title' ) ) taskInfo.Title = $LLIAPI.FormatPkg.ValToString( r.title ) end //Save the start date. if ( RecArray.IsColumn( r, 'StartDate' ) ) taskInfo.StartDate = ._CrackDate( r.StartDate ) end //Save the instructions. if ( RecArray.IsColumn( r, 'Instructions' ) ) taskInfo.Instructions = \ $LLIAPI.FormatPkg.ValToString( r.Instructions ) end //Save the callback script that the creator of the workflow map //selects from the Script to run field. if ( IsFeature( r, 'CustTaskScript' ) && ( \ r.CustTaskScript != [WebWFP_HTMLLabel._None_] ) ) taskInfo.ExAtts.CustTaskScript = r.CustTaskScript //Save the information about when to execute the callback //script (that is, the workflow event that triggers the //callback script). taskInfo.ExAtts.RunScript = r.RunScript else taskInfo.ExAtts.CustTaskScript = Undefined end //Save the template that the creator of the workflow map //selects from the Template to use field. if ( IsFeature( r, 'CustTaskTemplate' ) && ( \ r.CustTaskTemplate != [WebWFP_HTMLLabel._None_] ) ) taskInfo.CustomData.CustTaskTemplate = r.CustTaskTemplate else taskInfo.CustomData.CustTaskTemplate = Undefined end
53
//Save the duration. if ( RecArray.IsColumn( r, 'Duration' ) ) if IsDefined( r.Duration ) && Length( r.Duration ) Boolean inDays = ( r.DurationUnits == "Days" ) time = $LLIAPI.FormatPkg.StringToVal( r.Duration, \ RealType ) if ( Type( time ) != RealType ) retVal.OK = FALSE if inDays retVal.ErrMsg = \ [WebWork_ErrMsg.DurationMustBeANumberOfDays] else retVal.ErrMsg = \ [WebWork_ErrMsg.DurationMustBeANumberOfHours] end else taskInfo.DueDuration = \ $LLIAPI.FormatPkg.ConvertToSeconds( inDays, time ) end else taskInfo.DueDuration = Undefined end end //Save the group options. if RecArray.IsColumn( r, "GroupFlags" ) taskInfo.EXATTS.GroupFlags = Str.StringToInteger( \ r.GROUPFLAGS ) end elseif ( r.PaneIndex == 2 ) //Save the disposition types. for i = 1 to 5 if ( RecArray.IsColumn( r, Str.Format( "disposition_%1", \ i ) ) ) && \ Length( r.( Str.Format( "disposition_%1", i ) ) ) dispositions = { @dispositions, r.( Str.Format( \ "disposition_%1", i ) ) } else emptyDispos = { @emptyDispos, i } end end if RecArray.IsColumn( r, "disposition_selected" ) defaultDispo = $LLIAPI.FormatPkg.StringToVal( \ r.disposition_selected, integerType ) //Retrieve the disposition settings from the Permissions tab //for this task. Ignore those fields in which no values have //been specified. for i in emptyDispos if ( i < defaultDispo ) count += 1
54
end end defaultDispo -= count If ( Length( dispositions ) < defaultDispo ) retVal.OK = FALSE retVal.ErrMsg = \ [WebWork_ErrMsg.DefaultDispositionMustHaveAName] end else defaultDispo = 1 end if RecArray.IsColumn( r, "RequireDisposition" ) flags |= $WFPDisposition else dispositions = { 'Approve', 'Reject' } end //Save the permissions settings. if RecArray.IsColumn( r, "SeeAllComments" ) flags |= $WFPComments end if RecArray.IsColumn( r, "SendForReview" ) flags |= $WFPReview end if RecArray.IsColumn( r, "Delegate" ) flags |= $WFPDelegate end taskInfo.USERFLAGS = { flags, { { @dispositions }, \ defaultDispo } } else //Determine whether the data types that are attached to the //workflow need to display anything before setting up the data //for a particular task. For example, the custom display task //type needs to display a tab that allows the creator of a //workflow map to specify whether certain workflow attributes //are editable, required, or read-only. i = 3 for p in mapRec.WORK_PACKAGES obj = $WebWFP.WFPackageSubsystem.GetItem( { p.TYPE, \ p.SUBTYPE } ) if ( IsDefined( obj ) && IsDefined( p.USERDATA ) ) paneData = obj.GetMapData( prgCtx, taskInfo, p.USERDATA ) if ( IsDefined( paneData ) ) if ( i == r.PaneIndex ) retVal = obj.PutMapData( prgCtx, taskInfo, \ p.USERDATA, r ) break else i += 1
55
end end end end //Save any callback data. $WEBWFP.WFContentManager.StoreCallbackData( taskInfo, \ r ) end return retVal end
t_user.html
The following code sample describes how to design the Web page that is displayed when the creator of a workflow map edits a custom display task in the Workflow Painter. You edit a custom display task on the Custom Display Step Definition page, which is accessed by double-clicking the tasks icon or by right-clicking the task's icon, and then clicking Edit.
;;webscript t_user( Assoc data ) <!-- File: custmod/t_user.html --> ;;oscript{ Integer i List durationInfo String checked String dueDuration Dynamic taskInfo = data.TaskInfo //Set up the URLs that define the links on the Step Definition //page (for example, the Map Editor link which jumps back to //the Workflow Painter). String nextURL = Str.Format( \ "%1?func=wfp.TaskEdit&MapID=%2&TaskID=%3&NextURL=%4", \ .URL(), \ data.MapID, \ data.TaskID, \ Web.Escape( data.NextURL ) ) String chooseUserURL = .url() + Str.Format( \ "?func=wfp.TaskUserSet&MapID=%1&TaskID=%2&PerformerID=%3" + \ "&nextURL=%4", \ data.MapID, \ data.TaskID, \ taskInfo.PerformerID, \ Web.Escape( nextURL ) ); //Set up the contents of the Group Options list. List flags = { $WFMain.WFConst.kWFGroupStandard, \ $WFMain.WFConst.kWFGroupExpand, \ $WFMain.WFConst.kWFGroupExpandFull } List flagLabels = { [WebWFP_HTMLLabel.MemberAccept], \ [WebWFP_HTMLLabel.OneLevelExpand], \ [WebWFP_HTMLLabel.FullExpand] }
56
;;} //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"> `[WebWFP_HTMLLabel.StepName_]` </FONT></TD> <TD> <IMG SRC="`.ModImg( 'custmod' )``data.Gif`" WIDTH="16" HEIGHT="16" ALIGN="BOTTOM" BORDER="0" VALIGN="TOP" HALIGN="RIGHT"> <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"> `[WebWFP_HTMLLabel.AssignedTo_]` </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>
57
<TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> `[WebWFP_HTMLLabel.GroupOptions_]` </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"> `[WebWFP_HTMLLabel.Instructions_]` </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"> Script to run: </FONT></TD> <TD> <SELECT NAME="CustTaskScript" ONCHANGE="markTaskEditDirty();"> ;String scriptName ;List scriptsList = $Custmod.UtilityPkg.ListScripts() <OPTION><None> ;for scriptName in scriptsList <OPTION `( taskInfo.ExAtts.CustTaskScript == scriptName ) ? 'selected':''`>`scriptName` ;end </SELECT> <BR> //Set up the Step Becomes Ready and Step Is Done radio //buttons.
58
<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"> Template to use: </FONT></TD> <TD> <SELECT NAME="CustTaskTemplate" ONCHANGE="markTaskEditDirty();"> ;String templateName ;List templatesList = $Custmod.UtilityPkg.ListTemplates() <OPTION><None> ;for templateName in templatesList <OPTION `( taskInfo.customData.CustTaskTemplate == templateName ) ? 'selected':''`>`templateName` ;end </SELECT> </TD> </TR> //Set up the Duration field. ;;oscript{ if IsDefined( taskInfo.DueDuration ) durationInfo = $LLIAPI.FormatPkg.ConvertFromSeconds( \ taskInfo.DueDuration ) dueDuration = $LLIAPI.FormatPkg.ValToString( \ durationInfo[2] ) else durationInfo = { TRUE, 0 } end ;;} <TR>
59
<TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> `[WebWFP_HTMLLabel.Duration_]` </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"> `[WebWFP_HTMLLabel.StartDate_]` </FONT></TD> <TD NOWRAP> ;;call <.htmlPrefix() + 'datefield.html'>( 'StartDate', taskInfo.StartDate, TRUE, TRUE ) </TD> </TR> //Set up the Action field, which contains the Add to Workflow //Definition button. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> `[WebDoc_HTMLLabel.Action_]` </FONT></TD> <TD> <INPUT TYPE="Submit" VALUE="`[WebWFP_HTMLLabel.AddToWorkflowDefinitionButtonLabel]`"> </TD> </TR> </TABLE> ;;end
60
For more information about these scripts, see the code samples that follow.
61
9. Create a script, and name it PutReviewData. For more information, see PutReviewData(), page 74. 10. Create a script, and name it ReassignStep. For more information, see ReassignStep(), page 75. 11. Create an HTML file, name it redirect.html, and store it in your modules /html directory (for example, c:/opentext/module/custmod_1_0_0/html). For more information about this HTML file, see redirect.html, on page 78. You have created the object necessary to handle the task operations. Now you must provide the code required to customize the information for the custom display task type.
GetDisplayPerformerInfo()
The following code sample describes how to retrieve the name and ID of the workflow participant to which the task is assigned.
Function Dynamic GetDisplayPerformerInfo( \ Object prgCtx, \ Record taskRec ) Dynamic Dynamic Integer Object performer retVal performerID uSession = prgCtx.USession()
//Search for the ID of the Livelink user to which the task is //assigned. if ( RecArray.IsColumn( taskRec, 'PERFORMERID' ) ) performerID = taskRec.PERFORMERID elseif ( RecArray.IsColumn( taskRec, 'WORK' ) ) performerID = taskRec.WORK.SUBWORKTASK_PERFORMERID elseif ( RecArray.IsColumn( taskRec, 'SUBWORKTASK_PERFORMERID' \ ) ) performerID = taskRec.SUBWORKTASK_PERFORMERID else performerID = Undefined end //If the Livelink user ID is defined, retrieve the Livelink user //name that is associated with it. if ( IsDefined( performerID ) ) performer = UAPI.GetByID( uSession.fSession, performerID ) //If the Livelink user name is found, create an Assoc named retVal //in which you store the Livelink user name and ID. if ( !IsError( performer ) ) retVal = Assoc.CreateAssoc() retVal.ID = performer[ 1 ].ID retVal.Name = performer[ 1 ].NAME
62
GetPainterInfo()
The following code sample describes how to define the information that the Workflow Painter needs to know about the custom display task type.
Function Assoc GetPainterInfo( \ Object prgCtx, \ Record task = Undefined ) Assoc Assoc Assoc String String info linkData retVal gif = .fTaskGif name = .GetTaskName()
//Retrieve the title and image used to represent the custom //display task type in the Workflow Painter. if ( IsDefined( task ) ) info = .GetDisplayInfo( prgCtx, task ) name = info.Title gif = info.Gif end retVal.ID = Str.String( .fType ) + '_' + Str.String( .fSubType ) //Specify a name for the task in the Workflow Painter. retVal.Name = name //Specify the name of the image that is displayed in the //Workflow Painter to represent the custom display task. retVal.Gif = gif //Specify whether this task should be added to the Step Icon //Palette in the Workflow Painter. retVal.PaletteTask = .fPaletteTask //Specify whether this task can be duplicated. retVal.Duplicatable = .fDuplicatable //Specify the name of the Edit request handler for the custom //display task type. This request handler displays the custom //display task when you click the task name in your Tasks list. retVal.RHandler = 'wfp.TaskEdit'
63
//Specify the name of the View request handler for the custom //display task type. This request handler displays the detailed //status for the custom display task when you click a task name //on the Step List page. retVal.RHandlerWorkView = 'work.TaskDetail' //Specify the name of the Choose User request handler for the //custom display task type. This request handler is called when //you right-click the custom display task icon in the Workflow //Painter, and then click Choose Performer to specify the //performer of the task. retVal.RHandlerChoose = 'wfp.TaskUserSet' //Specify the background color of the task when it is displayed //in the Map Overview window in the Workflow Painter. retVal.Background = 'flesh' //Retrieve the link information associated with the task type. //This includes the maximum number of link types that can come //from the task type and the maximum number of link types that //can go to this task type. linkData = .GetTaskTypeObj().GetLinkInfo() //Specify the maximum number of link types that can come from //this task type. Most task types can only have a single link //type coming from them (either a standard link or a loopback //link); however, a conditional step can have two link types //coming from it. retVal.MaxLinkTypes = linkData.MaxLinkTypes //Specify the type of links that can go to this task type. retVal.LinkTypesTo = linkData.LinkTypesTo //Specify the type of links that can come from this task type. retVal.LinkTypesFrom = linkData.LinkTypesFrom return( retVal ) end
GetPainterMenu()
The following code sample describes how to define the menu commands that appear when you right-click the custom display task icon in the Workflow Painter.
Function List GetPainterMenu( Boolean viewonly ) List retval //If the menu commands are not set to viewonly, populate the //following Assocs. if ( !viewonly )
64
Assoca Assocb Assocc Assocd Assoce Assocf //Populate Assoca with the label, font, help, and userdata //values. The label is the name of the menu command, as it //appears in the popup menu that is displayed when you right//click the task type in the Workflow Painter. The font value //specifies the type of font used to display the menu command. //The help value is the text that is displayed on the Status //Bar when you position your cursor over the Edit command in //the popup menu. The userdata value identifies the request //handler that executes the Edit command. a.label = [WebWork_MenuLabel.Edit] a.font = "bold" a.help = [WebWork_MenuLabel.EditThisStepSAttributes] a.userdata = "rhandler" //Populate Assocb with the label, help, and userdata values. //The label is the name of the menu command, as it appears in //the popup menu that is displayed when you right-click the //task type in the Workflow Painter. The help value is the text //that is displayed on the Status Bar when you position your //cursor over the Duplicate command in the popup menu. The //userdata value identifies the request handler that executes //the Duplicate command. b.label = [WebWork_MenuLabel.Duplicate] b.help = [WebWork_MenuLabel.DuplicateTheCurrentStepSelection] b.userdata = "duplicate" //Set c.separator to TRUE to insert a separator line between //the Duplicate and Choose Performer commands in the popup menu //that appears when you right-click the task type in the //Workflow Painter. c.separator = "true" //Populate Assocd with the label, help, and userdata values. //The label is the name of the menu command, as it appears in //the popup menu that is displayed when you right-click the //task type in the Workflow Painter. The help value is the text //that is displayed on the Status Bar when you position your //cursor over the Choose Performer command in the popup menu. //The userdata value identifies the request handler that //executes the Choose Performer command. d.label = [WebWork_MenuLabel.ChoosePerformer] d.help = [WebWork_MenuLabel.ChooseAUserOrGroupForThisStep] d.userdata = "rhandlerChoose" //Set e.separator to TRUE to insert a separator line between //the Choose Performer and Delete commands in the popup menu //that appears when you right-click the task type in the //Workflow Painter.
65
e.separator = "true" //Populate Assocf with the label, help, and userdata values. //The label is the name of the menu command, as it appears in //the popup menu that is displayed when you right-click the //task type in the Workflow Painter. The help value is the text //that is displayed on the Status Bar when you position your //cursor over the Delete command in the popup menu. The //userdata value identifies the request handler that executes //the Delete command. f.label = [WebWork_MenuLabel.Delete] f.help = [WebWork_MenuLabel.DeleteTheCurrentStepSelection] f.userdata = "delete" //Create a list of the Assocs that hold the values for the menu //commands, and name the list retVal. retval = { a, b, c, d, e, f } //If the menu commands are set to viewonly, populate Assoca with //the label, font, help, and userdata values for the read-only //menu command (view). else Assoca a.label = [WebWork_MenuLabel.View] a.font = "bold" a.help = [WebWork_MenuLabel.ViewThisStep] a.userdata = "rhandlerWorkView" //Store Assoca in a list and name the list retVal. retval = { a } end return retval end
Note The GetPainterMenu() script displays the Edit, Duplicate, Choose Performer, and Delete commands on the menu that appears when you right-click the custom display task icon in the Workflow Painter. The Choose Performer command is separated from the rest of the commands in the menu by two separator lines.
GetStatusDisplay()
The following code sample describes how to retrieve the information that is displayed when a workflow participant clicks the task name on the Step List page. The Step List page is accessed by clicking the Step List tab on the Detailed Status page for a workflow.
function Assoc GetStatusDisplay( \ Object prgCtx, \ Dynamic context, \ Dynamic data = Undefined ) Assoc a
66
Assoc retVal Assoc tabPaneInfo Assoc tmp Integer whichTab RecArray auditInfo RecArray disposition RecArray performer String title Record Record mapRec = context.MAP_PAINTER task = context
whichTab = ( RecArray.IsColumn( data, 'PaneIndex' ) ) ? \ Str.StringToInteger( data.PaneIndex ) : 1 //Populate the tmp Assoc with Label, URL, HelpKey, and Active //values. The Label value specifies the name of the tab that you //are preparing for display (General). The URL value identifies //the page to display on the General tab. The HelpKey value //specifies the help page to display for this task type. If set //to TRUE, the Active value indicates that the tab is the active //tab (currently displayed). If set to FALSE, the Active value //specifies that the General tab is not the active tab and must //be called for display. tmp.Label = [WebWork_HTMLLabel.General] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( data ), 1 ) tmp.HelpKey = 'User' // do not XLATE tmp.Active = FALSE //Store the tmp Assoc in a list and assign it to //tabPaneInfo.TabList. tabPaneInfo.TabList = { tmp } a.Gif = '16user.gif' //Determine whether the workflow participant can reassign the //task by checking permissions. a.CanReassign = $WFMain.WAPIPkg.CheckWFPermissions( \ prgCtx, task, $WFMain.WFConst.kWFChangeWork ) //Retrieve the disposition data for the task. disposition = $WFMain.WAPIPkg.GetDispositionData( \ prgCtx, task.SUBWORKTASK_SUBWORKID, task.SUBWORKTASK_TASKID ) //If a disposition was specified for this task, add it to the //Assoc of data that is passed to the HTML file so that it can be //displayed. if ( IsDefined( disposition ) && Length( disposition ) ) a.Disposition = Str.Quote( $LLIAPI.FormatPkg.ValToString( \ disposition[ 1 ].VALUE ), '"' ) end if ( IsDefined( task.SUBWORKTASK_PERFORMERID ) ) performer = UAPI.GetByID( prgCtx.USession().fSession, \ task.SUBWORKTASK_PERFORMERID )
67
if ( !IsError( performer ) ) a.PerformerName = performer[ 1 ].NAME if ( performer[ 1 ].TYPE != UAPI.USER ) a.Gif = '16group.gif' end end end a.WorkRec = task tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwork' tmp.HTMLFile = 'wwtuser.html' tmp.Data = a tabPaneInfo.PaneList = { tmp } //Retrieve the audit trail, if necessary. if ( whichTab == 2 ) auditInfo = $WFMain.WAPIPkg.GetAuditRec( \ prgCtx, task.WORK_WORKID, task.SUBWORK_SUBWORKID, \ task.SUBWORKTASK_TASKID ) if ( IsError( auditInfo ) ) auditInfo = Undefined end end //Add the Audit tab to the Step Detail page for this type of //task. tmp = Assoc.CreateAssoc() tmp.Label = [WebWork_HTMLLabel.Audit] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( data ), 2 ) tmp.HelpKey = 'Audit' tmp.Active = FALSE tabPaneInfo.TabList = { @tabPaneInfo.TabList, tmp } tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwork' tmp.HTMLFile = 'audittrail.html' tmp.Data = auditInfo tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, tmp } tmp = .GetPackageStatusData( prgCtx, task, data, tabPaneInfo ) //Set the Active flag for the tab that is currently selected. if ( tmp.OK ) if ( ( whichTab < 2 ) || ( whichTab > Length( \ tabPaneInfo.TabList ) ) ) whichTab = 1 end tabPaneInfo.TabList[ whichTab ].Active = TRUE end
68
//Set up an Assoc that returns all of the data required by //Livelink to draw the Step Detail page. retVal.OK = tmp.OK retVal.ErrMsg = tmp.ErrMsg retVal.HTMLFile = "wwt.html" retVal.ModuleName = 'webwork' retVal.Tab = whichTab retVal.TabInfo = tabPaneInfo retVal.Data = task //Set the masthead information so that the correct header is //displayed at the top of the Step Detail page. retVal.HeaderArgs = $WebWork.WFPkg.GetHeaderTypeArgs( 'STATUS', \ task.SUBWORK_TITLE ) return( retVal ) end
GetTaskEditData()
The following code sample describes how to retrieve the information that is displayed when a workflow participant clicks the task name on the Tasks page in their Personal Workspace.
function Assoc GetTaskEditData( \ Object prgCtx, \ Record taskInfo, \ Record r ) Assoc a Assoc data Assoc paneData Assoc retVal Assoc tabPaneInfo Assoc tmp Dynamic status Integer whichTab List tabList Object obj RecArray packages Record p WAPIWORK work Boolean Boolean Integer Integer ok = true groupStep = False flags = WAPI.STARTTASK_FLAG_REEXECUTE i = 1
tmp = GetCustomData( taskinfo.subworktask_customdata ) if ( IsDefined( tmp ) && IsDefined( tmp.CustTaskTemplate ) ) data.HTMLFile = 'redirect.html' data.CustTaskTemplate = tmp.CustTaskTemplate data.ModuleName = 'custmod' else whichTab = ( RecArray.IsColumn( r, 'PaneIndex' ) ) ? \ Str.StringToInteger( r.PaneIndex ) : Undefined
69
//If a workflow participant has accessed the task but is just //viewing the tabs and not actually entering data, do not add //rows to the audit trail for these operations. if ( IsDefined( whichTab ) ) if ( whichTab > 0 ) flags = flags | WAPI.STARTTASK_FLAG_NOAUDIT end data.HTMLFile = 'tgeneric.html' data.ModuleName = 'webwork' data.TaskInfo = taskInfo //Set the masthead information. data.HeaderArgs = $WebWork.WFPkg.GetHeaderTypeArgs( \ 'WORK', taskInfo.SUBWORK_TITLE ) //Determine whether the task has been assigned to a Livelink //user or a group. if !( $WFMain.WAPIPkg.CheckTaskAssignedToUser( prgCtx, \ taskInfo ) ) groupStep = True end //Set up the information required to draw the first tab //(General). tmp = Assoc.CreateAssoc() tmp.Label = [WebWork_HTMLLabel.General] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( r ), i ) tmp.Active = FALSE tabPaneInfo.TabList = { tmp } a.TaskInfo = taskInfo a.GroupStep = groupStep //Use the standard General tab that lets a group member //accept the task and add it to their task list. This is the //same General tab that is used for a User task type. tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwork' tmp.HTMLFile = 'taskgeneralpane.html' tmp.Data = a tabPaneInfo.PaneList = { tmp } if ( !groupStep ) //Get a work handle. work = prgCtx.WSession().AllocWork() if ( !IsError( work ) ) //Use the StartTask() script to verify that the //performer of this task can access the data associated //with the task. This script sets up the work object so //that it can be used to access the work package. You
70
//can also use the StartTask() script to make sure that //this task is ready to be complete (and was not //completed already). status = WAPI.StartTask( \ work, \ r.WorkID, \ r.SubWorkID, \ r.TaskID, \ flags ) if ( !IsError( status ) ) //Retrieve the current work package. packages = $WFMain.WAPIPkg.GetWorkPackages( \ prgCtx, work, taskInfo ) if ( !IsError( packages ) ) i += 1 for p in packages obj = \ $WebWork.WFPackageSubsystem.GetItem( \ { p.TYPE, p.SUBTYPE } ) if ( IsDefined( obj ) && IsDefined( \ p.USERDATA ) ) a = obj.GetTabInfo( prgCtx, r, \ p.USERDATA, i ) a.Active = FALSE paneData = obj.GetData( \ prgCtx, taskInfo, p.USERDATA, \ r ) if ( IsDefined( paneData ) ) i += 1 paneData.IsEditable = \ True tabPaneInfo.TabList = { \ @tabPaneInfo.TabList, a } tabPaneInfo.PaneList = \ {@tabPaneInfo.PaneList, \ paneData } end end end //If the data was not retrieved correctly //(OK=FALSE), return an error message. else ok = False retVal.ApiError = work retVal.ErrMsg = \ [Web_ErrMsg2.CouldNotAccessWorkpackage] end //If the data was not retrieved correctly //(OK=FALSE), return an error message. else ok = False retVal.ApiError = work retVal.ErrMsg = [Web_ErrMsg2.CouldNotAccessWork]
71
end WAPI.FreeWork( work ) else ok = False retVal.ApiError = work retVal.ErrMsg = [Web_ErrMsg2.CouldNotAllocwork] end end if ( ok ) if ( ( whichTab < 2 ) || ( whichTab > Length( \ tabPaneInfo.TabList ) ) ) whichTab = 1 end tabPaneInfo.TabList[ whichTab ].Active = True end data.TabInfo = tabPaneInfo data.Tab = whichTab else ok = False retVal.ErrMsg = \ [WebWork_ErrMsg.TheArgumentPaneIndexIsRequired] end end retVal.OK = ok retVal.Data = data return( retVal ) end Function Dynamic GetCustomData( Dynamic val ) Dynamic retVal = val while ( Type( retVal ) == StringType ) retVal = Str.StringToValue( retVal ) end return( retVal ) end
GetTaskGif()
The following code sample describes how to define the image used to represent the task in the Workflow Painter. This image changes depending on the performer of the task.
Function String GetTaskGif( \ Object prgCtx, \ Record taskRec ) String retVal Integer id = .GetTaskTypeObj().GetTaskPerformerId( taskRec ) if ( IsDefined( id ) ) retVal = .GetDisplayInfo( prgCtx, taskRec ).Gif else retVal = .fTaskGif end return( retVal ) end
72
NewPerformer()
The following code sample describes how to update task information if the task is reassigned in Livelink.
Function Assoc NewPerformer( \ Object prgCtx, \ Record taskRec, \ Integer newID ) Assoc retVal Dynamic userInfo String name //Set some default variables and locate the initiator task object //in the WebWork OSpace. Boolean success = True Integer painterInfo = 0 Integer performerID = 0 Object taskType = $WebWork.WFTaskSubsystem.GetItemByName( \ 'Initiator' ) String initTitle = taskType.GetTaskName() String title = taskRec.TITLE //Determine the default title for the task based on the //performer ID. if ( !IsDefined( taskRec.PERFORMERID ) ) name = .GetTaskName() elseif ( taskRec.PERFORMERID == 0 ) name = initTitle else userInfo = UAPI.GetByID( prgCtx.USession().fSession, \ taskRec.PERFORMERID ) if ( !IsError( userInfo ) ) userInfo = userInfo[ 1 ] name = userInfo.NAME end end //Determine the default title for the task based on the //new performer ID. if ( newID == 0 ) // Initiator title = initTitle elseif ( !IsDefined( newID ) ) // Generic user title = .GetTaskName() performerID = newID else performerID = newID painterInfo = newID //Determine the user name of the performer, based on their //performer ID. userInfo = UAPI.GetByID( prgCtx.USession().fSession, newID )
73
if ( !IsError( userInfo ) && Length( userInfo ) ) userInfo = userInfo[ 1 ] title = userInfo.NAME end end //If the title of the task was set to the default title, then //update it with the new default title. If the title of the task //was not set to the default title, then dont change it. if ( success ) if ( taskRec.TITLE == name ) taskRec.TITLE = title end taskRec.PERFORMERID = performerID taskRec.PAINTER[ 2 ] = painterInfo end retVal.OK = success return( retVal ) end
PutReviewData()
The following code sample describes how to create a script that saves the instructions you provide to the reviewers of a task when you submit it for review. These instructions can also include the duration and some group options.
function assoc PutReviewData( \ Object prgCtx, \ Record mapRec, \ Record taskInfo, \ Record r ) Assoc retVal Real time retVal.OK = TRUE //Save the step name. if ( RecArray.IsColumn( r, 'Title' ) ) taskInfo.Title = $LLIAPI.FormatPkg.ValToString( r.title ) end //Save the instructions. if ( RecArray.IsColumn( r, 'Instructions' ) ) taskInfo.Instructions = $LLIAPI.FormatPkg.ValToString( r.Instructions ) end //Save the duration. if ( RecArray.IsColumn( r, 'Duration' ) ) if IsDefined( r.Duration ) && Length( r.Duration ) Boolean inDays = ( r.DurationUnits == "Days" )
74
time = $LLIAPI.FormatPkg.StringToVal( r.Duration, RealType ) if ( Type( time ) != RealType ) retVal.OK = FALSE if inDays retVal.ErrMsg = \ [WebWork_ErrMsg.DurationMustBeANumberOfDays] else retVal.ErrMsg = \ [WebWork_ErrMsg.DurationMustBeANumberOfHours] end else taskInfo.DueDuration = \ $LLIAPI.FormatPkg.ConvertToSeconds( inDays, time ) end else taskInfo.DueDuration = Undefined end end //Save the group options. if RecArray.IsColumn( r, "GroupFlags" ) taskInfo.EXATTS.GroupFlags = Str.StringToInteger( \ r.GROUPFLAGS ) end return retVal end
ReassignStep()
The following code sample describes how to create a script that lets workflow participants reassign a task on the Step Detail page.
Function Boolean ReassignStep( \ Object prgCtx, \ Record taskRec, \ Record user, \ Record workData, \ WAPIWORK work ) Assoc userInfo Integer where List cbData Boolean Object Object String ok = False uSession = prgCtx.USession() wSession = prgCtx.WSession() cr = Str.EOL()
//Determine whether a manager of a workflow has permission to //reassign the task. if ( $WFMain.WAPIPkg.CheckWFPermissions( prgCtx, workData, \ $WFMain.WFConst.kWFChangeWork ) )
75
ok = wSession.StartTrans() //If the title of the task was set to the default title, then //update it with the new default title. If the title of the task //was not set to the default title, then dont change it. if ( ok ) userInfo = .GetDisplayInfo( prgCtx, taskRec ) ok = UpdateStepTitle( prgCtx, work, taskRec, userInfo, \ user ) //If the task has been assigned to a group, add a performer //callback script that runs if the workflow loops back during the //reassignment. If the workflow loops back, this callback makes //sure that the task is reassigned to the entire groupnot just to //the group member that originally accepted it. if ( ok ) if ( user.TYPE != UAPI.USER ) cbData = { { $WFMain.WFConst.kCBSetGrpStepPerformer, \ user.ID } } else cbData = Undefined end //Save the callback script in the database. ok = UpdateMapTask( prgCtx, taskRec, cbData ) end //Set the performer ID to the new user ID. if ( ok ) ok = UpdatePerformer( prgCtx, work, taskRec, user ) end if ( !wSession.EndTrans( ok ) ) ok = False end end end return( ok ) end Function Boolean UpdatePerformer( \ Object prgCtx, \ WAPIWORK work, \ Record old, \ Record new ) Boolean success List info //Make the WAPI call that reassigns the task. success = prgCtx.WSession().CheckRetVal( \ WAPI.ReassignTask( \ work, \
76
old.WORK.SUBWORKTASK_WORKID, \ old.WORK.SUBWORKTASK_SUBWORKID, \ old.WORK.SUBWORKTASK_TASKID, \ new.ID ) ) if ( success ) info = { 1, { Str.String( [WebWork_Message.StepReassignedTo] \ ), new.ID } } $WFMain.WAPIPkg.AddAuditData( prgCtx, work, info ) old.WORK.SUBWORKTASK_PERFORMERID = new.ID end return( success ) end Function Boolean UpdateStepTitle( \ Object prgCtx, \ WAPIWORK work, \ Record task, \ Assoc info, \ Record user ) String Boolean Object name success = True wSession = prgCtx.WSession()
//Save the new task title to the database. if ( info.Title == task.WORK._SUBWORKTASK_PERFORMERID_Name ) name = user.NAME success = wSession.CheckRetVal( WAPI.UpdateTaskAttribute( \ work, \ task.WORK.SUBWORKTASK_WORKID, \ task.WORK.SUBWORKTASK_SUBWORKID, \ task.WORK.SUBWORKTASK_TASKID, \ WAPI.SUBWORKTASK_TITLE, \ name ) ) if ( success ) task.WORK.SUBWORKTASK_TITLE = name end end return( success ) end Function Boolean UpdateMapTask( \ Object prgCtx, \ Record taskData, \ List cbData = Undefined ) Boolean success WAPIMAPTASK task Object WAPIMAP session = prgCtx.WSession() map = session.AllocMap()
77
success = session.CheckRetVal( WAPI.LoadMapByID( map, \ taskData.WORKINFO.SUBWORK_MAPID ) ) if ( success ) task = WAPI.AllocNthMapTask( map, \ taskData.WORK.SUBWORKTASK_TASKID ) success = session.CheckRetVal( task ) if ( success ) task.pPerformerCB = cbData WAPI.FreeMapTask( task ) success = session.CheckRetVal( WAPI.ReplaceMap( map ) ) end end WAPI.FreeMap( map ) return( success ) end
redirect.html
This HTML file describes how to display the HTML file that the creator of a workflow map assigns to a task of this type.
;;webscript redirect( Assoc taskData ) <!-- File: custtask/redirect.html --> //Display the HTML file that the creator of the workflow assigned //to this task. ;;call <.Module( 'custmod' ).PathPrefix() + 'templates' + File.Separator() + taskData.CustTaskTemplate>() ;;end
78
79
CBExecute()
The following code sample describes how to handle the callback scripts that can be used with the custom display task type.
Function Assoc CBExecute( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer subWorkID, \ Integer taskID, \ Integer returnSubWorkID, \ Integer returnTaskID, \ List info, \ Dynamic extraData = Undefined ) Assoc data Dynamic retVal Boolean handled = True //If the callback script that is being executed is one of the //scripts that is added by the custom display task type, then run //the script. switch( info[ 1 ] ) case 500 retVal = $Custmod.UtilityPkg.ExecuteCustTaskScript( \ prgCtx, work, workID, subWorkID, taskID, info[ 2 ] ) end default handled = False end end data.Handled = handled data.retVal = retVal return( data ) end
ExecuteCustTaskScript()
The following code sample describes how to create a script that loads information about the custom display task and uses that information to locate and run the appropriate callback script.
Function Boolean ExecuteCustTaskScript( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer subWorkID, \ Integer taskID, \ String scriptName ) Dynamic taskInfo Boolean success = False Object uapiCtx = prgCtx.Usession() //Load information about the custom display task. taskInfo = prgCtx.WSession().LoadTaskStatus( workID, subWorkID, \
80
taskID ) if ( !IsError( taskInfo ) ) UAPI.ExpandList( \ uapiCtx.fSession, \ taskInfo, \ { \ 'WORK_OWNERID', \ 'WORK_MANAGERID', \ 'SUBWORKTASK_PERFORMERID' } ) //Call the ExecuteScript() script which uses the information //about the task to locate and run the correct callback script. success = $Custmod.UtilityPkg.ExecuteScript( prgCtx, work, \ taskInfo, scriptName ) end return( success ) end
ExecuteScript()
The following code sample describes how to create a script that locates and runs the callback script that is associated with this custom display task.
Function Boolean ExecuteScript( \ Object prgCtx, \ WAPIWORK work, \ Dynamic taskInfo, \ String scriptName ) Boolean Dynamic String String String success = True err = Undefined moduleDir = $Custmod.custtaskmodule.PathPrefix() scriptsDir = moduleDir + "scripts" + File.Separator() scriptFile = scriptsDir + scriptName
//Convert the HTML files that have OScript in them to pure HTML. result = $WebLingo.WebScript.RunFileWithArglist( \ scriptFile, \ Undefined, \ Undefined, \ { prgCtx, work, taskInfo }, \ true ) if ( Type( result ) == ListType ) echo( "Error during $WebLingo.WebScript.RunFile =", result ) success = false elseif IsNotError( result ) success = true else echo( "Error during $WebLingo.WebScript.RunFile =", result ) success = false end return( success ) end Dynamic
81
ListScripts()
The following code sample describes how to create a script that returns a list of scripts that are stored in the /scripts directory of the custmod module (for example, c:/opentext/module/custmod_8_1_x/scripts).
function List ListScripts() String scriptPath String moduleDir = $Custmod.custtaskmodule.PathPrefix() String scriptsDir = moduleDir + "scripts" + File.Separator() List vFileListList = File.FileList( scriptsDir ) List retList = {} //Retrieve the list of scripts that are stored in your //module's /script directory. These are the scripts that the //creators of workflow maps can attach to the custom display task //type in the Workflow Painter. if ( !IsError( vFileListList ) && \ ( Length( vFileListList ) > 0 ) ) for scriptPath in vFileListList retList = { @retList, File.GetName( scriptPath ) } end end return( retList ) end
ListTemplates()
The following code sample describes how to create a script that returns a list of HTML templates that are stored in the /templates directory of the custmod module (for example, c:/opentext/module/custmod_1_0_0/templates).
Function List ListTemplates() String templatePath String moduleDir = $Custmod.custtaskmodule.PathPrefix() String templatesDir = moduleDir + "templates" + File.Separator() List vFileListList = File.FileList( templatesDir ) List retList = {} //Retrieve the list of HTML templates that are stored in your //module's /template directory. These are the HTML templates that //the creators of workflow maps can attach to the custom display //task type in the Workflow Painter. if ( !IsError( vFileListList ) && \ ( Length( vFileListList ) > 0 ) ) for templatePath in vFileListList retList = { @retList, File.GetName( templatePath ) } end end return( retList ) end
82
83
84
Chapter Five
85
Description Stores the name of the custom tab that is displayed in the Livelink interface when you create a new data type. If a data type is attached to a workflow, its custom tab is displayed when: The creator of the workflow map edits a task on a Step Definition page in the Workflow Painter. Workflow participants access the work package of the executing workflow from their Personal Workspaces. Stores a unique integer that works with the fType feature to identify the object. The following objects also contain fSubType features: The WFPackage object, which is orphaned when you define the Workflow Painter information The WFPackage object, which is orphaned when you define the information that controls the operation of the data type in executing workflows The values of the fSubType features for the WFPackage objects must match the value of this fSubType feature. Stores a unique integer that works with the fSubType feature to identify the object. The following objects also contain fType features: The WFPackage object, which is orphaned when you define the Workflow Painter information The WFPackage object, which is orphaned when you define the information that controls the operation of the data type in executing workflows The values of the fType features for the WFPackage objects must match the value of this fType feature.
fSubType
fType
86
Note When adding data types, Open Text recommends setting the fSubType values to 1 and the fType values to an integer higher than 10. For each data type that you create, you can modify the following scripts.
Table 5-2: Scripts Associated With the WFDataTypes Object Script
CheckTaskDone()
Description Verifies that a workflow participant has provided all of the data types required information before their input is saved and the workflow is routed to the next workflow participant. For example, if you create a data type that groups workflow attributes and you indicate that entries are required for some of those attributes, this script verifies that a workflow participant has provided information for those required attributes before the workflow is routed to the next workflow participant. This script returns a list that can contain two items: a Boolean value and an error message. The Boolean value specifies whether the required data has been provided. If the required data has not been provided, the Boolean value is set to FALSE and an error message identifying the required values that must be specified before Livelink can route the work package to the next workflow participant is returned. Creates a new instance of the data type when the data type is attached to a workflow map in the Workflow Painter. This script is called when the creator of a workflow map selects the custom data type check box in the Packages section of a workflow maps Properties page, and then clicks the Add to Workflow Definition button. Adds the data type to the work package of the specified workflow. This script is called when the initiator of a workflow clicks the workflow maps name in Livelink to begin the initiation process. Deletes any previous data that has been stored for the data type. This script is called when new values are specified for the data type. Returns the information about the data type that must be available to the initiator of a workflow when they work on the Start task. This script is called when the initiator of a workflow clicks the workflow maps name in Livelink to begin the initiation process. The initiator of a workflow to which a custom data type has been attached can click the custom data type tab and provide information for the data type before starting the workflow process. Because the initiator is the first workflow participant to provide data type information, this script does not have to retrieve any previous values that may have been stored for the data type. When the initiator clicks the Initiate button to start the workflow process, their input is saved and routed with the work package to the next workflow participant.
CreateNewInstance()
CreateWorkData()
DeleteWorkData()
LoadStartTaskWorkData()
87
LoadWorkData()
RemoveWorkData()
SaveWorkData()
SetReviewData()
SetSubWorkData()
SetSubWorkReturnData()
Description Returns the information about the data type that must be available to workflow participants when they work on their tasks. This script is called when a workflow participant clicks the custom data type tab in the work package of an active workflow task. When a workflow participant clicks the custom data type tab, the information that was stored for the data type when the previous task was completed is retrieved and displayed. This script populates the data types values with the input of the previous workflow participant. It is called for all workflow tasksexcept the Start task. Note For information about displaying data type values for the Start task, see LoadStartTaskWorkData(). Returns information about a data type for the specified workflow. This script is called when a workflow manager views the detailed status of a workflow that contains this data type. It is also called by event trigger scripts throughout the execution of a workflow. Removes the data type from the specified workflow. This script is called when a workflow that contains the data type is deleted from Livelink. It is also called when a workflow manager modifies an executing workflow by removing the data type. Saves the data type information throughout the execution of a workflow. This script is called each time that a workflow participant updates the data type information. Stores the information about a data type that must be passed to the sub-workflow that is created when a workflow participant sends a task for review. This information is used by the SetSubWorkData() and SetSubWorkReturnData() scripts. Note For more information about sending a task for review, see the Livelink online help. Passes data type information from a workflow to a sub-workflow. This script is called when a sub-workflow starts or when a task is sent for review. Passes data type information from a sub-workflow back to a main workflow. This script is called when a sub-workflow finishes or when a task that has been sent for review finishes. This is the companion script to the SetSubWorkData() script.
88
For each data type that you create, you can modify the following features.
Table 5-3: Features Associated With the WFPackage Object Feature
fSubType
Description Stores a unique integer that works with the fType feature to identify the object. The following objects also contain fSubType features: The WFDataTypes object, which is orphaned when you define the API object for the data type The WFPackage object, which is orphaned when you define the information that controls the operation of the data type in executing workflows The values of the fSubType features for the WFDataTypes object and the other WFPackage object must match the value of this fSubType feature. Stores a unique integer that works with the fSubType feature to identify the object. The following objects also contain fType features: The WFDataTypes object, which is orphaned when you define the API object for the data type The WFPackage object, which is orphaned when you define the information that controls the operation of the data type in executing workflows The values of the fType features for the WFDataTypes object and the other WFPackage object must match the value of this fType feature.
fType
Note When adding data types, Open Text recommends setting the fSubType values to 1 and the fType values to an integer higher than 10. For each data type that you create, you can modify the following scripts.
Table 5-4: Scripts Associated With the WFPackage Object Script
GetMapData()
Description Specifies the name and location of the HTML file that is displayed to the creator of the workflow map when they define data type information for tasks in the Workflow Painter. The HTML file called by this script is displayed when the creator of a workflow map clicks the custom data type tab on a Step Definition page when defining tasks in the Workflow Painter.
89
Description Saves the information specified on the HTML page that is referenced in the GetMapData() script. This script saves the settings that the creator of a workflow map specifies when they define the data type information for tasks in the Workflow Painter. This script is called when the creator of a workflow map adds the data type information that they specify on the custom data type page to the workflow definition (by clicking the Add to Workflow Definition button).
In addition to the features and scripts that you modify to define the Workflow Painter information for a data type, you must create the HTML file that is displayed to the creator of the workflow map when they define the data type information for each workflow task in the Workflow Painter. This HTML page is displayed when the creator of a workflow map clicks the custom data type tab on the Step Definition page when defining tasks in the Workflow Painter.
90
Description Stores a unique integer that works with the fType feature to identify the object. The following objects also contain fSubType features: The WFDataTypes object, which is orphaned when you define the API object for the data type The WFPackage object, which is orphaned when you define the Workflow Painter information for the data type The values of the fSubType features for the WFDataTypes object and the other WFPackage object must match the value of this fSubType feature. Stores a unique integer that works with the fSubType feature to identify the object. The following objects also contain fType features: The WFDataTypes object, which is orphaned when you define the API object for the data type The WFPackage object, which is orphaned when you define the Workflow Painter information for the data type The values of the fType features for the WFDataTypes object and the other WFPackage object must match the value of this fType feature.
fType
Note When adding data types, Open Text recommends setting the fSubType values to 1 and the fType values to an integer higher than 10.
91
For each data type that you create, you can modify the following scripts.
Table 5-6: Scripts Associated With the WFPackage Object Script
GetData()
Description Sets up the display of the HTML page that is loaded when the initiator of a workflow provides data type information for the Start task, when a workflow participant opens a workflow task in their Tasks list, and when a workflow manager views the detailed status for this type of task. This script sets up the tabs on the HTML page that is called when a Livelink user is working with the data stored by the data type for the workflow. Specifies the name and location of the HTML file that is displayed to the creator of a workflow map when they define a sub-workflow task that contains the custom data type in a workflow that also contains the custom data type. This HTML file is displayed when the creator of a workflow map clicks the custom data type tab on the Sub-Map Step Definition page. The Sub-Map Step Definition page lets the creator of a workflow map specify the information about the data type that the main workflow passes to a sub-workflow. This page is also displayed when a workflow participant sends a task for review. Generates the names of the custom data type tabs that are displayed when workflow participants work on their tasks. This script also generates the URLs that determine which HTML files are displayed when each tab is active. This script ensures that the correct page is displayed when a workflow participant clicks a custom data type tab in the work package of an active workflow task. Note You can access the work package by clicking a task name on the Tasks page in your Personal Workspace. Saves the data type information that the creator of a workflow map specifies on the Sub-Map Step Definition page, when defining data type information for a sub-workflow task. This script is called when the creator of the workflow map clicks the Add to Workflow Definition button on the custom data type page (accessed by clicking the custom data type tab on the Sub-Map Step Definition page in the Workflow Painter). Saves the information that is entered on the HTML page referenced by the GetData() script. The SaveData() script saves the information that the initiator of a workflow specifies for the Start task, the information that a workflow participant enters when working on this type of task in their Task list, and the information specified by the workflow manager when viewing the detailed status for this type of task.
GetSubmapData()
GetTabInfo()
PutSubmapData()
SaveData()
92
In addition to the features and scripts that you modify to define the information that controls the operation of a data type, you must create the HTML files that are displayed when: The creator of a workflow map defines the data type information for a sub-workflow task on the Sub-Map Step Definition page. A workflow participant accesses the custom data type tab in the work package of an active workflow task.
93
The organization that the Table Values data type provides makes it easy for workflow participants to locate and use the workflow attributes for which they must provide values throughout the execution of a workflow. To use the Table Values data type, the creator of a workflow map must first attach the Table Values data type to the workflow map definition, and then define whether each workflow attribute is editable, required, or read-only for each task in the workflow map. The Table Values data type is added to a workflow map definition by selecting the Table Values check box on the workflow maps Properties page. The workflow attributes are defined by clicking the Table Values tab on the Step Definition page for each task in the workflow map, and then clicking Editable, Entry Required, or Read-Only in the corresponding workflow attribute fields.
94
Figure 5-2: The Table Values Data Type Tab in the Workflow Painter
If the creator of a workflow map clicks Editable in a workflow attribute field, the workflow participant responsible for completing that task may input new values for the attribute. If the creator of a workflow map clicks Entry Required in a workflow attribute field, the workflow participant responsible for completing that task must input new values for the attribute. If the creator of a workflow map clicks Read-Only in a workflow attribute field, the workflow participant responsible for completing that task cannot input new values for the attribute. When a workflow map that contains the Table Values data type is initiated in Livelink, the Table Values tab (containing the Project and Customer tabs) is added to the work package and routed to each workflow participant when their tasks become ready. As workflow participants work on tasks, they specify values for the workflow attribute fields on the Project and Customer pages. The information that each workflow participant provides is saved in external database tables and updated as the work package is routed from one participant to another. Each workflow participant can view the information provided by the workflow participant who completed the preceding taskexcept the workflow initiator (because there is no preceding task).
95
4. Create three children of the CustModDBScripts object, and name them MSSQL, Oracle, and Sybase, respectively. The names of these child objects are case-sensitivetype them exactly as they appear in step 4. 5. In the CustModDBScripts object, create three Dynamic features, and name them MSSQL, Oracle, and Sybase, respectively. 6. In the CustModDBScripts object, set the value of the MSSQL feature to the object reference number of the MSSQL object that you created in step 4. 7. Set the value of the Oracle feature to the object reference number of the Oracle object that you created in step 4. 8. Set the value of the Sybase feature to the object reference number of the Sybase object that you created in step 4. 9. In the ModuleDBObject, do the following: Ensure that the fDBVersion feature is a list, and set its value to {8, 0, 1}. This list represents the version of the database in which you are creating the tables. Set the value of the fEnabled feature to TRUE. Set the value of the fExecPkg feature to the object reference number of the CustModExecPkg object that you created in step 1. Ensure that the fInstallScripts feature is a List type, and set its value to
{'cust_sql'}.
Set the value of the fModuleName feature to custmod. Set the value of the fName feature to Custom Module. Set the value of the fScripts feature to the object reference number of the CustModDBScripts object that you created in step 3. Ensure that the fUninstallScripts feature is a List type, and set its value to {'cust_drop'}.
10. In the Custmod Globals object, run the BuildOSpace() script. 11. In the CustModDBScripts object, create a script and name it cust_sql. This script is used to create the tables when you install your custom module. For more information about the cust_sql() script, see cust_sql(), page 97. 12. In the CustModDBScripts object, create a script and name it cust_drop. This script is used to delete the tables from the database when you uninstall your custom module. For more information about the cust_drop() script see cust_drop(), page 98.
96
13. In the Configure object, set the value of the fHasDBSchema feature to TRUE. 14. Uninstall and reinstall the custmod module. Tip You may have to manually uninstall the custmod module and then reinstall it before your changes are registered with the Livelink database.
cust_sql()
The following code sample can be used as a prototype for the script that creates the tables in your database when you install your custom module.
#ifdef COMPILEME create table %Prefix%Cust_Project ( WorkflowID %type_int% Project_Name %type_char_64% Priority %type_int% ID_Code %type_char_32% DueDate %type_date% ) / create index Cust_Project_Primary on %Prefix%Cust_Project ( WorkflowID ) / create table %Prefix%Cust_Customers ( WorkflowID %type_int% Name %type_char_64% Addr1 %type_char_32% Addr2 %type_char_32% City %type_char_32% State %type_char_32% Zip %type_char_32% Phone %type_char_32% Fax %type_char_32% ) /
create index Cust_Customers_Primary on %Prefix%Cust_Customers ( WorkflowID ) / insert into %Prefix%KIni values( '%IniSection%', '%IniKeyword%', '{8,0,1}' ) / %commit_command% / #endif
97
cust_drop()
The following code sample can be used as a prototype for the script that removes the tables from the database when you uninstall your custom module.
#ifdef COMPILEME drop table %Prefix%Cust_Project / drop table %Prefix%Cust_Customers / delete from KIni where IniSection = '%IniSection%' and IniKeyword = '%IniKeyword%' / %commit_command% / #endif
98
SetSubPaneIndexArg
The following code sample describes how to create a utility script required by the Table Values data type.
Function String SetSubPaneIndexArg( \ String url, \ Integer paneIndex ) List findInfo PatChange change PatFind findPat String tmp change = Pattern.CompileChange( '#1' + Str.String( paneIndex ) ) findPat = Pattern.CompileFind( '<&custpaneindex=><[0-9]+>' ) findInfo = Pattern.Find( url, findPat, True ) if ( IsDefined( findInfo ) ) url = Pattern.Change( url, findPat, change, True ) else url = Str.Format( '%1&custpaneindex=%2', \ url, \ paneIndex ) end return( url ) end
99
The fDataName feature stores the text that is displayed on the custom data type tab in the Livelink interface. 6. Override the following scripts:
CheckTaskDone() CreateNewInstance() CreateWorkData() DeleteWorkData() LoadStartTaskWorkData() LoadTaskWorkData() LoadWorkData() RemoveWorkData() SaveWorkData() SetReviewData() SetSubWorkData() SetSubWorkReturnData()
For more information about these scripts, see the code samples that follow. 7. Create a script, and name it LoadTableValues. For more information about the LoadTableValues() script, see LoadTableValues(),page 105. 8. Create a script, and name it SaveTableValues. For more information about the SaveTableValues() script, see SaveTableValues()page 108. 9. Create a script, and name it UpdateSubWorkData. For more information about the UpdateSubWorkData() script, see UpdateSubWorkData(),page 111. 10. Create a script, and name it UpdateTableValues. For more information about the UpdateTableValues() script, see UpdateTableValues(),page 112. You have created a data types API object. Now you must provide the code required to customize the API object for the Table Values data type.
100
CheckTaskDone()
The following code sample describes how to verify that a workflow participant has specified values in all of the required workflow attribute fields on the Project and Customer tabs before the workflow is routed to the next workflow participant.
Function List CheckTaskDone( \ Object prgCtx, \ Dynamic data ) String msg String name List retVal = { True, '' } //Examine each workflow attribute field on the Project and //Customer pages on the Table Values tab, and determine which //fields are required. If a value has not been entered in a //required field, return a message, indicating that a value is //required for that field. for name in Assoc.Keys( data.Fields ) if ( name in data.Required ) if ( !IsDefined( data.fields.Name ) ) msg = Str.Format( 'A Value is required for the' + \ 'table value %1.', \ Str.Quote( name, '"' ) ) retVal = { False, msg } break end //If the creator of a workflow map specified that the //Customer field is a required field for this task (which //means that the entire Customer tab is displayed to //workflow participants in the work package), verify that //the workflow participant has entered a value in the //Customer Name field on the Customer tab. If a value has //not been entered, return a message indicating that a name //for the customer is required. if ( Str.Upper( name ) == 'CUSTOMER' ) if ( !IsDefined( data.Fields.Customer.Name ) ) msg = 'You must enter a name for the customer.' retVal = { False, msg } break end end end end return( retVal ) end
101
CreateNewInstance()
The following code sample describes how to create a new instance of the Table Values data type when it is attached to a workflow map in the Workflow Painter.
Function Dynamic CreateNewInstance( \ Object prgCtx, \ Dynamic map = Undefined, \ Dynamic info = Undefined ) Assoc retVal String name //Create a list named fields, which stores the names of the //fields that are displayed on the Table Values tab when the //creator of the workflow map edits a step in the Workflow //Painter. Then create a list named cust_fields that stores the //names of the fields that are displayed on the Customer tab. List fields = { 'Project_Name', 'Priority', 'DueDate', \ 'Customer', 'ID_Code' } List cust_fields = { 'Name', 'Addr1', 'Addr2', 'City', \ 'State', 'Zip', 'Phone', 'Fax' } //Create an Assoc that stores all the field names. Then set all //field values to Undefined. retVal.Fields = Assoc.CreateAssoc() for name in fields retVal.Fields.( name ) = Undefined end //Set the default value of the Priority field to 3, which is //low priority. retVal.Fields.Priority = 3 //Create an Assoc that stores the field names for the Customer //tab (Customer Name, Address 1, Address 2, City, State, Zip //Code, Phone, and Fax). Then set all field values to Undefined. retVal.Fields.Customer = Assoc.CreateAssoc() for name in cust_fields retVal.Fields.Customer.( name ) = Undefined end retVal.Required = {} retVal.NonEditable = {} return( retVal ) end
102
CreateWorkData()
The following code sample describes how to add the Table Values data type to the work package of the specified workflow.
Function Boolean CreateWorkData( \ Object prgCtx, \ WAPIWORK work, \ List wfInfo, \ Dynamic data ) Boolean Record success r
List info = { .fType, .fSubType, wfInfo[ 2 ] } Object session = prgCtx.WSession() success = session.StartTrans() //Call the ReadyForModification() script to verify that the data //type is in a format that can be saved. if ( success ) data = .ReadyForModification( prgCtx, data ) //Add the Table Values data type to the specified workflow. success = $WFMain.WAPIPkg.AddWorkDataPackage( work, \ 'TableValues', info ) //Call the SaveTableValues() script to save the values that a //workflow participant specifies for the workflow attribute //fields on the Customer and Project pages. if ( success ) success = .SaveTableValues( prgCtx, wfInfo[ 2 ], data ) end if ( !session.EndTrans( success ) ) success = False end end return( success ) end
DeleteWorkData()
The following code sample describes how to delete any previous data that has been stored for the custom workflow attributes in the database tables. This script is called by the UpdateTableValues() script when Livelink updates the data in the database tables.
Function Boolean DeleteWorkData( \ Object prgCtx, \ Integer workID )
103
//Delete the row in the database table that stores the previous //values for the workflow attribute fields on the Customer tab. sqlResult = CAPI.Exec( connect.fConnection, \ 'Delete from Cust_Customers where ' + \ 'WorkflowID = :A1', \ workID ) success = session.CheckRetVal( sqlResult ) //Delete the row in the database table that stores the previous //values for the workflow attribute fields on the Project tab. if ( success ) sqlResult = CAPI.Exec( connect.fConnection, \ 'Delete from Cust_Project where ' + \ 'WorkflowID = :A1', \ workID ) success = session.CheckRetVal( sqlResult ) end return( success ) end
LoadStartTaskWorkData()
The following code sample describes how to load the information about the data type that is required to display the Table Values tab to the initiator of the workflow when they work on the Start task. This script does not load data from the database tables because no values have previously been specified. Instead, it loads information about each workflow attribute field on the Project and Customer pages on the Table Values tab (that is, whether each field is editable, required, or read-only). Note The creator of a workflow map specifies whether the workflow attribute fields for each task in a workflow are editable, required, or read-only when they define tasks in the Workflow Painter.
Function Dynamic LoadStartTaskWorkData( \ Object prgCtx, \ Record taskInfo, \ Dynamic data, \ Integer holderID ) Dynamic taskData Dynamic retVal = data taskData = taskInfo.FORM retVal.NonEditable = {} retVal.Required = {} //Determine which workflow attribute fields are read-only. Ready//only fields cannot be edited by the initiator of the workflow. if ( IsDefined( taskData ) ) if ( IsDefined( taskData.NONEDITABLE_TABLE_VALUES ) )
104
retVal.NonEditable = taskData.NONEDITABLE_TABLE_VALUES end //Determine which workflow attribute fields are required. //Required fields must be edited by the initiator of the //workflow. if ( IsDefined( taskData.REQUIRED_TABLE_VALUES ) ) retVal.Required = taskData.REQUIRED_TABLE_VALUES end end return( retVal ) end
LoadTableValues()
The following code sample describes how to create a script that loads information about the Table Values data type that is stored in the database tables. This script is called by the LoadTaskWorkData() script to retrieve the previously specified values for the workflow attribute fields on the Project and Customer pages on the Table Values tab in the work package for a particular workflow. This ensures that the most recent values are displayed on the Project and Customer pages throughout the execution of the workflow.
Function Assoc LoadTableValues( \ Object prgCtx, \ Integer workID ) Assoc project Assoc retVal Dynamic sqlResult Object Object session = prgCtx.WSession() connect = session.fDbConnect
//Retrieve the current values of the workflow attribute fields on //the Project page for this workflow from the database table. //The workflow ID is used to determine the current values of the //workflow attributes for this particular workflow. sqlResult = CAPI.Exec( connect.fConnection, \ 'select * from Cust_Project where WorkflowID = :A1', \ workID ) //Store the information that you retrieved from the database //tables in an Assoc named project. if ( session.CheckRetVal( sqlResult ) && Length( sqlResult ) ) project = Assoc.FromRecord( sqlResult[ 1 ] ) //Remove the workflow ID from the project Assoc. Assoc.Delete( project, 'WorkflowID' ) //Retrieve the values of the workflow attribute fields on the //Customer page for this workflow from the database table. //The workflow ID is used to determine the current values of //the workflow attributes for this particular workflow.
105
sqlResult = CAPI.Exec( connect.fConnection, \ 'select * from Cust_Customers where ' + \ 'WorkflowID = :A1', \ workID ) //Store the information that you retrieved from the database //tables in an Assoc named project.Customer. if ( session.CheckRetVal( sqlResult ) && Length( sqlResult ) ) project.Customer = Assoc.FromRecord( sqlResult[ 1 ] ) //Remove the workflow ID from the project.Customer Assoc. Assoc.Delete( project.Customer, 'WorkflowID' ) retVal.Fields = project retVal.NonEditable = {} retVal.Required = {} retVal.WorkflowID = workID else //If unsuccessful, return an error. retVal = sqlResult end else //If unsuccessful, return an error. retVal = sqlResult end return( retVal ) end
LoadTaskWorkData()
The following code sample describes how to load information about the Table Values data type from the database tables. This script calls another script named LoadTableValues(), which populates the fields on the Project and Customer pages (based on the workflow maps ID) throughout the execution of a workflow. For more information about the LoadTableValues() script, see LoadTableValues(), page 105.
Function Dynamic LoadTaskWorkData( \ Object prgCtx, \ WAPIWORK work, \ Record taskInfo, \ Dynamic data ) Dynamic taskData
//Call the LoadTableValues() script. Assoc retVal = .LoadTableValues( prgCtx, data ) if ( !IsError( retVal ) ) taskData = taskInfo.MAPTASK_FORM
106
//Store the read-only values. if ( IsDefined( taskData ) ) if ( IsDefined( taskData.NONEDITABLE_TABLE_VALUES ) ) retVal.NonEditable = taskData.NONEDITABLE_TABLE_VALUES end //Store the required values. if ( IsDefined( taskData.REQUIRED_TABLE_VALUES ) ) retVal.Required = taskData.REQUIRED_TABLE_VALUES end end end return( retVal ) end
LoadWorkData()
This code sample describes how to return information about the Table Values data type for the specified workflow.
Function Dynamic LoadWorkData( \ Object prgCtx, \ WAPIWORK work, \ Dynamic data ) //Call the LoadTableValues() script. Dynamic retVal = .LoadTableValues( prgCtx, data ) return( retVal ) end
RemoveWorkData()
This code sample describes how to remove the Table Values data type from the specified workflow.
Function Boolean RemoveWorkData( \ Object prgCtx, \ WAPIWORK work, \ Dynamic data ) //Delete the Table Values data type. Boolean success = $WFMain.WAPIPkg.RemoveWorkDataPackage( \ work, 'TableValues' ) //Call the DeleteWorkData() script to delete the previous values //that may have been stored for the Table Values data type in the //database tables. if ( success ) .DeleteWorkData( prgCtx, data ) end return( success ) end
107
SaveTableValues()
This code sample describes how to create a script that saves the values that a workflow participant specifies for the workflow attribute fields on the Customer and Project pages. This script is called by the UpdateTableValues() script after the previous values have been deleted from the database by the DeleteWorkData() script. Notes For more information about the UpdateTableValues() script, see UpdateTableValues(), page 112. For more information about the DeleteWorkData() script, see DeleteWorkData(), page 103.
Function Boolean SaveTableValues( \ Object prgCtx, \ Integer workID, \ Assoc data ) Boolean Dynamic success sqlResult
Assoc fields = data.Fields Assoc customer = data.Fields.Customer Object session = prgCtx.WSession() Object connect = session.fDbConnect //In the database tables, store the workflow ID, along with the //values of the Project Name, Project Code, Due Date, and //Priority fields, as specified on the Project page at the time //that this script is called. sqlResult = CAPI.Exec( connect.fConnection, \ 'insert into Cust_Project( WorkflowID,' + \ 'Project_Name, Priority, DueDate, ID_Code ) ' + \ 'values ( :A1, :A2, :A3, :A4, :A5 )', \ workID, \ fields.Project_Name, \ fields.Priority, \ fields.DueDate, \ fields.ID_Code ) success = session.CheckRetVal( sqlResult ) //In the database tables, store the workflow ID, along with the //values of the Customer Name, Address 1, Address 2, City, State, //Zip Code, Phone, and Fax fields, as specified on the Customer //page at the time that this script is called. if ( success ) sqlResult = CAPI.Exec( connect.fConnection, \ 'insert into Cust_Customers( WorkflowID,' + \ 'Name, Addr1, Addr2, City, State, Zip, Phone,' + \ 'Fax ) ' + \ 'values ( :A1, :A2, :A3, :A4, :A5, :A6, :A7,' + \ ':A8, :A9 )', \ workID, \
108
customer.Name, \ customer.Addr1, \ customer.Addr2, \ customer.City, \ customer.State, \ customer.Zip, \ customer.Phone, \ customer.Fax ) success = session.CheckRetVal( sqlResult ) end return( success ) end
SaveWorkData()
The following code sample describes how to update the information stored in the database tables for the Table Values data type. This script calls the UpdateTableValues() script which then calls the DeleteWorkData() script (to remove the previous information) and the SaveTableValues() script (to add the new information).
Function Boolean SaveWorkData( \ Object prgCtx, \ WAPIWORK work, \ Record taskRec, \ Dynamic data ) Boolean success Record r Object session = prgCtx.WSession() success = session.StartTrans() //Call the UpdateTableValues() script. if ( success ) success = .UpdateTableValues( prgCtx, data ) if ( !session.EndTrans( success ) ) success = False end end return( success ) end
SetReviewData()
The following code sample describes how to store information about the Table Values data type that must be passed to the sub-workflow that is created when a workflow participant sends a task for review.
Function Dynamic SetReviewData( \ Object prgCtx, \ Dynamic data ) //If the data that you are passing to the sub-workflow for review //is Undefined, pass all the data. Dynamic retVal return( retVal ) end
109
SetSubWorkData()
The following code sample describes how to retrieve the information about the Table Values data type that the main workflow passes to a sub-workflow.
Function Boolean SetSubWorkData( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer subWorkID, \ Integer taskID, \ Integer returnID, \ RecArray pkgs, \ Dynamic data ) List List Record Boolean mainAttribNames subAttribNames r success = True
//Locate the Table Values data type in the RecArray of data types //that have been added to the workflow. Then call the //UpdateSubWorkData() script which passes the data from the main //workflow to the sub-workflow. for r in pkgs if ( ( r.TYPE == .fType ) && ( r.SUBTYPE == .fSubType ) ) success = .UpdateSubWorkData( \ prgCtx, \ r.USERDATA, \ returnID, \ data ) break end end return( success ) end
SetSubWorkReturnData()
The following code sample describes how to determine what type of information must be passed from a sub-workflow back to the main workflow.
Function Boolean SetSubWorkReturnData( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer returnWorkID, \ Integer returnTaskID, \ RecArray pkgs, \ Dynamic data ) Record Boolean r success = True
110
//Locate the Table Values data type in the RecArray of data types //that have been added to the workflow. Then call the //UpdateSubWorkData() script which passes information from the //sub-workflow back to the main workflow. for r in pkgs if ( ( r.TYPE == .fType ) && ( r.SUBTYPE == .fSubType ) ) success = .UpdateSubWorkData( \ prgCtx, \ returnWorkID, \ r.USERDATA, \ data ) break end end return( success ) end
UpdateSubWorkData()
The following code sample describes how to create a script that passes data from the Table Values data type from one workflow to another.
Function Boolean UpdateSubWorkData( \ Object prgCtx, \ Integer toID, \ Integer fromID, \ List fields ) // The fields to pass. Undefined = all Assoc data Assoc subWorkData String name Boolean Boolean success = True updateAll = !IsDefined( fields )
//Call the LoadTableValues() script to load the information about //the Table Values data type from the database tables. data = .LoadTableValues( prgCtx, fromID ) success = !IsError( data ) if ( success ) if ( !updateAll ) updateAll = True //Determine which fields to pass from one workflow to another. for name in Assoc.Keys( data.Fields ) if ( !( name in fields ) ) updateAll = False break end end end
111
if ( !updateAll ) subWorkData = .LoadTableValues( prgCtx, toID ) success = !IsError( data ) //Set the values of the workflow attributes in the new workflow //equal to the values of the workflow attributes in the current //workflow. if ( success ) for name in fields subWorkData.Fields.( name ) = data.Fields.( name ) end end data = subWorkData end if ( success ) //Call the DeleteWorkData() script to delete any previous //data that has been stored for the custom workflow //attributes in the database tables. success = .DeleteWorkData( prgCtx, toID ) end if ( success ) //Save the new data. success = .SaveTableValues( prgCtx, toID, data ) end end return( success ) end
UpdateTableValues()
The following code sample describes how to create a script that updates the values that workflow participants specify for each workflow attribute field on the Project and Customer pages. This script is called throughout the execution of a workflow to update the information for the Table Values data type in the database tables.
Function Boolean UpdateTableValues( \ Object prgCtx, \ Dynamic data ) //Call the DeleteWorkData() script to delete any previously saved //workflow attribute data that has been stored in the database //tables. Boolean success = .DeleteWorkData( prgCtx, data.WorkflowID )
//Call the SaveTableValues() script to add the new workflow //attribute values to the database tables.
112
if ( success ) success = .SaveTableValues( prgCtx, data.WorkflowID, data ) end return( success ) end
For more information about these scripts, see the code samples that follow. 5. Create an HTML file, and name it t_tablevalues.html. For more information about this HTML file, see t_tablevalues.html, on page 115. You have created the object necessary to define a data types Workflow Painter information. Now you must provide the code required to customize the object for the Table Values data type.
113
GetMapData()
The following code sample specifies the name and location of the HTML file that is displayed to the creator of a workflow map when they define the Table Values data type information for each task in a workflow map.
function Assoc GetMapData( \ Object prgCtx, \ Dynamic context, \ Dynamic data ) Dynamic retVal RecArray array //Create an Assoc that stores the name and location of the HTML //file that is displayed when the creator of a workflow map //clicks the Table Values tab on a Step Definition page in the //Workflow Painter. The file is called t_tablevalues.html and is //stored in the /html folder in the custmod module directory //structure (for example, c:/opentext/module/custmod_1_0_0/html). if ( IsDefined( data ) ) retVal = Assoc.CreateAssoc() retVal.HTMLFile = 't_tablevalues.html' retVal.ModuleName = 'custMod' retVal.Data = Assoc.CreateAssoc() retVal.Data.taskInfo = context retVal.Data.Data = data end return( retVal ) end
PutMapData()
The following code sample describes how to save the information specified on the HTML page that is referenced in the GetMapData() script (that is, t_tablevalues.html). This script saves the settings that the creator of a workflow map specifies when they define whether each workflow attribute field is editable, required, or read-only for a particular workflow task.
function Assoc PutMapData( \ Object prgCtx, \ Dynamic context, \ Dynamic data, \ Record r ) Assoc retVal List nonEditable List required String name String key //Determine whether each workflow attribute field is editable, //required, or read-only.
114
for name in Assoc.Keys( data.Fields ) key = 'TV_' + name if ( IsFeature( r, key ) ) if ( r.( key ) == 'ReadOnly' ) nonEditable = { @nonEditable, name } elseif ( r.( key ) == 'Required' ) required = { @required, name } end end end //Store the information about the workflow attribute fields in an //Assoc named context.Form so that it can be accessed when the //workflow is initiated. if IsUndefined( context.Form ) context.Form = Assoc.CreateAssoc() end context.Form.NONEDITABLE_TABLE_VALUES = nonEditable context.Form.REQUIRED_TABLE_VALUES = required retVal.OK = TRUE return retVal end
t_tablevalues.html
The following HTML file is displayed to the creator of a workflow map when they define tasks in a workflow map that contains the Table Values data type. This HTML page lets the creator of a workflow map specify whether each field on the Table Values page (accessed from the Step Definition page) is editable, required, or read-only.
;;webscript t_tablevalues( Dynamic data ) <!-- File: custmod/t_tablevalues.html --> ;;oscript{ String a String selected Integer i = 1 Record theTask = data.taskInfo Assoc theForm = theTask.Form if ( !IsDefined( theForm ) ) theForm = Assoc.CreateAssoc() end ;;} <TABLE BORDER="0" CELLPADDING="2" CELLSPACING="1"> ;for a in Assoc.Keys( data.data.Fields ) <TR> <TD bgcolor="#CCCCCC" NOWRAP VALIGN="CENTER"><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> `a`: </FONT></TD> <TD>
115
<SELECT NAME="TV_`a`" ONCHANGE="markTaskEditDirty();"> //Add the Editable option to the popup menu that is displayed for //each workflow attribute on the Table Values page when the creator //of a workflow map defines the task. ;selected = ( !( a in theForm.REQUIRED_TABLE_VALUES ) && !( a in theForm.NONEDITABLE_TABLE_VALUES ) ) ? " SELECTED" : "" <OPTION VALUE="Editable"`selected`>`[WebWFP_HTMLLabel.Editable]` //Add the Entry Required option to the popup menu that is displayed //for each workflow attribute on the Table Values page when the //creator of a workflow map defines the task. ;selected = ( ( selected == "" ) && ( a in theForm.REQUIRED_TABLE_VALUES ) ) ? " SELECTED" : "" <OPTION VALUE="Required"`selected`>`[WebWFP_HTMLLabel.EntryRequired]` //Add the Read-Only option to the popup menu that is displayed for //each workflow attribute on the Table Values page when the creator //of a workflow map defines the task. ;selected = ( ( selected == "" ) && ( a in theForm.NONEDITABLE_TABLE_VALUES ) ) ? " SELECTED" : "" <OPTION VALUE="ReadOnly"`selected`>`[WebWFP_HTMLLabel.ReadOnly]` </SELECT> </TD> </TR> ;i += 1 ;end //Set up the Action row, which contains the Add to Workflow //Definition button. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> `[WebDoc_HTMLLabel.Action_]` </FONT></TD> <TD> <INPUT TYPE="Submit" VALUE="`[WebWFP_HTMLLabel.AddToWorkflowDefinitionButtonLabel]`"> </TD></TR> </TABLE> ;;end
116
For more information about these scripts, see the code samples that follow. 6. Create an HTML file, and name it submap_tablevalues.html. For more information about this HTML file, see submap_tablevalues.html, page 123. 7. Create an HTML file, and name it tablevalues.html. For more information about this HTML file, see tablevalues.html, page 126. 8. Create an HTML file, and name it projectpane.html. For more information about this HTML file, see projectpane.html, page 126. 9. Create an HTML file, and name it customerpane.html. For more information about this HTML file, see customerpane.html, page 129 You have created the object that is used to control a data type when it is manipulated in a workflow by workflow participants. Now you must provide the code required to customize the object for the Table Values data type.
117
GetData()
The following code sample describes how to set up the HTML page that is loaded when the initiator of a workflow specifies values for the workflow attributes on the Project and Customer pages just before the first task is routed to the corresponding workflow participant.
function Assoc GetData( \ Object prgCtx, \ Dynamic context, \ Dynamic data, \ Record request, \ Boolean forStatus = False ) Assoc paneData Assoc tabPaneInfo Assoc tmp Dynamic retVal Integer whichTab RecArray array if ( IsDefined( data ) && Length( data ) ) retVal = Assoc.CreateAssoc() retVal.Data = data retVal.HTMLFile = 'tablevalues.html' retVal.ModuleName = 'custmod' whichTab = ( RecArray.IsColumn( request, 'CustPaneIndex' \ ) ) ? Str.StringToInteger( request.CustPaneIndex ) : 1 //Set up the information required to display the Project tab. tmp.Label = 'Project' tmp.URL = $CustMod.CustModPkg.SetSubPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( request ), 1 ) tmp.Active = FALSE tabPaneInfo.TabList = { tmp } tmp = Assoc.CreateAssoc() tmp.ModuleName = 'custmod' tmp.HTMLFile = 'projectpane.html' tmp.Data = data tabPaneInfo.PaneList = { tmp } //Set up the information required to display the Customer tab. tmp = Assoc.CreateAssoc() tmp.Label = 'Customer' tmp.URL = $CustMod.CustModPkg.SetSubPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( request ), 2 ) tmp.Active = FALSE tabPaneInfo.TabList = { @tabPaneInfo.TabList, tmp } tmp = Assoc.CreateAssoc()
118
tmp.ModuleName = 'custmod' tmp.HTMLFile = 'customerpane.html' tmp.Data = data tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, tmp } //If the tab value is less than 2 or greater than the maximum //number of tabs, reset it to 1 so that the first tab is //displayed. if ( ( whichTab < 2 ) || ( whichTab > Length( \ tabPaneInfo.TabList ) ) ) whichTab = 1 end tabPaneInfo.TabList[ whichTab ].Active = True retVal.TabInfo = tabPaneInfo retVal.Tab = whichTab end return( retVal ) end
GetSubMapData()
The following code sample describes how to specify the name and location of the HTML file that is displayed to the creator of a workflow map when they define the sub-workflow task that contains the Table Values data type in a workflow that also contains the Table Values data type.
function Assoc GetSubmapData( \ Object prgCtx, \ Dynamic context, \ Dynamic data, \ Boolean loadData = True ) DAPINODE Dynamic Record Record String node retVal mapRec r mapName
Boolean found = False List passedValues = Undefined //Load the workflow map that will be used for the sub-workflow //task. if ( IsDefined( data ) ) if ( loadData ) if ( IsDefined( context.SUBMAPID ) ) node = DAPI.GetNodeByID( prgCtx.DapiSess(), \ DAPI.BY_DATAID, context.SUBMAPID, True ) if ( !IsError( node ) ) mapName = node.pName mapRec = $WFMain.WFMapPkg.LoadMap( prgCtx, { \
119
node, Undefined } ) //Determine whether the Table Values data type is included in the //work package of the sub-workflow task. if ( IsDefined( mapRec ) ) for r in mapRec.WORK_PACKAGES if ( ( r.Type == .fType ) && \ ( r.SubType == .fSubType ) ) found = True break end end end end end //If the Table Values data type is included in the work package //of the sub-workflow task, retrieve the data that must be passed //from the main workflow to the sub-workflow. if ( found ) for r in context.WORKPKGINFO if ( ( r.Type == .fType ) && \ ( r.SUBTYPE == .fSubType ) ) if ( IsDefined( r.USERDATA ) ) passedValues = r.USERDATA end break end end end end retVal = Assoc.CreateAssoc() retVal.Data = Assoc.CreateAssoc() retVal.Data.taskInfo = context retVal.Data.Data = data retVal.Data.Found = found retVal.Data.Title = mapName retVal.Data.PassedValues = passedValues retVal.HTMLFile = 'submap_tablevalues.html' retVal.ModuleName = 'custmod' end return( retVal ) end
GetTabInfo()
The following code sample describes how to generate the names of the custom data type tabs (Table Values, Project, and Customer) and the URLs required to display the correct HTML pages when workflow participants work on their tasks.
Function Assoc GetTabInfo( \ Object prgCtx, \ Record request, \ Dynamic data, \ Integer paneIndex )
120
Assoc
retVal
//Set up the URL that is called when workflow participants click //the Table Values tab when working on tasks in their Task list. String myURL = $WebDSP.HTMLPkg.ArgsToURL( request ) String url = $WebWork.WFPkg.SetPaneIndexArg( myURL, \ paneIndex ) if ( !IsFeature( request, 'subpaneindex' ) ) url = $CustMod.CustModPkg.SetSubPaneIndexArg( url, 1 ) end //Return an Assoc that contains the name of the tab, the URL that //is called when a workflow participant clicks the Table Values //tab, and any online help information associated with the tab. retVal.Label = .GetDataTypeObj().fDataName retVal.URL = url retVal.HelpKey = .GetDataTypeObj().OSName retVal.Active = FALSE return( retVal ) end
PutSubMapData()
The following code sample describes how to save the information that the creator of a workflow map specified when defining data type information for a sub-workflow task (on the Sub-Map Step Definition page).
function Assoc PutSubMapData( \ Object prgCtx, \ Dynamic context, \ Dynamic data, \ Record r ) Assoc commentData Assoc retVal List valsToPass Record pkgRec Record rec String name //Locate the Table Values data type in the RecArray of data types //associated with a workflow. for rec in context.WorkPkgInfo if ( ( rec.Type == .fType ) && ( rec.SubType == .fSubtype ) ) pkgRec = rec break end end //If information //been passed to //RecArray. This //that should be //workflow. about the Table Values data type has not yet the sub-workflow task, add a new Record to the Record contains the list of workflow attributes passed from the main workflow to the sub-
121
if ( !IsDefined( pkgRec ) ) pkgRec = $LLIAPI.RecArrayPkg.NewRecord( context.WORKPKGINFO ) pkgRec.Type = .fType pkgRec.SubType = .fSubType end //Save the list of workflow attributes that should be passed to //the sub-workflow. This is the same list of attributes that //determines which workflow attributes should be passed from the //sub-workflow back to the main workflow. for name in Assoc.Keys( data.Fields ) if ( IsFeature( r, name ) ) valsToPass = { @valsToPass, name } end end pkgRec.USERDATA = valsToPass retVal.OK = TRUE return( retVal ) end
SaveData()
The following code sample describes how to save the information that the initiator of a workflow specifies on the Project and Customer pages for the Start task before routing the workflow to the first workflow participant.
function Dynamic SaveData( \ Object prgCtx, \ Record request, \ Assoc data ) Assoc fields Assoc retVal String name Integer pane = Str.StringToInteger( request.CustPaneIndex ) retVal.OK = True //Save the values of the fields on the Project tab. if ( pane == 1 ) fields = data.Fields fields.Project_Name = request.project_name fields.ID_Code = request.ID_Code fields.DueDate = Str.StringToValue( request.DueDate ) fields.Priority = Str.StringToInteger( request.Priority ) //Save the values of the fields on the Customer tab. elseif ( pane == 2 ) fields = data.Fields.Customer for name in Assoc.Keys( fields ) if ( IsFeature( request, name ) ) fields.( name ) = Request.( name ) end end end retVal.Data = data return( retVal ) end
122
submap_tablevalues.html
The following HTML file is displayed when the creator of a workflow map defines a subworkflow task that contains the Table Values data type in a workflow that also contains the Table Values data type (by clicking the Table Values tab on the Sub-Map Step Definition page). It lets the creator of a workflow map specify the type of data that is passed between the main workflow and the sub-workflow.
;;webscript submap_tablevalues( Dynamic data ) <!-- File: custmod/submap_tablevalues.html --> //Edit table values for a sub-workflow map. This file assumes that //you are already in the context of an HTML form. ;Boolean noValues = !IsDefined( data.PassedValues ) ;List vals = data.PassedValues <TABLE BORDER="0" CELLPADDING="2" CELLSPACING="1"> //Set up the Table Values row. <TR> <TD bgcolor="#CCCCCC" NOWRAP VALIGN="TOP"><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> Table Values: </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"> </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>
123
</TR> //Set up a check box to specify whether the priority //values pass between the main workflow and the //sub-workflow. <TR> ;if ( noValues || ( 'Priority' in vals ) ) ; checked = "CHECKED" ;else ; checked = "" ;end <TD HEIGHT="20"> <INPUT TYPE="CHECKBOX" `checked` NAME="Priority" ONCLICK="markTaskEditDirty();"> Priority passes between main workflow and sub-workflow </TD> </TR> //Set up a check box to specify whether the due date //passes between the main workflow and the sub//workflow. <TR> ;if ( noValues || ( 'DueDate' in vals ) ) ; checked = "CHECKED" ;else ; checked = "" ;end <TD HEIGHT="20"> <INPUT TYPE="CHECKBOX" `checked` NAME="DueDate" ONCLICK="markTaskEditDirty();"> Due Date passes between main workflow and sub-workflow </TD> </TR> //Set up a check box to specify whether the project //code passes between the main workflow and the //sub-workflow. <TR> ;if ( noValues || ( 'ID_Code' in vals ) ) ; checked = "CHECKED" ;else ; checked = "" ;end <TD HEIGHT="20"> <INPUT TYPE="CHECKBOX" `checked` NAME="ID_Code" ONCLICK="markTaskEditDirty();"> Project Code passes between main workflow and sub-workflow </TD> </TR> //Set up a check box to specify whether the customer
124
//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> </TD> </TR> </TABLE> </TD> </TR> //Set up the Action row, which contains the Add to Workflow //Definition button. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> `[WebDoc_HTMLLabel.Action_]` </FONT></TD> <TD> <INPUT TYPE="Submit" VALUE="`[WebWFP_HTMLLabel.AddToWorkflowDefinitionButtonLabel]`"> </TD> </TR> </TABLE> ;;end
125
tablevalues.html
The following HTML file sets up the tabs that are displayed when a workflow participant clicks the Table Values tab in the work package of an executing workflow. This HTML page sets up the Project and Customer pages.
;;webscript tablevalues( Assoc a ) <!-- File: tablevalues.html --> ;Assoc ;Assoc ;String tabInfo = a.TabInfo paneInfo = tabInfo.PaneList[ a.Tab ] thisURL = $WebDSP.HTMLPkg.ArgsToURL( .fArgs )
<INPUT TYPE="HIDDEN" NAME="custPaneIndex" VALUE="1"> <SCRIPT LANGUAGE="JavaScript"> <!-function SubmitMyPage() {if ( document.myForm.xAction != null ) {document.myForm.xAction.value = 'Update';} document.myForm.NextURL.value = "`%LthisURL`"; document.myForm.paneIndex.value = '`.fArgs.PaneIndex`'; document.myForm.custPaneIndex.value = '`.fArgs.custPaneIndex`'; document.myForm.submit();} %// --> </SCRIPT> ;paneInfo.IsEditable = a.IsEditable ;;call <.HTMLPrefix() + 'tabber.html'>( 1, '#666666', tabInfo.TabList, Undefined, 'istedirty' ) ;;call <.ModHTMLPrefix( a.ModuleName ) + paneInfo.HTMLFile>( paneInfo ) ;;call <.HTMLPrefix() + 'tabber.html'>( 0, '#666666' ) ;;end
projectpane.html
The following HTML file sets up the information required to display the Project page when a workflow participant clicks the Project tab on the Table Values page in the work package for an executing workflow.
;;webscript projectpane( Assoc a ) <!-- File: projectpane.html --> ;String selected ;Assoc data = a.Data.Fields <TABLE BORDER="0" CELLPADDING="2" CELLSPACING="1" WIDTH="100%"> //Set up the Project Name row. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> Project Name: </FONT></TD>
126
;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"> Project Code: </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"> Due Date: </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"> Priority: </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
127
;selected = ( data.priority == 2 ) ? 'SELECTED' : '' <OPTION `selected` VALUE="2">Medium ;selected = ( ( data.priority == 3 ) || !IsDefined( data.priority ) ) ? 'SELECTED' : '' <OPTION `selected` VALUE="3">Low </SELECT> </TD> ;else <TD NOWRAP> ;switch( data.priority ) ; case 1 High ; end ; ; ; case 2 Medium end default Low ; end ;end </TD> ;end </TR> ;if ( a.IsEditable ) //Set up the Action row, which contains the Update and Reset //buttons. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> `[WebDoc_HTMLLabel.Action_]` </FONT></TD> <TD> <INPUT TYPE="BUTTON" VALUE="`[WebDoc_HTMLLabel.UpdateButtonLabel]`" NAME="IgnoreMe" ONCLICK="SubmitMyPage();"> <INPUT TYPE="RESET" VALUE="`[WebDoc_HTMLLabel.Reset]`"> </TD> </TR> ;end </TABLE> ;;end
128
customerpane.html
The following HTML file sets up the information required to display the Customer page when a workflow participant clicks the Customer tab on the Table Values page in the work package of an executing workflow.
;;webscript customerpane( Assoc a ) <!-- File: customerpane.html --> ;Assoc data = a.Data.Fields.Customer ;Boolean isEditable = ( a.IsEditable && !( 'Customer' in a.Data.NonEditable ) ) <TABLE BORDER="0" CELLPADDING="2" CELLSPACING="1" WIDTH="100%"> //Set up the Customer Name row. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> Customer Name: </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"> Address1: </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"> Address2: </FONT></TD> ;if ( isEditable )
129
<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"> City: </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"> State: </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"> Zip Code: </FONT></TD> ;if ( isEditable ) <TD NOWRAP> <INPUT TYPE="TEXT" SIZE="15" NAME="zip" VALUE="`data.zip`" ONCHANGE="markTaskEditDirty();"> </TD> ;else <TD>`data.zip`</TD> ;end </TR>
130
//Set up the Phone row. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> Phone: </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"> Fax: </FONT></TD> ;if ( isEditable ) <TD NOWRAP> <INPUT TYPE="TEXT" SIZE="20" NAME="fax" VALUE="`data.fax`" ONCHANGE="markTaskEditDirty();"> </TD> ;else <TD>`data.fax`</TD> ;end </TR> //Set up the Action row, which contains the Update and Reset //buttons. ;if ( isEditable ) <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> `[WebDoc_HTMLLabel.Action_]` </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
131
132
Chapter Six
133
Description Retrieves the name of the custom workflow type defined by the integer values of the wfType and wfSubType variables. If the values of the wfType and wfSubType variables correspond to the values of the wfType and wfSubType variables in the GetWFTypes() script, the corresponding workflow name is assigned to the workflow type and a variable named handled is set to TRUE. If the values of the wfType and wfSubType variables do not correspond to the values of the wfType and wfSubType variables in the GetWFTypes() script, a name is not assigned to the workflow type and the handled variable is set to FALSE. Defines the type, subtype, and name of the custom workflow types that you create. This script returns a list of Assocs. Each Assoc contains an integer that specifies the type (wfType), an integer that specifies the subtype (wfSubType), and a string that specifies the name of the workflow type. Defines the changes that you want to make to a particular workflows map definition. This script runs each time a workflow is initiated in Livelink. It does not apply to sub-workflows.
GetWFTypes()
StartWF()
After you orphan the WFCustomScriptPkg object and modify the GetWFTypeName(), GetWFTypes(), and StartWF() scripts, the Workflow Type list is exposed in the Livelink interface. This list is displayed on the General tab of a workflow maps Properties page and contains the names of the different types of workflows that you have created. The Workflow Type list lets the creators of workflow maps choose which type of workflow they want to create (that is, which custom operations they want to apply to their workflow map definition just before their workflow is initiated).
134
For more information about these scripts, see the code samples that follow. You have created the objects necessary to add a workflow type to Livelink. Now you must provide the code required to perform the custom operations. Note Because the WFCustomScriptPkg object is a superclass object, it may contain many child objects. If you want a particular custom operation to be performed by only one of those child objects, you can use the handled variable. When a custom operation needs to be performed, the child objects of the WFCustomScriptPkg object are passed the information in order. When the handled variable is set to TRUE, the operation was performed successfully; otherwise, the information is passed to the remaining child objects.
135
CBExecute()
The following code sample defines the callback script that this workflow type calls before the workflow is initiated in Livelink. This callback script displays workflow information in the Debug window when you are running Livelink Builder. The workflow information is taken from the WWork table in the Livelink database. For more information about the WWork table, see Using WAPI and OScript for Workflow Management and Status, page 13. Note If you are running Livelink on the Livelink Intranet server and you set Debug to 1, 2, or 11 in the opentext.ini file, workflow information from the WWork table in the Livelink database is displayed in the thread logs. Thread logs are stored in the /logs directory of your primary Livelink installation (for example, c:/opentext/logs).
Function Assoc CBExecute( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer subWorkID, \ Integer taskID, \ Integer returnSubWorkID, \ Integer returnTaskID, \ List info, \ Dynamic extraData ) Assoc data Dynamic retVal Boolean handled = True
//Perform a switch statement on the callback scripts //identifier. If it is handled by this script, the handled field //returns TRUE and Livelink stops searching for a script to //handle the callback scripts operation. Then the workflow //information is echoed to the Debug window in the Livelink //Builder. switch( info[ 1 ] ) case 100 List workStatus RecArray item workStatus = prgCtx.WSession().LoadWorkStatus( workID, \ subWorkID ) for item in workStatus $LLIAPI.RecArrayPkg.DumpRecArray( item ) echo() echo() end retVal = True
136
end default handled = False end end data.Handled = handled data.retVal = retVal return( data ) end
GetWFTypeName()
Before you modify the GetWFTypeName() script, you must define the workflow types in the GetWFTypes() script. For more information about defining workflow types, see GetWFTypes(), page 138. The following code sample describes how to retrieve the names of the workflow types that you define in the GetWFTypes() script:
Function Assoc GetWFTypeName( \ Integer wfType, \ Integer wfSubType ) Assoc Boolean String retVal handled = False name = Undefined
//If the wfType and wfSubType integer values are recognized, //assign the name of the corresponding workflow type and set the //handled variable to TRUE, indicating that a valid name has been //assigned to the workflow type. If the wfType and wfSubType //values are not recognized, the handled variable is set to FALSE //and the wfType and wfSubType integer values are automatically //passed to the next child object. if ( ( wfType == 1 ) && ( wfSubType == 100 ) ) name = 'Custom Workflow Type' handled = True elseif ( ( wfType == 1 ) && ( wfSubType == 101 ) ) name = 'Another Custom Workflow Type' handled = True end //Assign the results to fields in the retval Assoc, and then //return retVal. retVal.Handled = handled retVal.Name = name return( retVal ) end
137
GetWFTypes()
The following code sample describes how to define the different types of custom workflows in Livelink:
Function List GetWFTypes() Assoc data List retVal //Set the type (wfType), subtype (wfSubType), and name of the //new workflow type. Store these values in an Assoc named data, //and then add the information in the Assoc to a list named //retVal. data.Type = 1 data.SubType = 100 data.Name = 'Custom Workflow Type' retVal = { data } //Create a new Assoc which will store the values of the next //custom workflow type. data = Assoc.CreateAssoc() //Set the type (wfType), subtype (wfSubType), and name of //another new workflow type. Store these values in the new data //Assoc. data.Type = 1 data.SubType = 101 data.Name = 'Another Custom Workflow Type' //Append the information in the data Assoc to the end of the //retVal list, and then return the list. retVal = { @retVal, data } return( retVal ) end
StartWF()
The following code sample contains the custom operations that you want to associate with each workflow type you create. This script tells Livelink to append the current date to the name of a Custom Workflow Type workflow in the Livelink Title bar and to run a callback script before a Custom Workflow Type workflow is initiated in Livelink.
Function Assoc StartWF( \ Object prgCtx, \ Record mapInfo, \ String name, \ List additions ) Assoc List retVal cbInfo
138
//Append the current date to the name of a workflow. name = name + ' - ' + Str.String( Date.Now() ) //Perform custom operations for each workflow type that you have //defined. This script defines the callback script that is run //before the initiation of a Custom Workflow Type workflow. if ( ( mapInfo.MAPINFO.TYPE == 1 ) && ( mapInfo.MAPINFO.SUBTYPE \ == 100 ) ) cbInfo = mapInfo.MAPINFO.INITIATECB cbInfo = ( IsDefined( cbInfo ) ) ? cbInfo : {} cbInfo = { @cbInfo, { 100, Undefined } } mapInfo.MAPINFO.INITIATECB = cbInfo end //Populate the variables in the retVal Assoc, and then return //retVal. retVal.Name = name retVal.Additions = additions retVal.OK = True return( retVal ) end
139
140
Chapter Seven
141
Submap
To create an event trigger script, you determine which type of event trigger script can handle the operation that you want to perform, and then orphan one of the following event trigger objects: WFMain:WFRoot:CallbackEventScripts:GeneralCallbackScripts WFMain:WFRoot:CallbackEventScripts:PerformerCallbackScripts WFMain:WFRoot:CallbackEventScripts:SubmapCallbackScripts Each event trigger object is registered in a subsystem. General event trigger objects are registered in $WFMain:WFCBGeneralSubsystem. Performer event trigger objects are registered in $WFMain:WFCBPerformerSubsystem. Submap event trigger objects are registered in $WFMain:WFCBSubmapSubsystem. You determine the custom operations that can be performed throughout the execution of a workflow by creating event trigger scripts that are contained in the event trigger objects. You can create multiple event trigger scripts within an event trigger objecteach of which extends the functionality of a Livelink workflow in different ways. Livelink becomes aware of the event trigger objects when you register them in their subsystems. The event trigger interface (that is, the Event Scripts tab) is also exposed in the Livelink interface when you register the event trigger objects in their subsystems. The event trigger interface lets the creator of a workflow map associate an event trigger script with a specific workflow event. Behind the scenes, the creator of the workflow map specifies the data that is stored in the correct callback column in the WAPI database table. For more information about how Livelink handles callback events, see Understanding Callback Events in Livelink Workflow, page 21. Note After you install the custom module in which the event trigger functionality is contained, the creator of a workflow map can access the
142
Event Scripts tab when editing a workflow step definition in Livelink. The Event Scripts tab can also be accessed from the workflows Properties page. Use event trigger scripts for operations that require more data than is contained in the work package. If the operations require additional input from a workflow participant, consider creating a new task type. For more information about adding task types, see Adding New Task Types, page 31.
143
Description Contains a Boolean value. When set to TRUE, the fEnabled feature lets you register the object with which it is associated in WFMain:WFCBGeneralSubsystem. Contains a String value. This feature stores the name that you want to represent the event trigger script on the Event Scripts tab in the Livelink interface. By default, the name that you give to the script is used to name the option on the Event Scripts tab in the Livelink interface; however, you can customize the name that is displayed by creating this String feature.
fScriptName
144
Notes If the names of the scripts that you add to the GeneralCallbackScripts object do not begin with a 0 or an underscore (_), they are displayed on the Event Scripts tab in the Livelink interface as options for Livelink users to attach to general workflow events. If the names of your scripts begin with a 0 or an underscore (_), they are not displayed in the Livelink interface, but can be used as utility scripts that are referenced by the event trigger scripts. If you attach a general event trigger script to a Step Ready event and the event trigger script returns a value of TRUE, the step is automatically completed. After you set the features associated with the GeneralCallbackScripts object, you can create the general event trigger scripts. A prototype for the general event trigger scripts is stored in the ODocumentation() script which is contained in the GeneralCallbackScripts object. General event trigger scripts must return a Boolean valueTRUE for success or FALSE for failure.
Table 7-3: Script Associated With the GeneralCallbackScripts Object Script
ScriptName()
After you orphan the general event trigger object, set the fEnabled feature, and create the general event trigger scripts, you must run the BuildOSpace() script in the Globals object of your custom OSpace and restart the Livelink Builder to register your changes. The BuildOSpace() script registers the GeneralCallbackScripts object in WFMain:WFCBGeneralSubsystem and exposes the Event Scripts tab in the Livelink interface. When you install the custom module in which the general event trigger functionality is contained, you can view your changes in the Livelink interface. The names that you specified in the fScriptName features of the GeneralCallbackScripts object are displayed as options in the lists associated with general workflow events on the Event Scripts tab. This lets you add general event trigger scripts to general workflow events. Note For more information about creating workflows in Livelink, see the Livelink online help.
145
ChangeAttribute()
The following code sample describes how to update an attribute in a workflow.
Function Boolean ChangeAttribute( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer subWorkID, \ Integer taskID, \ Integer returnSubWorkID, \ Integer returnTaskID, \ Dynamic extraData = Undefined ) //The following variable declarations are taken from the //prototype for the general event trigger scripts. This //information is stored in the 0Documentation() script //that is contained in the GeneralCallbackScripts object in your
146
//custom module. RecArray Record Boolean Boolean attribs r found = False success = True
//The following variable declaration references the attributes //API object. This object contains the scripts that store data in //the database, retrieve data from the database, and delete data //in the database. All of the different types of data that pass //through a workflow have an attributes API object. The //attributes API objects are all children or orphans of //WFMain:WFRoot:WFObjectTypes:WFDataTypes. Object obj = $WFMain.WFPackageSubsystem.GetItemByName( \ 'WFAttributes' ) //The following variable declaration calls the script that loads //the different types of data that are flowing through the //workflow. RecArray array = $WFMain.WAPIPkg.LoadWorkData( prgCtx, work )
//Walk through all the different types of data that are flowing //through this workflow, (for example, comments, attachments, //forms, attributes) and locate the workflow attributes. if ( IsDefined( obj ) ) for r in array if ( { r.TYPE, r.SUBTYPE } == { obj.fType, obj.fSubType } ) found = True break end end //When the attributes RecArray is found, store it in attribs. //Then reset the found variable so that it can be used again. if ( found ) attribs = r.USERDATA found = False //Walk through the attributes and locate the one that you //want to update. Then update its value. for r in attribs if ( r.Name == 'desired attribute' ) r.Value = 'Some new value' found = True break end end //When the attribute is updated, save the new workflow data. if ( found ) success = obj.SaveWorkData( prgCtx, work, Undefined, \ attribs )
147
Notes You can use breakpoints to debug event trigger scripts in the same way that you use breakpoints to debug other scripts in the Livelink Builder; however, placing a breakpoint in an event trigger script always returns a workflow error. Some calls in the event trigger scripts (especially those that use WAPIWORK) will fail if you have added breakpoints. When you finish debugging event trigger scripts, you must remove the breakpoints that you have added. Another way to debug callback scripts is to send messages to the Builders Debug window using the Echo function. For more information about the Echo function, see the Livelink Builder Developers Guide.
148
Description Contains a Boolean value. When set to TRUE, the fEnabled feature lets you register the object with which it is associated in WFMain:WFCBPerformerSubsystem. Contains a String value. This feature stores the name that you want to represent the event trigger script on the Event Scripts tab in the Livelink interface. By default, the name that you give to the script is used to name the option on the Event Scripts tab in the Livelink interface; however, you can customize the name that is displayed by creating this String feature.
fScriptName
Note If the names of the scripts that you add to the PerformerCallbackScripts object do not begin with a 0 or an underscore (_), they are displayed on the Event Scripts tab in the Livelink interface as options for Livelink users to attach to the Assign Step Performer workflow event. If the names of your scripts begin with a 0 or an underscore (_), they are not displayed in the Livelink interface, but can be used as utility scripts that are referenced by the event trigger scripts. After you set the features associated with the PerformerCallbackScripts object, you can create the performer event trigger scripts. A prototype for the performer event trigger scripts is stored in the ODocumentation() script which is contained in the PerformerCallbackScripts object. If successful, these scripts return the ID of a Livelink user or group to which the workflow step is assigned when it becomes ready; otherwise, these scripts return errors.
Table 7-5: Script Associated With the PerformerCallbackScripts Object Script
ScriptName()
After you create and edit the performer event trigger scripts, you must run the BuildOSpace() script in the Globals object of your custom OSpace and restart the Livelink Builder to register your changes. The BuildOspace() script registers the
149
PerformerCallbackScripts object in WFMain:WFCBPerformerSubsystem and exposes the Event Scripts tab in the Livelink interface.
When you install the custom module in which the performer event trigger functionality is contained, you can view your changes in the Livelink interface. The names that you specified in the fScriptName features of the PerformerCallbackScripts object are displayed as options in the Assign Step Performer list on the Events Script tab. This lets you add performer event trigger scripts to Assign Step Performer workflow events. Note For more information about creating workflows in Livelink, see the Livelink online help.
150
ChooseUser()
The following code sample describes how to identify the user to which the next step in a workflow is assigned.
Function Integer ChooseUser( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer subWorkID, \ Integer taskID, \ Integer returnSubWorkID, \ Integer returnTaskID, \ Integer performerID = Undefined ) //The following variable declarations are taken from the //prototype for performer event trigger scripts. This //information is stored in the ODocumentation() script associated
151
//with the PerformerCallbackScripts object in your custom module. Dynamic RecArray RecArray Record String Boolean Boolean userID attribs performer r userName found = False success = True
//The following variable declaration references the attributes //API object. This object contains the scripts that store data in //the database, retrieve data from the database, and delete data //in the database. All of the different types of data that pass //through a workflow have an attributes API object. The //attributes API objects are all children or orphans of //WFMain:WFRoot:WFObjectTypes:WFDataTypes. Object obj = $WFMain.WFPackageSubsystem.GetItemByName( \ 'WFAttributes' ) Object uSession = prgCtx.USession() Object wSession = prgCtx.WSession() //Call the script that loads the different types of data that are //flowing through the workflow (for example, comments, //attachments, forms, attributes). RecArray array = $WFMain.WAPIPkg.LoadWorkData( prgCtx, work )
//Walk through all the different types of data that are flowing //through this workflow, (for example, comments, attachments, //forms, attributes) and locate the workflow attributes. if ( IsDefined( obj ) ) for r in array if ( { r.TYPE, r.SUBTYPE } == { obj.fType, obj.fSubType } ) found = True break end end //When the attributes RecArray is found, store it in attribs. //Then reset the found variable so that it can be used again. if ( found ) attribs = r.USERDATA found = False //Walk through the attributes RecArray looking for the //UserName attribute. for r in attribs if ( r.Name == 'UserName' ) userName = r.Value found = True break end end //When the UserName attribute is found, retrieve the UserID
152
//variable. if ( found ) performer = UAPI.GetUser( uSession.fSession, userName ) //When the UserID variable is found, set its value. if ( IsNotError( performer ) ) userID = performer[ 1 ].ID else userID = performer end end end end //If the userID variable is not found, return an error. You can //set the error to Error.Get( 600 ), which is a generic workflow //error. If you want to display a more specific error message, //you can set fErrorMsg in the WSession object. if ( !IsDefined( userID ) ) userID = Error.Get( 600 ) wSession.fErrorMsg = "Could not find the attribute UserName" +\ "to determine user." end return( userID ) end
Notes You can use breakpoints to debug event trigger scripts in the same way that you use breakpoints to debug other scripts in the Livelink Builder; however, placing a breakpoint in an event trigger script always returns a workflow error. Some calls in the event trigger scripts (especially those that use WAPIWORK) will fail if you have added breakpoints. When you finish debugging event trigger scripts, you must remove the breakpoints that you have added. Another way to debug callback scripts is to send messages to the Builders Debug window using the Echo function. For more information about the Echo function, see the Livelink Builder Developers Guide.
153
Description Contains a Boolean value. When set to TRUE, the fEnabled feature lets you register the object with which it is associated in the WFMain:WFCBSubmapSubsystem. Contains a String value. This feature stores the name that you want to represent the event trigger script on the Event Scripts tab in the Livelink interface. By default, the name that you give to the script is used to name the option on the Event Scripts tab in the Livelink interface; however, you can customize the name that is displayed by creating this String feature.
fScriptName
Note If the names of the scripts that you add to the SubmapCallbackScripts object do not begin with a 0 or an underscore (_), they are displayed on the Event Scripts tab in the Livelink interface as options for Livelink users to attach to the Determine Sub-Map To Use workflow event. If the names of your scripts begin with a 0 or an underscore (_), they are not displayed in the Livelink interface, but can be used as utility scripts that are referenced by the event trigger scripts. After you set the features associated with the SubmapCallbackScripts object, you can create the submap event trigger scripts. A prototype for the submap event trigger scripts is stored in the ODocumentation() script associated with the SubmapCallbackScripts object. If successful, these scripts return an integer representing the ID of a workflow map definition (as stored in WAPI) that is initiated as a sub-workflow when the corresponding step becomes ready; otherwise these scripts return errors.
Table 7-7: Script Associated With the SubmapCallbackScripts Object Script
ScriptName()
After you create and edit the submap event trigger scripts, you must run the BuildOSpace() script in the Globals object of your custom OSpace, and restart the Livelink Builder to register your changes. The BuildOSpace() script registers the GeneralCallbackScripts object in WFMain:WFCBSubmapSubsystem and exposes the Event Scripts tab in the Livelink interface.
154
When you install the custom module in which the submap event trigger functionality is contained, you can view your changes in the Livelink interface.The names that you specified in the fScriptName features of the SubmapCallbackScripts object are displayed as options in the Determine Sub-Map To Use list on the Event Scripts tab. This lets you add submap event trigger scripts to Determine Sub-Map To Use workflow events. Note For more information about creating workflows in Livelink, see the Livelink online help.
155
subworkflow()
The following code sample describes how to define a sub-workflow map on-the-fly and save the map definition in WAPI.
Function Integer subworkflow( \ Object prgCtx, \ WAPIWORK work, \ Integer workID, \ Integer subWorkID, \ Integer taskID, \ Integer returnSubWorkID, \ Integer returnTaskID, \ Dynamic extraData = Undefined ) //The following variable declarations are taken from the //prototype for submap event trigger scripts. This information //is stored in the ODocumentation() script associated with the //SubmapCallbackScripts object in your custom module. Assoc taskData
156
Dynamic user Dynamic mapID Integer flags List dispositions RecArray attrRec Record mapRec Record newTask Boolean Object Object Object Object Object Object ok = True mapPkg = $WFMain.WFMapPkg pkgSubSystem = $WFMain.WFPackageSubsystem recArrayPkg = $LLIAPI.RecArrayPkg taskSubSystem = $WFMain.WFTaskSubsystem uSession = prgCtx.USession() wSession = prgCtx.WSession()
//Set the position of the first step in the sub-workflow map. //Because the Start step is just a placeholder step, it is not //counted as the first step in a sub-workflow. Point pos = Point( 100, 50 )
//Retrieve a generic map record to use as the base for the sub//workflow map. You can also retrieve a standard, empty map that //has the Start step, Attributes, Attachments, and Comments data //types added by calling //mapRec = mapPkg.VFMakeNewFreshMap( prgCtx, \ //Assoc.CreateAssoc() ) mapRec = mapPkg.CreateMapRec() //Specify a title for the sub-workflow map. mapRec.MAPINFO.TITLE = 'The on-the-fly sub-workflow' //Add a Start step to the sub-workflow map. mapPkg.VFAddNewTask( \ prgCtx, \ taskSubsystem.GetItemByName( 'WFStartTask' ), \ mapRec, \ Undefined, \ Point( 20, 50 ) ) //Add the Attributes and Comments data types to the sub-workflow //map. mapPkg.VFAddPackage( \ prgCtx, \ pkgSubsystem.GetItemByName( 'WFComments' ), \ mapRec, \ Undefined ) mapPkg.VFAddPackage( \ prgCtx, \ pkgSubsystem.GetItemByName( 'WFAttributes' ), \ mapRec, \ Undefined ) //Add attributes to the Attribute data type.
157
attrRec = mapRec.WORK_PACKAGES[ 2 ].USERDATA //attrRec is a RecArray that stores the attributes. There are //five columns in attrRec: Name, DataType, DisplayType, //ValidValues, and Value. RecArray.AddRecord( attrRec, { 'Name', StringType, 'Field', {}, \ Undefined } ) RecArray.AddRecord( attrRec, { 'Due Date', DateType, 'Field', \ {}, Undefined } ) RecArray.AddRecord( attrRec, { 'Country', StringType, 'Popup', \ {'Canada','Germany','USA'}, 'USA' } ) RecArray.AddRecord( attrRec, { 'Critical Report?', BooleanType, \ 'Checkbox', {}, 0 } ) //Add a step for each user to whom the data should be sent. The //first step is assigned to the Admin user. user = UAPI.GetUser( uSession.fSession, 'Admin' ) if ( !IsError( user ) ) user = user[ 1 ] newTask = mapPkg.VFAddNewTask( \ prgCtx, \ taskSubsystem.GetItemByname( 'GenericUserTask' ), \ mapRec, \ user, \ pos ) //Set any additional information for the step. //Specify the title of the step. taskData.TITLE = 'Step for Admin' //Specify the duration of the step in seconds (for example, one //day is 86400 seconds. taskData.DUEDURATION = 86400 //Specify the instructions for the step. taskData.INSTRUCTIONS = 'Work on this step' //Specify the options associated with the step. In this case //give the user permission to see all comments, to send the //task for review, to delegate permissions, and to require //dispositions. flags flags flags flags |= |= |= |= $WFPComments $WFPReview $WFPDelegate $WFPDisposition
dispositions = { 'Approve', 'Reject' } taskData.USERFLAGS = { flags, { dispositions, 1 } } //taskData.USERFLAGS[1] contains the permission flags. //USERFLAGS[2][1] contains a list of string dispositions.
158
//USERFLAGS[2][2] contains the ordinal number of the //defaulted disposition. //Specify the attribute data. Assoc formData List visibleAttrs = { 'Name', 'Due Date', 'Country', \ 'Critical Report?' } List requiredAttrs = { 'Name', 'Country' } List nonEditableAttrs = {} formData.VISIBLE_ATTRIBS = visibleAttrs formData.REQUIRED_ATTRIBS = requiredAttrs formData.NONEDITABLE_ATTRIBS = nonEditableAttrs taskData.FORM = formData //Specify all remaining data for the step. SetTaskData( newTask, taskData ) //Add a link between the Start step and this step. mapPkg.AddLinkRecord( \ mapRec.TASKS, \ mapRec.LINKS, \ mapRec.TASKS[ 1 ], \ mapRec.TASKS[ 2 ], \ 0 ) else ok = False //Specify the error message that is displayed if the userID //value is not found. wSession.fErrorMsg = 'Could not find Admin user.' end if ( ok ) //Generate a new position for the next step in the sub//workflow. pos += Point( 75, 0 ) //Add an Initiator step to the sub-workflow map. newTask = mapPkg.VFAddNewTask( \ prgCtx, \ taskSubsystem.GetItemByname( 'InitiatorTask' ), \ mapRec, \ Undefined, \ pos ) //Specify the title of the step. taskData.TITLE = 'Step for Initiator' //Specify all the data required for the step.
159
SetTaskData( newTask, taskData ) //Add a link between the User step and this step. mapPkg.AddLinkRecord( \ mapRec.TASKS, \ mapRec.LINKS, \ mapRec.TASKS[ 2 ], \ mapRec.TASKS[ 3 ], \ 0 ) end if ( ok ) RecArray array
//Generate a new position for the next step in the sub//workflow. pos += Point( 75, 0 ) //Add an Evaluate step to the sub-workflow map. newTask = mapPkg.VFAddNewTask( \ prgCtx, \ taskSubsystem.GetItemByname( 'Conditional' ), \ mapRec, \ Undefined, \ pos ) //Specify the title of the step. newTask.TITLE = 'Evaluate' array = newTask.CONDITIONCB //array is a RecArray that stores the evaluation information. //There are six columns in the RecArray: Type, Name, Condition, //Value, Display, and Conjunction. RecArray.AddRecord( array, \ { 2, 'Step for Initiator', '=', 'Approve', \ 'Step for Initiator = Approve', ' and' } ) RecArray.AddRecord( array, { { 1, 3 }, 'Country', '=', \ 'USA', 'Country = USA', Undefined } ) //Specify all the data required for the step. SetTaskData( newTask, taskData ) //Add a link between the Initiator step and this step. mapPkg.AddLinkRecord( \ mapRec.TASKS, \ mapRec.LINKS, \ mapRec.TASKS[ 3 ], \ mapRec.TASKS[ 4 ], \ 0 ) end if ( ok )
160
//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, \
161
mapRec.LINKS, \ mapRec.TASKS[ 4 ],\ mapRec.TASKS[ 6 ], \ WAPI.MAPTASK_FALSELINKS ) end if ( ok ) //Save the sub-workflow map definition to WAPI. mapID = $WFMain.WAPIPkg.SaveSubMap( prgCtx, mapRec ) end if ( !ok ) mapID = Error.Get( 600 ) end return( mapID ) end Function Void SetTaskData( \ Record task, \ Assoc data ) String key for key in Assoc.Keys( data ) task.( key ) = data.( key ) end end
Notes You can use breakpoints to debug event trigger scripts in the same way that you use breakpoints to debug other scripts in the Livelink Builder; however, placing a breakpoint in an event trigger script always returns a workflow error. Some calls in the event trigger scripts (especially those that use WAPIWORK) will fail if you have added breakpoints. When you finish debugging event trigger scripts, you must remove the breakpoints that you have added. Another way to debug callback scripts is to send messages to the Builders Debug window using the Echo function. For more information about the Echo function, see the Livelink Builder Developers Guide.
162
Contains a Boolean value. When set to TRUE, this feature works with the FactoryName() script to allow the Admin user (Livelink Administrator) to restrict access to event trigger scripts. Returns a string that identifies the event trigger scripts to which you want to restrict access
FactoryName()
To restrict access to event trigger scripts: 1. Locate the event trigger object that contains the event trigger script to which you want to restrict access in your custom module. 2. Set the fObjectFactory feature to TRUE. 3. Type the string value that you want to return in the FactoryName() script. This string value is displayed in the Usage Privileges section of the Administer Object and Usage Privileges page. Tip You can use the name that you specified in the fScriptName feature by referencing that feature (.fScriptName) in the FactoryName() script. 4. In the Custmod Globals object, run the BuildOSpace() script 5. Save and export your OSpace, and then exit and restart the Livelink Builder. You can restrict access to general event trigger scripts, performer event trigger scripts, and submap event trigger scripts individually by setting different return values for the FactoryName() scripts for each event trigger object. You can restrict access to all general
163
event trigger scripts, performer event trigger scripts, and submap event trigger scripts together by setting the same return values for the FactoryName() scripts for each event trigger object. After you modify the fObjectFactory features and the FactoryName() scripts contained in the event trigger objects for the event trigger scripts to which you want to restrict access, you must run the BuildOSpace() script in the Globals object of your custom OSpace. Then, the Admin user can go to the Administer Privileges section of the Livelink Intranet Administration page and specify which Livelink users can attach the event trigger scripts to workflow events. Suppose that you create general, performer, and submap event trigger scripts in your custom module and you want to restrict access to the performer and submap event trigger scripts. You can set the fObjectFactory feature to TRUE and modify the FactoryName() script to return the same string value in the performer and submap event trigger objects. This means that the Admin user can specify which Livelink users can attach the performer and submap event trigger scripts to workflow events. In this case, certain Livelink users can attach the performer and submap event trigger scripts to workflow events and all Livelink users can attach the general event trigger scripts to workflow events.
164
Appendix A
165
The fTaskName feature holds the text that is displayed in the Workflow Painter for the form task type. 5. Override the following scripts: GetPainterInfo() ReadyTaskForInitiation() SetTaskDefaults() SetTaskRecFromMapTask() For more information about these scripts, see the code samples that follow. You have created the objects necessary to define a task types API. Now you must provide the code required to customize the API object for the form task type.
GetPainterInfo()
The following code sample describes the default values of the GetPainterInfo() script.
Function Dynamic GetPainterInfo( \ Object prgCtx, \ Record task, \ Dynamic context = Undefined ) //Store the name of the task type in a dynamic variable named //retVal. By default, this is the name that you specified in the //fTaskName feature of the FormTaskAPI object (that is, Custom //Task Type). Dynamic retVal = Str.String( task.TITLE ) return( retVal ) end
166
ReadyTaskForInitiation()
The following code sample describes how to prepare the task for initiation in Livelink. It stores callback scripts and any other additional data that is required by the form task type throughout the execution of the workflow.
Function Boolean ReadyTaskForInitiation( \ Object prgCtx, \ Record r, \ RecArray workPkg ) Assoc expandInfo List cbInfo List prevCBs RecArray user Boolean success = True Object const = $WFMain.WFConst Object uSession = prgCtx.USession() Record userData = $WFMain.WFMapPkg.CreateTaskUserDataRec() userData.TYPE = r.TYPE userData.SUBTYPE = r.SUBTYPE userData.PERMFLAGS = r.USERFLAGS r.USERDATA = userData //Add a standard Done callback script. This callback script is //called when the task is completed and the workflow is routed to //the next workflow participant. The Done callback script //instructs all of the data types in the workflow to make a copy //of the information that was entered at this step. The form data //type stores the information in a separate table so that each //version is available at the end of a workflow. if ( IsDefined( r.DONECB ) ) cbInfo = { @r.DONECB, { const.kCBSetTaskDoneData, Undefined } } else cbInfo = { { const.kCBSetTaskDoneData, Undefined } } end r.DONECB = cbInfo //If the performer of the task is a group, add a callback script //that identifies the group name. Then, if the group task is //part of a loopback, the task is reassigned to the whole group //when the route loops back (and not to the individual group //member who initially accepted the task). if ( IsDefined( r.PERFORMERID ) ) if ( r.PERFORMERID > 0 ) user = UAPI.GetByID( uSession.fSession, r.PERFORMERID ) if ( !IsError( user ) ) if ( user[ 1 ].TYPE != UAPI.USER ) prevCBs = r.PERFORMERCB prevCBs = ( IsDefined( prevCBs ) ) ? prevCBs : {} r.PERFORMERCB = { { const.kCBSetGrpStepPerformer, r.PERFORMERID }, @prevCBs } end end
167
else //If the performer is Undefined, add a callback script that //assigns the task to the initiator of the workflow. r.PERFORMERCB = { { const.kCBGetInitiator, Undefined } } r.PERFORMERID = Undefined end end //If the performer of the task is a group, and that group should //be expanded so that the task is assigned to all members of the //group, add a Submap callback script. This Submap callback //script creates a sub-workflow that expands the members of the //group. if ( IsSet( r.EXATTS.GroupFlags ) && \ ( r.EXATTS.GroupFlags != const.kWFGroupStandard ) ) expandInfo.Type = r.TYPE expandInfo.SubType = r.SUBTYPE expandInfo.Flag = r.EXATTS.GroupFlags r.SUBMAPIDCB = { { const.kCBExpandGroup, expandInfo } } end //Verify that a form has been selected for display with this task //type. if ( IsDefined( r.FORM ) && IsDefined( r.FORM.FORM_DISPLAYFORM )) r.FORM.FORM_FORMS = { r.FORM.FORM_DISPLAYFORM } else success = False end return( success ) end
SetTaskDefaults()
The following code sample describes how to specify the default information required by Livelink to recognize the form task type. This is also where you specify data that must be present if the creator of the workflow does not edit the task before the workflow is initiated.
Function Void SetTaskDefaults( \ Object prgCtx, \ Record taskRec, \ Dynamic context = Undefined ) //Set values for the default information required by Livelink to //recognize the form task type. The fType and fSubType values //hold unique integers that identify the task type. taskRec.TYPE = .fType taskRec.SUBTYPE = .fSubType taskRec.EXATTS = Assoc.CreateAssoc( Assoc.NotSetValue() ) taskRec.CUSTOMDATA = Assoc.CreateAssoc( Assoc.NotSetValue() ) //Specify the name of the task and the performer ID associated
168
//with the task. These values are displayed as default values on //the Step Definition page when a form task is edited for the //first time. taskRec.TITLE = Str.String( .fTaskName ) taskRec.PERFORMERID = Undefined end
SetTaskRecFromMapTask()
The following code sample describes how to convert a form task that has been prepared for initiation to a workflow map definition task.
Function Void SetTaskRecFromMapTask( \ WAPIMAPTASK task, \ Record r, \ Record taskData ) List Dynamic Object Integer cbInfo data = task.pUserData const = $WFMain.WFConst groupFlag = const.kWFGroupStandard
//Convert the values stored in the WAPIMAPTASK to the //corresponding fields in the map definition task. r.TYPE = data.TYPE r.SUBTYPE = data.SUBTYPE r.USERFLAGS = data.PERMFLAGS r.SUBMAPID = task.pSubMapID r.PERFORMERID = taskData.WORK.SUBWORKTASK_PERFORMERID r.READYCB = task.pReadyCB //Remove the Done callback script. r.DONECB = task.pDoneCB r.DONECB = $WFMain.WFMapPkg.RemoveCBInfoType( r.DONECB, \ const.kCBSetTaskDoneData ) r.KILLCB = task.pKillCB //Remove the Performer callback script. r.PERFORMERCB = task.pPerformerCB r.PERFORMERCB = $WFMain.WFMapPkg.RemoveCBInfoType( \ r.PERFORMERCB, const.kCBSetGrpStepPerformer ) r.CONDITIONCB = task.pConditionCB //Convert the values stored in the WAPIMAPTASK to the //corresponding fields in the map definition task. r.FORM = task.pForm r.PAINTER = task.pPainter r.STARTDATE = task.pStartDate r.DUEDURATION = task.pDueDuration r.DUEDATE = task.pDueDate r.DUETIME = task.pDueTime
169
r.FLAGS = task.pFlags r.TITLE = taskData.WORK.SUBWORKTASK_TITLE r.DESCRIPTION = task.pDescription r.INSTRUCTIONS = task.pInstructions r.PRIORITY = task.pPriority //Walk through the SubMap callback scripts and search for a //callback script that expands group members. If this type of //callback script is found, determine whether the callback script //expands the group and its subgroups or whether the callback //script expands only the first level of group members. r.SUBMAPIDCB = task.pSubMapIdCB for cbInfo in r.SUBMAPIDCB if ( cbInfo[ 1 ] == const.kCBExpandGroup ) groupFlag = cbInfo[ 2 ].Flag break end end //Remove the callback script that expands group members and //store the expand group flag value in the appropriate field of //the r.EXATTS Assoc. r.SUBMAPIDCB = $WFMain.WFMapPkg.RemoveCBInfoType( r.SUBMAPIDCB, \ const.kCBExpandGroup ) r.EXATTS = Assoc.CreateAssoc( Assoc.NotSetValue() ) r.EXATTS.GroupFlags = groupFlag end
170
For more information about these scripts, see the code samples that follow. 5. Create an HTML script, and name it formtask.html. For more information about this HTML file, see formtask.html, page 177. You have created the object necessary to define a task types Workflow Painter information. Now you must provide the code required to customize the information for the form task type.
GetMapData()
The following code sample describes how to display the Step Definition page for this task type when the creator of a workflow map edits the task in the Workflow Painter.
function Assoc GetMapData( \ Object prgCtx, \ Integer mapID, \ Integer taskID, \ Record mapRec, \ Record request ) Assoc Assoc Assoc a paneData performerInfo
171
Assoc retVal Assoc tabPaneInfo Dynamic tmp Integer whichTab List tabList Object obj Record p Boolean Integer Record String String String knownUser = False i = 1 taskInfo = mapRec.TASKS[ taskID ] gif = 'guy.gif' name = [WebWFP_HTMLLabel.User] objName = .OSName
whichTab = ( RecArray.IsColumn( request, 'PaneIndex' ) ) ? \ Str.StringToInteger( request.PaneIndex ) : 1 //Specify the commonedittask.html file as the HTML file to //display when the creator of a workflow map edits the form task //type in the Workflow Painter. Specify the location of the //commonedittask.html file (that is, the webwfp module). retVal.HTMLFile = "commonedittask.html" retVal.ModuleName = 'webwfp' //Create an Assoc named retVal.Data and populate it with the //task and map information, including the ID of the workflow map, //the ID of the task, the URL of the next page to display, and the //header information for the task. retVal.Data = Assoc.CreateAssoc() retVal.Data.MapID = mapID retVal.Data.TaskID = taskID retVal.Data.NextURL = request.NextURL retVal.Data.HeaderLabel = 'Form Step Definition' retVal.Data.HeaderArgs = $WebWork.WFPkg.GetHeaderTypeArgs( \ 'PAINT', $WebWork.WFPkg.GetMapName( prgCtx, mapID, mapRec ) ) //Create an Assoc named tmp that stores all of the data required //to paint the first tab that appears when the creator of a //workflow map double-clicks the form task icon in the Workflow //Painter (that is, the General tab). tmp = Assoc.CreateAssoc() tmp.Label = [WebWork_HTMLLabel.General] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( $WebDSP.HTMLPkg.ArgsToURL( request ), i ) tmp.HelpKey = objName tmp.Active = FALSE //This task type uses the commonedittask.html file. This HTML //file expects to be passed a list of tab names, along with a //list of the data to be displayed on each tab. TabList is a list //of Assocs that lists each tab to be displayed. There is another //list of Assocs that identifies the panes to be displayed with //each tab. This second list of Assocs contains the HTML //information and all other information that the pane needs to //draw itself.
172
tabPaneInfo.TabList = { tmp } a = Assoc.CreateAssoc() a.TaskInfo = taskInfo a.Gif = '16form.gif' a.MapID = mapID a.TaskID = taskID - 1 a.Forms = GetFormNames( prgCtx, mapRec ) a.NextURL = request.NextURL //Retrieves the name of the performer of the task and a .gif file //that represents the performer type (that is, a user or a //group). If the step is assigned to the initiator of the //workflow, then <Initiator> is returned as the performers name. if ( IsDefined( taskInfo.PERFORMERID ) ) if ( taskInfo.PERFORMERID == 0 ) name = [WebWFP_Label.LtInitiatorGt] else tmp = UAPI.GetByID( prgCtx.USession().fSession, \ taskInfo.PERFORMERID ) if ( !IsError( tmp ) ) knownUser = True name = tmp[ 1 ].NAME if ( tmp[ 1 ].TYPE != UAPI.USER ) gif = '2-guys.gif' end end end end performerInfo.Name = name performerInfo.Gif = gif performerInfo.KnownUser = knownUser performerInfo.ID = taskInfo.PERFORMERID a.PerformerInfo = performerInfo //Create an Assoc named tmp that stores the name of your custom //module, the HTML file to display, and the data that appears on //the General tab. tmp = Assoc.CreateAssoc() tmp.ModuleName = 'custmod' tmp.HTMLFile = 'formtask.html' tmp.Data = a tabPaneInfo.PaneList = { tmp } i += 1 //List the callback scripts that fire, if applicable. Assoc eventInfo List fields = { 'PERFORMERCB', 'READYCB', 'DONECB', 'KILLCB', \ 'RESURRECTCB' } List events = { [WebWFP_HTMLLabel.AssignStepPerformer], \ [WebWFP_HTMLLabel.StepBecomesReady], \ [WebWFP_HTMLLabel.StepIsDone], \ [WebWFP_HTMLLabel.StepIsKilled], \ [WebWFP_HTMLLabel.StepIsResurrected] }
173
eventInfo.Events = events eventInfo.FieldNames = fields eventInfo = $WFMain.WFMapPkg.GetValidEvents( prgCtx, eventInfo ) if ( eventInfo.NumberOfEvents > 0 ) tmp = Assoc.CreateAssoc() tmp.Label = [WebWFP_HTMLLabel.EventScripts] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( request ), i ) tmp.HelpKey = objName + "." + 'EventScripts' tmp.Active = FALSE tabPaneInfo.TabList = { @tabPaneInfo.TabList, tmp } a = Assoc.CreateAssoc() a.EventInfo = eventInfo a.DataRec = taskInfo tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwfp' tmp.HTMLFile = 't_events.html' tmp.Data = a tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, tmp } end //Store the pane information for the tabs in the data Assoc. Then //set the active tab. By default, the active tab is 1 (or //whichever tab was originally passed into the script). if ( ( whichTab < 2 ) || ( whichTab > Length( \ tabPaneInfo.TabList ) ) ) whichTab = 1 end tabPaneInfo.TabList[ whichTab ].Active = True retVal.Data.TabInfo = tabPaneInfo retVal.Data.Tab = whichTab return( retVal ) end //Retrieves the names of the forms that have been added to the //workflow map, so that they can be displayed in the Form To //Display list on the Step Definition page. Function List GetFormNames( \ Object prgCtx, \ Record mapRec ) Assoc tmp List retVal Record r Object obj = $WFMain.WFPackageSubsystem.GetItemByName( \ 'Form' ) if ( IsDefined( obj ) ) for r in mapRec.WORK_PACKAGES if ( { r.Type, r.SubType } == { obj.fType, obj.fSubType } ) for tmp in obj.ReadyForModification( prgCtx, r.USERDATA ) retVal = { @retVal, tmp.Name } retVal = { @retVal, @tmp.SubForms } end
174
break end end end //Add None as the first item in the Form To Display list on the //Step Definition page for the form task type. retVal = { [WebWFP_HTMLLabel.None], @retVal } return( retVal ) end
PutMapData()
The following code sample describes how to save the data that the creator of a workflow map enters on the Form Step Definition page.
function assoc PutMapData( \ Object prgCtx, \ Record mapRec, \ Record taskInfo, \ Record r ) Assoc paneData Assoc retVal Integer defaultDispo Integer flags Integer i Integer permAndDispositionFlags List dispositions List emptyDispos Object obj Real time Record p Integer count = 0 retVal.OK = TRUE if ( ( r.PaneIndex == 1 ) || ( r.PaneIndex == 0 ) ) //Save the step name. if ( RecArray.IsColumn( r, 'Title' ) ) taskInfo.Title = $LLIAPI.FormatPkg.ValToString( r.title ) end //Save the start date. if ( RecArray.IsColumn( r, 'StartDate' ) ) taskInfo.StartDate = ._CrackDate( r.StartDate ) end //Save the instructions. if ( RecArray.IsColumn( r, 'Instructions' ) ) taskInfo.Instructions = $LLIAPI.FormatPkg.ValToString( r.Instructions ) end
175
//Save the duration. if ( RecArray.IsColumn( r, 'Duration' ) ) if IsDefined( r.Duration ) && Length( r.Duration ) Boolean inDays = ( r.DurationUnits == "Days" ) time = $LLIAPI.FormatPkg.StringToVal( r.Duration, RealType ) if ( Type( time ) != RealType ) retVal.OK = FALSE if inDays retVal.ErrMsg = [WebWork_ErrMsg.DurationMustBeANumberOfDays] else retVal.ErrMsg = [WebWork_ErrMsg.DurationMustBeANumberOfHours] end else taskInfo.DueDuration = $LLIAPI.FormatPkg.ConvertToSeconds( inDays, time ) end else taskInfo.DueDuration = Undefined end end //Save the group options. if RecArray.IsColumn( r, "GroupFlags" ) taskInfo.EXATTS.GroupFlags = Str.StringToInteger( r.GROUPFLAGS ) end //Save the selected form. if RecArray.IsColumn( r, "FormName" ) taskInfo.Form = IsDefined( taskInfo.Form ) ? taskInfo.Form : Assoc.CreateAssoc() taskInfo.Form.FORM_DISPLAYFORM = ( r.FormName == [WebWFP_HTMLLabel.None] ) ? Undefined : r.FormName end else //Save the callback script information. $WEBWFP.WFContentManager.StoreCallbackData( taskInfo, r ) end return retVal end
176
formtask.html
The following code sample describes how to design the Web page that is displayed when the creator of a workflow map edits a form task in the Workflow Painter. You edit a form task on the Form Step Definition page, which is accessed by double-clicking the task's icon or by right-clicking the task's icon, and then clicking Edit.
//Pass Assoc.data into the formtask.html file. Assoc.data contains //all of the information required to display this Web page. ;;webscript formtask( Assoc data ) <!-- File: aaaformwfpaint/formtask.html --> ;;oscript{ Integer i List durationInfo String checked String dueDuration Dynamic taskInfo = data.TaskInfo String selForm = 'None' //Set up the URLs that define the links on the Step Definition //page (for example, the Map Editor link which jumps back to //the Workflow Painter). String nextURL = Str.Format( \ "%1?func=wfp.TaskEdit&MapID=%2&TaskID=%3&NextURL=%4", \ .URL(), data.MapID, data.TaskID, Web.Escape( data.NextURL ) ) String chooseUserURL = .url() + Str.Format( \
"?func=wfp.TaskUserSet&MapID=%1&TaskID=%2&PerformerID=%3&nextURL=% 4", \ data.MapID, data.TaskID, taskInfo.PerformerID, Web.Escape( nextURL ) ); List flags = { $WFMain.WFConst.kWFGroupStandard, \ $WFMain.WFConst.kWFGroupExpand, \ $WFMain.WFConst.kWFGroupExpandFull } List flagLabels = { [WebWFP_HTMLLabel.MemberAccept],\ [WebWFP_HTMLLabel.OneLevelExpand], \ [WebWFP_HTMLLabel.FullExpand] } ;;} //Set up the information that is displayed on the General tab of //the Form Step Definition page. This includes the title, the //performer, the group options, the form to display, and the //duration of the task. <TABLE BORDER="0" CELLPADDING="2" CELLSPACING="1"> //Set up the Step Name field. <TR>
177
<TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> `[WebWFP_HTMLLabel.StepName_]` </FONT></TD> <TD> <IMG SRC="`.ModImg( 'custmod' )``data.Gif`" WIDTH="16" HEIGHT="16" ALIGN="BOTTOM" BORDER="0" VALIGN="TOP" HALIGN="RIGHT"> <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"> `[WebWFP_HTMLLabel.AssignedTo_]` </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"> `[WebWFP_HTMLLabel.GroupOptions_]` </FONT></TD> <TD> <SELECT NAME="GroupFlags" ONCHANGE="markTaskEditDirty();"> ;for i = 1 to Length( flags ) ;if ( taskInfo.EXATTS.GroupFlags == flags[ i ] ) <OPTION VALUE="`flags[ i ]`" SELECTED>`flagLabels[ i ]` ;else <OPTION VALUE="`flags[ i ]`">`flagLabels[ i ]` ;end ;end </SELECT> </TD>
178
</TR> //Set up the Form To Display field. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> `[WebFormWF_HTMLLabel.FormToDisplay_]` </FONT></T D> ;if ( IsDefined( taskInfo.Form ) ) ;if ( IsDefined( taskInfo.Form.FORM_DISPLAYFORM ) ) ;selForm = taskInfo.Form.FORM_DISPLAYFORM ;end ;end <TD> <SELECT NAME="FormName" ONCHANGE="markTaskEditDirty();"> `%L$HTMLPkg.FmtPopupItems( data.Forms, selForm )` </SELECT> </TD> </TR> //Set up the Accept Message field. <TR> <TD bgcolor="#CCCCCC" NOWRAP valign="TOP"><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> `[WebFormWF_HTMLLabel.AcceptMessage_]` </FONT></T D> <TD> <TEXTAREA NAME="Instructions" ROWS="6" COLS="45" WRAP="SOFT" ONCHANGE="markTaskEditDirty();">`taskInfo.Instructions`</TEXTAREA> </TD> </TR> //Set up the Duration field. ;;oscript{ if IsDefined( taskInfo.DueDuration ) durationInfo = $LLIAPI.FormatPkg.ConvertFromSeconds( taskInfo.DueDuration ) dueDuration = $LLIAPI.FormatPkg.ValToString( durationInfo[2] ) else durationInfo = { TRUE, 0 } end ;;} <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> `[WebWFP_HTMLLabel.Duration_]` </FONT></TD> <TD> <INPUT TYPE="TEXT" NAME="Duration" VALUE="`dueDuration`" SIZE="5" ONCHANGE="markTaskEditDirty();">
179
;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"> `[WebWFP_HTMLLabel.StartDate_]` </FONT></TD> <TD NOWRAP> ;;call <.htmlPrefix() + 'datefield.html'>( 'StartDate', taskInfo.StartDate, TRUE, TRUE ) </TD> </TR> //Set up the Action field, which contains the Add to Workflow //Definition button. <TR> <TD bgcolor="#CCCCCC" NOWRAP><FONT FACE="`[WebDsp_Font.SansSerif]`" size="2"> `[WebDoc_HTMLLabel.Action_]` </FONT></TD> <TD> <INPUT TYPE="Submit" VALUE="`[WebWFP_HTMLLabel.AddToWorkflowDefinitionButtonLabel]`"> </TD> </TR> </TABLE> ;;end
180
181
GetDisplayPerformerInfo()
The following code sample describes how to retrieve the name and ID of the Livelink user to which the task is assigned.
Function Dynamic GetDisplayPerformerInfo( \ Object prgCtx, \ Record taskRec ) Dynamic performer Dynamic retVal Integer performerID Object uSession = prgCtx.USession() //Search for the ID of the Livelink user to which the task is //assigned. if ( RecArray.IsColumn( taskRec, 'PERFORMERID' ) ) performerID = taskRec.PERFORMERID elseif ( RecArray.IsColumn( taskRec, 'WORK' ) ) performerID = taskRec.WORK.SUBWORKTASK_PERFORMERID elseif ( RecArray.IsColumn( taskRec, 'SUBWORKTASK_PERFORMERID' ) ) performerID = taskRec.SUBWORKTASK_PERFORMERID else performerID = Undefined end //If the Livelink user ID is defined, retrieve the Livelink user //name that is associated with it. if ( IsDefined( performerID ) ) performer = UAPI.GetByID( uSession.fSession, performerID ) //If the Livelink user name is found, create an Assoc named retVal //in which you store the Livelink user name and ID. if ( !IsError( performer ) ) retVal = Assoc.CreateAssoc() retVal.ID = performer[ 1 ].ID retVal.Name = performer[ 1 ].NAME end else retVal = [WebWork_Label.User] end return( retVal ) end
GetPainterInfo()
The following code sample describes how to define the information that the Workflow Painter needs to know about the form task type.
Function Assoc GetPainterInfo( \ Object prgCtx, \ Record task = Undefined ) Assoc info Assoc linkData Assoc retVal
182
String String
//Retrieve the title and image used to represent the form task //type in the Workflow Painter. if ( IsDefined( task ) ) info = .GetDisplayInfo( prgCtx, task ) name = info.Title gif = info.Gif end retVal.ID = Str.String( .fType ) + '_' + Str.String( .fSubType ) //Specify a name for the form task in the Workflow Painter. retVal.Name = name //Specify the image that is displayed in the Workflow Painter to //represent the form task type. retVal.Gif = gif //Specify whether this task should be added to the Step Icon //Palette in the Workflow Painter. retVal.PaletteTask = .fpaletteTask //Specify whether this task can be duplicated. retVal.Duplicatable = .fDuplicatable //Specify the name of the Edit request handler for the form task //type.This request handler displays the form task when you click //the task name in your Tasks list. retVal.RHandler = 'wfp.TaskEdit' //Specify the name of the View request handler for the form //task type. This request handler displays the detailed status //for the form task when you click a task name on the Step List //tab. retVal.RHandlerWorkView = 'work.TaskDetail' //Specify the name of the Choose User request handler for the //form task type. This request handler is called when you right//click the form task icon in the Workflow Painter and then click //Choose Performer to specify the performer of the task. retVal.RHandlerChoose = 'wfp.TaskUserSet' //Specify the background color of the task in the Map Overview //window in the Workflow Painter. retVal.Background = 'flesh' //Specify the name of the module that defines this task type. retVal.Module = 'custmod'
183
//Retrieve the link information associated with the task type. //This includes the maximum number of link types that can come //from the task type and the maximum number of link types that //can go to this task type. linkData = .GetTaskTypeObj().GetLinkInfo() //Specify the maximum number of link types that can come from //this task type. Most task types can only have a single link //type coming from them (either a standard link or a loopback //link); however, a conditional step can have two link types //coming from it. retVal.MaxLinkTypes = linkData.MaxLinkTypes //Specify the type of links that can go to this task type. retVal.LinkTypesTo = linkData.LinkTypesTo //Specify the type of links that can come from this task type. retVal.LinkTypesFrom = linkData.LinkTypesFrom return( retVal ) end
GetPainterMenu()
The following code sample describes how to define the menu commands that appear when you right-click the form task types icon in the Workflow Painter:
Function List GetPainterMenu( Boolean viewonly ) //If the menu commands are not set to viewonly, populate the //following Assocs. List retval if ( !viewonly ) Assoca Assocb Assocc Assocd Assoce Assocf //Populate Assoca with the label, font, help, and userdata //values. The label is the name of the menu command, as it //appears in the popup menu that is displayed when you right//click the task type in the Workflow Painter. The font value //specifies the type of font used to display the menu command. //The help value is the text that is displayed on the Status //Bar when you position your cursor over the Edit command in //the popup menu. The userdata value identifies the request //handler that executes the Edit command. a.label = [WebWork_MenuLabel.Edit] a.font = "bold"
184
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 }
185
//If the menu commands are set to viewonly, populate Assoca with //the label, font, help, and userdata values for the read-only //menu command (View). else Assoca a.label = [WebWork_MenuLabel.View] a.font = "bold" a.help = [WebWork_MenuLabel.ViewThisStep] a.userdata = "rhandlerWorkView" //Store Assoca in a list and name the list retval. retval = { a } end return retval end
Note The GetPainterMenu() script displays the Edit, Duplicate, Choose Performer, and Delete commands on the menu that appears when you right-click the form task icon in the Workflow Painter. The Choose Performer command is separated from the rest of the commands in the menu by two separator lines.
GetStatusDisplay()
The following code sample describes how to retrieve the information that is displayed when a workflow participant clicks the task name on the Step List page. The Step List page is accessed by clicking the Step List tab on the Detailed Status page.
function Assoc GetStatusDisplay( \ Object prgCtx, \ Dynamic context, \ Dynamic data = Undefined ) Assoc a Assoc retVal Assoc tabPaneInfo Assoc tmp Integer whichTab RecArray auditInfo RecArray disposition RecArray performer String title Record Record mapRec = context.MAP_PAINTER task = context
whichTab = ( RecArray.IsColumn( data, 'PaneIndex' ) ) ? \ Str.StringToInteger( data.PaneIndex ) : 1 //Populate the tmp Assoc with Label, URL, HelpKey, and Active //values. The Label value specifies the name of the tab that you
186
//are preparing for display (General). The URL value //identifies the page to display on the General tab. The HelpKey //value specifies the help page to display for this task type. If //set to TRUE, the Active value indicates that the tab is the //active tab (currently displayed). If set to FALSE, the Active //value specifies that the General tab is not the active tab and //must be called for display. tmp.Label = [WebWork_HTMLLabel.General] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( data ), 1 ) tmp.HelpKey = 'User' tmp.Active = FALSE //Store the tmp Assoc in a list and assign it to //tabPaneInfo.TabList. tabPaneInfo.TabList = { tmp } a.Gif = '16form.gif' //Determine whether the workflow participant can reassign the task //by checking permissions. a.CanReassign = $WFMain.WAPIPkg.CheckWFPermissions( \ prgCtx, task, $WFMain.WFConst.kWFChangeWork ) //Retrieve the disposition data for the task type. disposition = $WFMain.WAPIPkg.GetDispositionData( \ prgCtx, task.SUBWORKTASK_SUBWORKID, task.SUBWORKTASK_TASKID ) //If a disposition was specified for this task, add it to the //Assoc of data that is passed to the HTML file so that it can be //displayed. if ( IsDefined( disposition ) && Length( disposition ) ) a.Disposition = Str.Quote( $LLIAPI.FormatPkg.ValToString( \ disposition[ 1 ].VALUE ), '"' ) end a.WorkRec = task tmp = Assoc.CreateAssoc() tmp.ModuleName = 'formwf' tmp.HTMLFile = 'wwtuser.html' tmp.Data = a tabPaneInfo.PaneList = { tmp } //Retrieve the audit trail, if necessary. if ( whichTab == 2 ) auditInfo = $WFMain.WAPIPkg.GetAuditRec( \ prgCtx, task.WORK_WORKID, task.SUBWORK_SUBWORKID, \ task.SUBWORKTASK_TASKID ) if ( IsError( auditInfo ) ) auditInfo = Undefined end end
187
//Add the Audit tab to the Step Detail page for this type of //task. tmp = Assoc.CreateAssoc() tmp.Label = [WebWork_HTMLLabel.Audit] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( data ), 2 ) tmp.HelpKey = 'Audit' tmp.Active = FALSE tabPaneInfo.TabList = { @tabPaneInfo.TabList, tmp } tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwork' tmp.HTMLFile = 'audittrail.html' tmp.Data = auditInfo tabPaneInfo.PaneList = { @tabPaneInfo.PaneList, tmp } tmp = .GetPackageStatusData( prgCtx, task, data, tabPaneInfo ) //Set the Active flag for the tab that is currently selected. if ( tmp.OK ) if ( ( whichTab < 2 ) || ( whichTab > Length( \ tabPaneInfo.TabList ) ) ) whichTab = 1 end tabPaneInfo.TabList[ whichTab ].Active = TRUE end //Set up an Assoc that returns all of the data required by //Livelink to draw the Step Detail page. retVal.OK = tmp.OK retVal.ErrMsg = tmp.ErrMsg retVal.HTMLFile = "wwt.html" retVal.ModuleName = 'webwork' retVal.Tab = whichTab retVal.TabInfo = tabPaneInfo retVal.Data = task //Set the masthead information so that the correct header is //displayed at the top of the Detailed Status page. retVal.HeaderArgs = $WebWork.WFPkg.GetHeaderTypeArgs( 'STATUS', \ task.SUBWORK_TITLE ) return( retVal ) end
188
GetTaskEditData()
The following code sample describes how to retrieve the information that is displayed when a workflow participant clicks the task name on the Tasks page in their Personal Workspace.
function Assoc GetTaskEditData( \ Object prgCtx, \ Record taskInfo, \ Record r ) Assoc a Assoc data Assoc paneData Assoc retVal Assoc tabPaneInfo Assoc tmp Dynamic status List tabList Object obj RecArray packages Record p String nextURL String url WAPIWORK work Boolean Boolean Integer String ok = true groupStep = False flags = WAPI.STARTTASK_FLAG_REEXECUTE formName = Undefined
//Determine whether the task has been assigned to a Livelink user //or a group. if !( $WFMain.WAPIPkg.CheckTaskAssignedToUser( prgCtx, taskInfo \ ) ) groupStep = True end //If the task is assigned to a group, set the HTMLFile, //ModuleName, and TaskInfo values. The HTMLFile value specifies //the name of the HTML file that displays the task and other data //that the form needs. The ModuleName value indicates in which //module the task type is defined. if ( groupStep ) data.HTMLFile = 'tgeneric.html' data.ModuleName = 'webwork' data.TaskInfo = taskInfo //Set the masthead information for the Tasks tab. data.HeaderArgs = $WebWork.WFPkg.GetHeaderTypeArgs( 'WORK', \ taskInfo.SUBWORK_TITLE ) //Set up the information required to display the first tab on //the Work Package page.
189
tmp.Label = [WebWork_HTMLLabel.General] tmp.URL = $WebWork.WFPkg.SetPaneIndexArg( \ $WebDSP.HTMLPkg.ArgsToURL( r ), 1 ) tmp.Active = FALSE tabPaneInfo.TabList = { tmp } a.TaskInfo = taskInfo a.GroupStep = groupStep //Use the standard General tab that lets a group member accept //the task and add it to their task list. This is the same //General tab that is used for a User task type. tmp = Assoc.CreateAssoc() tmp.ModuleName = 'webwork' tmp.HTMLFile = 'taskgeneralpane.html' tmp.Data = a tabPaneInfo.PaneList = { tmp } tabPaneInfo.TabList[ 1 ].Active = True data.TabInfo = tabPaneInfo data.Tab = 1 else //Get a work handle. work = prgCtx.WSession().AllocWork() if ( !IsError( work ) ) //Use the StartTask() script to verify that the performer of //this task can access the data associated with the task. //This script sets up the work object so that it can be used //to access the work package. You can also use the //StartTask() script to make sure that this task is ready to //be complete (and was not completed already). status = WAPI.StartTask( \ work, \ r.WorkID, \ r.SubWorkID, \ r.TaskID, \ flags ) if ( !IsError( status ) ) //Retrieve the current work package. packages = $WFMain.WAPIPkg.GetWorkPackages( prgCtx, \ work, taskInfo ) if ( !IsError( packages ) ) if ( IsDefined( taskInfo.MAPTASK_FORM ) ) formName = taskInfo.MAPTASK_FORM.FORM_DISPLAYFORM end if ( IsDefined( formName ) )
190
nextUrl = \ Str.Format('%1?func=work.taskdone&workid=%2&subworkid=%3&' + \ 'taskid=%4&xAction=save&paneindex=1&nexturl=%5', \ r.SCRIPT_NAME, r.WorkID, r.SubWorkID, r.TaskID, Web.Escape( \ r.NextURL ) ) url = \ Str.Format('%1?func=custmod.formedit&workid=%2&subworkid=%3&' + \ 'taskid=%4&formname=%5&editable=true&nexturl=%6', \ r.SCRIPT_NAME, r.WorkID, r.SubWorkID, r.TaskID, Web.Escape( \ formName ), Web.Escape( nextURL ) ) retVal.RHandler = url //If the data was not retrieved correctly (OK=FALSE), //return an error message. else ok = False retVal.ErrMsg = \ [WebFormWF_ErrMsg.CouldNotFindFormContactWorkflowManager] end //If the data was not retrieved correctly (OK=FALSE), //return an error message. else ok = False retVal.ApiError = work retVal.ErrMsg = [Web_ErrMsg2.CouldNotAccessWorkpackage] end //If the data was not retrieved correctly (OK=FALSE), //return an error message. else ok = False retVal.ApiError = work retVal.ErrMsg = [Web_ErrMsg2.CouldNotAccessWork] end //Free the memory held by the WAPIWORK object. WAPI.FreeWork( work ) else ok = False retVal.ApiError = work retVal.ErrMsg = [Web_ErrMsg2.CouldNotAllocwork] end end retVal.OK = ok retVal.Data = data return( retVal ) end
191
NewPerformer()
The following code sample describes how to update task information if the task is reassigned in Livelink.
Function Assoc NewPerformer( \ Object prgCtx, \ Record taskRec, \ Integer newID ) //The name of this task is not dependent on the name of the //performer of the task, which means that there is no need to //call this script. Assoc retVal Boolean success = True taskRec.PERFORMERID = newID retVal.OK = success return( retVal ) end
ReassignStep()
The following code sample describes how to create a script that lets workflow participants reassign a task on the Step Detail page.
Function Boolean ReassignStep( \ Object prgCtx, \ Record taskRec, \ Record user, \ Record workData, \ WAPIWORK work ) Assoc userInfo Integer where List cbData Boolean ok = False Object uSession = prgCtx.USession() Object wSession = prgCtx.WSession() String cr = Str.EOL() //Verify that the performer of the task has permission to //reassign the task. if ( $WFMain.WAPIPkg.CheckWFPermissions( prgCtx, workData, \ $WFMain.WFConst.kWFChangeWork ) ) ok = wSession.StartTrans() //Reassign the task, and update the audit trail. if ( ok ) ok = UpdatePerformer( prgCtx, work, taskRec, user )
192
//If the task was reassigned to a group, remove the //group performer ID callback script. if ( ok ) if ( user.TYPE != UAPI.USER ) cbData = { { $WFMain.WFConst.kCBSetGrpStepPerformer, \ user.ID } } else cbData = Undefined end ok = UpdateMapTask( prgCtx, taskRec, cbData ) end if ( !wSession.EndTrans( ok ) ) ok = False end end end return( ok ) end Function Boolean UpdatePerformer( \ Object prgCtx, \ WAPIWORK work, \ Record old, \ Record new ) Boolean success List info //Make the WAPI call that reassigns the task. success = prgCtx.WSession().CheckRetVal( \ WAPI.ReassignTask( \ work, \ old.WORK.SUBWORKTASK_WORKID, \ old.WORK.SUBWORKTASK_SUBWORKID, \ old.WORK.SUBWORKTASK_TASKID, \ new.ID ) ) if ( success ) info = { 1, { Str.String( [WebWork_Message.StepReassignedTo] \ ), new.ID } } $WFMain.WAPIPkg.AddAuditData( prgCtx, work, info ) old.WORK.SUBWORKTASK_PERFORMERID = new.ID end return( success ) end Function Boolean UpdateMapTask( \ Object prgCtx, \ Record taskData, \ List cbData = Undefined ) Boolean success
193
task
success = session.CheckRetVal( WAPI.LoadMapByID( map, \ taskData.WORKINFO.SUBWORK_MAPID ) ) if ( success ) task = WAPI.AllocNthMapTask( map, \ taskData.WORK.SUBWORKTASK_TASKID ) success = session.CheckRetVal( task ) if ( success ) task.pPerformerCB = cbData //Free the memory held by the WAPIMAPTASK object. WAPI.FreeMapTask( task ) success = session.CheckRetVal( WAPI.ReplaceMap( map ) ) end end //Free the memory held by the WAPIMAP object. WAPI.FreeMap( map ) return( success ) end
194
Request Handlers
Request Handlers
Now that you have modified the scripts associated with the FormTaskWork object, you must create the custom request handlers that perform the operations requested when a workflow participant executes the new task type. The request handlers that you create are based on the LLRequestHandler object and are called FormEdit, FormEditStart, SaveForm, and SaveFormStart. To create the FormEdit and FormEditStart request handlers: 1. Orphan WebLL:LLRequestHandler in an OSpace in your custom module, and name it CustModuleLLRequestHandler. 2. Create a child object of the CustModuleLLRequestHandler object, and name it FormEdit. This custom request handler will be used to execute the form task operations. 3. In the FormEdit object, set the value of the fEnabled feature to TRUE. 4. Create a child object of the FormEdit object, and name it FormEditStart. 5. In the FormEdit object, edit the SetPrototype() script to define how to validate a request handler argument. For more information about the SetPrototype() script, see FormEdit SetPrototype(), page 197. 6. Run the SetPrototype() script. This script defines how to create a prototype that validates request handler arguments for the FormEdit request handler. This prototype is stored in the fPrototype feature. 7. Create a new Dynamic feature and name it fResponse. 8. Edit the Execute() script. For more information about the Execute() script for the FormEdit object, see FormEditExecute(), page 197. 9. In the FormEditStart object, edit the SetPrototype() script to define how to validate a request handler argument. For more information about the SetPrototype() script for the FormEditStart object, see FormEditStartSetPrototype(), page 201. 10. Run the SetPrototype() script. This script defines how to create a prototype that validates request handler arguments for the FormEditStart request handler. This prototype is stored in the fPrototype feature.
195
Request Handlers
11. Edit the Execute() script. For more information about the Execute() script for the FormEditStart object, see FormEditStartExecute(), page 201. 12. In the Globals object, run the BuildOSpace() script. To create the SaveForm and SaveFormEdit request handlers: 1. Create another child object of the CustModuleLLRequestHandler object, and name it SaveForm. This custom request handler will be used to save the form when a workflow participant clicks the Save button. 2. In the SaveForm object, set the value of the fEnabled feature to TRUE. 3. Edit the SetPrototype() script to define how to validate a request handler argument. For more information about the SetPrototype() script for the SaveForm object, see SaveFormSetPrototype(), page 204. 4. Run the SetPrototype() script. 5. Edit the Execute() script. 6. Create a child object of the SaveForm object, and name it SaveFormStart. 7. In the SaveFormStart object, edit the Execute() script. 8. In the Globals object, run the BuildOSpace() script. To create a request handler group: 1. Orphan WebDSP:WebDSPRoot:RequestHandlerGroup in an OSpace in your custom module, and name it CustModuleRequestHandlerGroup. 2. In the CustModuleRequestHandlerGroup object, set the value of the fEnabled feature to TRUE. 3. Run the SetRequestHandlers() script. This script creates a list of the request handlers in the OSpace, and stores this list in the fRequestHandlers feature. 4. In the Globals object, run the BuildOSpace() script.
196
Request Handlers
FormEditSetPrototype()
The following code sample describes how to validate request handler arguments for the FormEdit request handler. After you edit this script, you must run it to set the fPrototype feature for the request handler.
function void SetPrototype() .fPrototype =\ {\ { 'WorkID', IntegerType, [WebWork_RHParams.WorkID], FALSE \ },\ { 'SubWorkID', IntegerType, [WebWork_RHParams.SubWorkID], \ FALSE },\ { 'TaskID', IntegerType, [WebWork_RHParams.TaskID], TRUE, \ 0 },\ { 'FormName', StringType, [WebFormWF_RHParams.FormName], \ FALSE },\ { 'Editable', BooleanType, [WebFormWF_RHParams.Editable], \ FALSE },\ { 'NextURL', StringType, [WebWork_RHParams.NextURL], \ FALSE } \ } end
FormEditExecute()
The following code sample defines the operation of the FormEdit request handler for the form task type.
function Dynamic Execute( Dynamic ctxIn, Dynamic ctxOut, Record r ) Assoc extendedData Assoc form Assoc response Assoc saveData Assoc tmp Dynamic forms Dynamic status Dynamic taskInfo Object obj String mapName Object Object String prgCtx = .prgSession() subSystem = $WebForm.WebFormSubsystem pageType = 'WORK'
//Find the controller object for the forms task type (that is, the //form tasks API object). obj = $WFMain.WFPackageSubsystem.GetItemByName( 'Form' ) if ( IsDefined( obj ) ) taskInfo = prgCtx.WSession().LoadTaskStatus( \
197
Request Handlers
r.WorkID, \ r.SubWorkID, \ r.TaskID ) if ( .Check( taskInfo ) ) mapName = taskInfo[ 1 ].SUBWORK_TITLE //Retrieve the forms that are available to the workflow. //This value depends on whether the workflow is at the Start //task and if the data in the workflow is editable. The list //of forms gets loaded from a different place if the Start //task is active or if it is loaded from the Detailed Status //page. if ( r.TaskID > 0 ) if ( r.Editable ) forms = obj.LoadTaskWorkData( prgCtx, Undefined, \ taskInfo[ 1 ], r.SubWorkID ) else forms = obj.GetTaskStatusWork( prgCtx, taskInfo[ 1 ], \ r.SubWorkID ) end else forms = obj.LoadWorkData( prgCtx, Undefined, r.SubWorkID ) end .Check( forms ) end else .fError = 'Could not find Form controller object.' end if ( !IsDefined( .fError ) ) forms = forms.Forms //Locate the form that is being edited, in the list of //forms that are available to the workflow. form = GetForm( forms, r.FormName ) if ( !IsDefined( form ) ) .fError = \ Str.Format( [WebFormWF_ErrMsg.CouldNotFindForm1], \ r.FormName ) end end if ( !IsDefined( .fError ) ) //Set up the Assoc that will control the operation of the Save //button on the form. if ( r.Editable ) saveData.Func = 'formwf.saveform' saveData.LL_ID = r.SubworkID saveData.LL_FormName = r.FormName saveData.LL_NextURL = Web.Escape( r.NextURL ) end if ( Str.Locate( Str.Upper( r.NextURL ), 'WORK.STATUSTASKS' ) )
198
Request Handlers
pageType = 'STATUS' end //Retrieve any extra data that may be needed to display the //form. extendedData = subSystem.GetWorkflowExtendedData( \ prgCtx, \ r, \ mapName, \ pageType, \ form.TemplateID ) if ( .CheckError( extendedData ) ) //Add ctxOut to the request record. This is a gateway to the //Web server that serves up the HTML pages. tmp = Assoc.FromRecord( r ) tmp.CtxOut = ctxOut r = Assoc.ToRecord( tmp ) //Tell the forms subsystem to display the specified form. status = subSystem.DisplayForm( \ prgCtx, \ r, \ form.TemplateID, \ form.Data, \ saveData, \ extendedData ) if ( .CheckError( status ) ) response.Data = Assoc.CreateAssoc() response.Data.extendedData = extendedData //Store information about the form being displayed and //determine which HTML file to use to display the form. .fResponse = response if ( IsDefined( extendedData.HTMLFile ) ) .fHTMLFile = extendedData.HTMLFile else .fHTMLFile = Undefined end end end end return Undefined end function Assoc GetForm( \ List forms, \ String formName ) Assoc Assoc form formData
199
Request Handlers
Assoc status Integer templateID Integer where Assoc retVal = Undefined Boolean found = False Object prgCtx = .prgSession() //Look through all the forms and sub-forms that are //available to the workflow and see if the specified //form exists. for form in forms if ( form.Name == formName ) templateID = form.TemplateID formData = form.Data found = True else where = formName in form.SubForms //Find the ID of the template object for the form and find //all the data that has been entered into the form. if ( IsDefined( where ) && ( where > 0 ) ) templateID = form.SubFormTemplateIDs[ where ] formData = form.Data found = True end end if ( found ) retVal = Assoc.CreateAssoc() retVal.TemplateID = templateID retVal.Data = formData break end end return( retVal ) end
200
Request Handlers
FormEditStartSetPrototype()
The following code sample describes how to validate request handler arguments for the FormEditStart request handler. After you edit this script, you must run it to set the fPrototype feature for the request handler.
function void SetPrototype() .fPrototype =\ {\ { 'ID', IntegerType, [WebFormWF_RHParams.ID], FALSE },\ { 'FormName', StringType, [WebFormWF_RHParams.FormName], \ FALSE },\ { 'NextURL', StringType, [WebWork_RHParams.NextURL], \ FALSE } \ } end
FormEditStartExecute()
The following code sample defines the operation of the FormEditStart request handler for the form task type.
function Dynamic Execute( Dynamic ctxIn, Dynamic ctxOut, Record r ) Assoc extendedData Assoc form Assoc mapData Assoc response Assoc saveData Assoc tmp Dynamic status String mapName Object Object prgCtx = .prgSession() subSystem = $WebForm.WebFormSubsystem
//Load the workflow map. mapData = $WebWFP.WFPkg.LoadMap( prgCtx, r.ID ) if ( mapData.OK ) mapName = $WebWork.WFPkg.GetMapName( prgCtx, r.ID ) //Locate the specified form in the work package. form = GetForm( mapData.MapInfo.WORK_PACKAGES, r.FormName ) if ( !IsDefined( form ) ) .fError = Str.Format( [WebFormWF_ErrMsg.CouldNotFindForm1], r.FormName ) end else .fError = mapData.ErrMsg
201
Request Handlers
end if ( !IsDefined( .fError ) ) //Set up the Assoc that will control the operation of the Save //button on the form. saveData.Func = 'formwf.saveformstart' saveData.LL_ID = r.ID saveData.LL_FormName = r.FormName saveData.LL_NextURL = Web.Escape( r.NextURL ) //Get any extra data that will be needed to display the form //in a workflow (for example, the masthead information). extendedData = subSystem.GetWorkflowExtendedData( \ prgCtx, \ r, \ mapName, \ 'WORK', \ form.TemplateID ) if ( .CheckError( extendedData ) ) //Add ctxOut to the request record. This is a gateway to the //Web server that serves up the HTML pages. tmp = Assoc.FromRecord( r ) tmp.CtxOut = ctxOut r = Assoc.ToRecord( tmp ) //Call the Livelink Forms module and tell it to display the //specified form. status = subSystem.DisplayForm( \ prgCtx, \ r, \ form.TemplateID, \ form.Data, \ saveData, \ extendedData ) if ( .CheckError( status ) ) response.Data = Assoc.CreateAssoc() response.Data.extendedData = extendedData .fResponse = response if ( IsDefined( extendedData.HTMLFile ) ) .fHTMLFile = extendedData.HTMLFile else .fHTMLFile = Undefined end end end end return Undefined end
202
Request Handlers
function Assoc GetForm( \ RecArray work_packages, \ String formName ) Assoc form Assoc status Dynamic formData Dynamic retVal Integer templateID Integer where List forms List key Record r Boolean Object 'Form' ) Object found = False objType = $WFMain.WFPackageSubsystem.GetItemByName( \ prgCtx = .prgSession()
//Find the form data type in the workflows work package and //prepare it for use. if ( IsDefined( objType ) ) key = { objType.fType, objType.fSubType } for r in work_packages if ( key == { r.TYPE, r.SUBTYPE } ) forms = objType.ReadyForModification( prgCtx, r.USERDATA ) break end end end //Look through all the forms and sub-forms that are //available to the workflow and see if the specified //form exists. for form in forms if ( form.Name == formName ) templateID = form.TemplateID formData = form.Data found = True else where = formName in form.SubForms if ( IsDefined( where ) && ( where > 0 ) ) templateID = form.SubFormTemplateIDs[ where ] formData = form.Data found = True end end if ( found ) retVal = Assoc.CreateAssoc() retVal.TemplateID = templateID retVal.Data = formData
203
Request Handlers
SaveFormSetPrototype()
The following code sample describes how to validate request handler arguments for the SaveForm request handler. After you edit this script, you must run it to set the fPrototype feature for the request handler.
function void SetPrototype() .fPrototype =\ {\ { 'LL_ID', IntegerType, [WebFormWF_RHParams.ID], FALSE },\ { 'LL_FormName', StringType, \ [WebFormWF_RHParams.FormName], FALSE },\ { 'LL_NextURL', StringType, [WebWork_RHParams.NextURL], \ FALSE } \ } end
SaveFormExecute()
The following code sample defines the operation of the SaveForm request handler for the form task type.
function Dynamic Execute( Dynamic ctxIn, Dynamic ctxOut, Record r ) Assoc form Assoc formData Assoc tmp Dynamic forms Dynamic status DAPINODE template Integer index Object obj String name Object prgCtx = .prgSession()
//Find the controller object for the forms task type (that is, //the form tasks API object). obj = $WFMain.WFPackageSubsystem.GetItemByName( 'Form' ) if ( IsDefined( obj ) ) //Load the forms that are available to this workflow. forms = obj.LoadWorkData( prgCtx, Undefined, r.LL_ID )
204
Request Handlers
if ( .Check( forms ) ) forms = forms.Forms //Find the specific form that is being saved in the list of //forms that are available to the workflow. form = GetForm( forms, r.LL_FormName ) if ( IsDefined( form ) ) index = form.Index template = form.Template form = form.Form else .fError = Str.Format( [WebFormWF_ErrMsg.CouldNotFindForm1], r.LL_FormName ) end end else .fError = [WebFormWF_ErrMsg.CouldNotFindFormControllerObject] end if ( !IsDefined( .fError ) ) //Retrieve the data that was entered into the form. status = $WebForm.WebFormSubsystem.ValuesFromRequest( \ prgCtx, \ template, \ r, \ this ) if ( .CheckError( status ) ) //Store the data that was entered into the HTML page (form) //in the workflow. for name in Assoc.Keys( status.FormInfo ) form.Data.( name ) = status.FormInfo.( name ) end //Save the workflows form values back to the workflow. status = obj.UpdateForm( prgCtx, r.LL_ID, index, form ) end //Reset LL_NextURL to NextURL and set the NextURL to display //after the data is saved. tmp = Assoc.FromRecord( r ) tmp.NextURL = ( r.LL_NextURL[ 1 ] == '%' ) ? Web.UnEscape( tmp.LL_NextURL ) : tmp.LL_NextURL r = Assoc.ToRecord( tmp ) $WebForm.WebFormSubsystem.RedirectAfterSave( template, ctxOut, this, r, status ) end
205
Request Handlers
return Undefined end function Assoc GetForm( \ List forms, \ String formName ) Assoc form Assoc status Integer templateID Integer where Assoc retVal = Undefined Boolean found = False Integer index = 1 Object prgCtx = .prgSession() for form in forms if ( form.Name == formName ) templateID = form.TemplateID found = True else where = formName in form.SubForms if ( IsDefined( where ) && ( where > 0 ) ) templateID = form.SubFormTemplateIDs[ where ] found = True end end if ( found ) if ( !IsDefined( form.Data ) ) form.Data = Assoc.CreateAssoc() end status = $FormApi.TemplateSubsystem.TemplateNodeFromID( \ templateID, \ prgCtx.DSession() ) if ( status.OK ) retVal = Assoc.CreateAssoc() retVal.Form = form retVal.Index = index retVal.Template = status.Template else echo( 'Could not find template for ', formName ) end break end index += 1 end return( retVal ) end
206
Request Handlers
SaveFormStartExecute()
The following code sample describes how to validate request handler arguments for the SaveFormStart request handler. After you edit this script, you must run it to set the fPrototype feature for the request handler.
function Dynamic Execute( Dynamic ctxIn, Dynamic ctxOut, Record r ) Assoc form Assoc formData Assoc mapData Assoc status Assoc tmp DAPINODE template String name Object prgCtx = .prgSession()
//Load the workflow map. mapData = $WebWFP.WFPkg.LoadMap( prgCtx, r.LL_ID ) if ( mapData.OK ) //Locate the specified form in the work package. formData = GetForm( mapData.MapInfo.WORK_PACKAGES, r.LL_FormName ) if ( IsDefined( formData ) ) form = formData.Form template = form.Template //Retrieve the data that was entered into the form. status = $WebForm.WebFormSubsystem.ValuesFromRequest( \ prgCtx, \ template, \ r, \ this ) if ( .CheckError( status ) ) form.Template = Undefined //Store the values from the HTML page (form) into the //workflow. for name in Assoc.Keys( status.FormInfo ) form.Data.( name ) = status.FormInfo.( name ) end //Store the data in the workflow map definition.
207
Request Handlers
formData.Record.USERDATA[ formData.Index ] = form //Save the workflow map definition. if ( !$WebWFP.WFPkg.SaveMap( prgCtx, mapData.MapInfo, mapData.MapHolder, True ) ) status.OK = False status.ErrMsg = Str.Format( [WebWFP_ErrMsg.CouldNotSaveMap1], prgCtx.WSession().fErrorMsg ) end end //Reset LL_NextURL to NextURL and set the NextURL to //display. tmp = Assoc.FromRecord( r ) tmp.NextURL = ( r.LL_NextURL[ 1 ] == '%' ) ? \ Web.UnEscape( tmp.LL_NextURL ) : tmp.LL_NextURL r = Assoc.ToRecord( tmp ) $WebForm.WebFormSubsystem.RedirectAfterSave( template, ctxOut, this, r, status ) else .fError = Str.Format( 'Could not find form "%1" in map.', r.LL_FormName ) end else .fError = mapData.ErrMsg end return Undefined end function Assoc GetForm( \ RecArray work_packages, \ String formName ) Assoc form Assoc status Integer templateID Integer where List forms List key Record r Assoc retVal = Undefined Boolean found = False Integer index = 1 Object objType = $WFMain.WFPackageSubsystem.GetItemByName( 'Form' ) // No XLATE Object prgCtx = .prgSession() //Find the forms data type in the workflows work package and //prepare it for use. if ( IsDefined( objType ) ) key = { objType.fType, objType.fSubType } for r in work_packages
208
Request Handlers
if ( key == { r.TYPE, r.SUBTYPE } ) forms = r.USERDATA = objType.ReadyForModification( \ prgCtx, r.USERDATA ) break end end end for form in forms if ( form.Name == formName ) templateID = form.TemplateID found = True else where = formName in form.SubForms if ( IsDefined( where ) && ( where > 0 ) ) templateID = form.SubFormTemplateIDs[ where ] found = True end end //Look through all the forms and sub-forms //available to the workflow and see if the //form exists. If the form is found, store //about the form in an Assoc and return it //used to display the form. that are specified the information so that it can be
if ( found ) status = $FormApi.TemplateSubsystem.TemplateNodeFromID( \ templateID, \ prgCtx.DSession() ) if ( status.OK ) form.Template = status.Template if ( !IsDefined( form.Data ) ) form.Data = Assoc.CreateAssoc() end retVal = Assoc.CreateAssoc() retVal.Form = form retVal.Record = r retVal.Index = index else echo( 'Could not find template for ', formName ) end break end index += 1 end return( retVal ) end
209
Request Handlers
210
Index
A
access restricting access to event trigger scripts, 163 API object adding data types, 86, 99 adding task types, 32 architecture, 12 attachments, work packages, 2 attributes, work packages, 2 OSpace, 26 packaging, 30 query string, 28 request handler group, 28 Custom Display task example 1, 39 custom module adding data types, 85 adding event trigger scripts, 141 adding task types, 31 adding the form task type, 165 adding workflow types, 133 CustomDisplayAPI object, 39 CustomDisplayPaint object, 48 CustomDisplayWork object, 61 customerpane.html, 129
C
callback events, 21 callback scripts event trigger scripts, 141 CBExecute() script WFCustomScriptPkg object, 80, 136 ChangeAttribute() script, 146 CheckTaskDone() script, 87, 101 ChooseUser() script, 151 comments, work packages, 2 Configure request handler object, 28 CreateNewInstance() script, 87, 102 CreateReviewMapRec() script, 40 CreateWorkData() script, 87, 103 creator, workflow relationship, 3 cust_drop() script, 98 cust_sql() script, 97 custmod module, 26 Configure request handler, 28 configuring, 27 directory structure, 26
D
data types adding new, 85 adding schema, 95 adding the Table Values data type, 94 API object, 86, 99 creating database tables, 95 defining the Web object, 117 example 2, 94 Livelink Workflow architecture, 18 overview, 9 Web object, 91 Workflow Painter information, 89, 113 data types, routing legacy data, 18 database storage workflow management information, 14 DeleteWorkData() script, 87, 103
211
Request Handlers
getting started, 25 GetWFTypeName() script, 134, 137 GetWFTypes() script, 134, 138
E
Evaluate step, 5, 17 event trigger scripts adding new, 141 architecture, 23 choosing, 142 example 4, 146 example 5, 151 general, 142, 144 Livelink Workflow architecture, 21 overview, 10 performer, 142, 149 restricting access, 163 submap, 142, 154 subsystems, 142 ExecuteCustTaskScript() script, 80 ExecuteScript() script, 81
H
HTML files customerpane.html, 129 projectpane.html, 126 redirect.html, 78 submap_tablevalues.html, 123 t_tablevalues.html, 115 t_user.html, 56 tablevalues.html, 126
I
icons Step Icon Palette, 5 Initiator step, 5, 17 initiator, workflow relationship, 3 introduction, 1
F
FactoryName() script, 163 Figure 1-1, The Workflow Painter, 4 Figure 1-2, Function Overview window, 6 Figure 1-3, Map Overview Window, 6 Figure 2-1, Workflow Management Group Permissions, 14 Figure 2-2, The WWork Table, 14 Figure 2-3, The WSubWork Table, 15 Figure 2-4, The WMapTask Table, 16 Figure 2-5, Executing Callback Scripts, 22 fObjectFactory feature, 163 forms creating the form task type, 165 Function Window, 6
L
legacy data, routing, 18 ListScripts() script, 82 ListTemplates() script, 82 Livelink Workflow, 1 architecture, 11, 12 Livelink Workflow Map, 19 LoadStartTaskWorkData() script, 87, 104 LoadTableValues() script, 105 LoadTaskWorkData() script, 88, 106 LoadWorkData() script, 88, 107
M
manager permissions, 4, 13 workflow relationship, 3 Map Editor page. See Workflow Painter Map Overview Window, 6 Milestone step, 5, 17 module architecture, 25 module directory structure, 26 module.ini file, 28
G
general event trigger scripts, 144, 146 GeneralCallbackScripts object, 144 GetData() script, 92, 118 GetDisplayPerformerInfo() script, 37, 62 GetMapData() script, 34, 48, 89, 114 GetPainterInfo() script, 33, 37, 43, 63 GetPainterMenu() script, 37, 64 GetStatusDisplay() script, 37, 66 GetSubmapData() script, 92 GetSubMapData() script, 119 GetTabInfo() script, 92, 120 GetTaskEditData() script, 37, 69 GetTaskGif() script, 72
N
NewPerformer() script, 38, 73
212
Request Handlers
O
OScript Livelink Workflow architecture, 12 workflow status, 13 OSpace creating new, 26 overview, 1 extending Livelink Workflow, 9 Map Overview window, 6
P
package types, 85 packages, work, 2 Painter. See Workflow Painter Palette. See Step Icon Palette participant, workflow relationship, 3 paths, workflow, 2 performer event trigger scripts, 149 PerformerCallbackScripts object, 149 pFlags, 16 projectpane.html, 126 properties Function Overview window, 6 PutMapData() script, 35, 52, 90, 114 PutReviewData() script, 74 PutSubmapData() script, 92 PutSubMapData() script, 121
Q
query string setting up a custom module, 28
R
ReadyTaskForInitiation() script, 33, 43 ReassignStep() script, 75 redirect.html, 78 relationships, workflow, 3 RemoveWorkData() script, 88, 107 request handlers Configure, 28 RequestHandlerGroup object, 28 RequestHandlerGroup object, 28 roles. See workflow relationships routes, workflow, 2
S
SaveData() script, 92, 122 SaveTableValues() script, 108
SaveWorkData() script, 88, 109 schema, adding, 95 scripts callback, 21 CBExecute(), 80, 136 ChangeAttribute(), 146 CheckTaskDone(), 87, 101 ChooseUser(), 151 CreateNewInstance(), 87, 102 CreateReviewMapRec(), 40 CreateWorkData(), 87, 103 cust_drop(), 98 cust_sql(), 97 DeleteWorkData(), 87, 103 event trigger scripts, 141 ExecuteCustTaskScript(), 80 ExecuteScript(), 81 FactoryName(), 163 GetData(), 92, 118 GetDisplayPerformerInfo(), 37, 62 GetMapData(), 34, 48, 89, 114 GetPainterInfo(), 33, 37, 43, 63 GetPainterMenu(), 37, 64 GetStatusDisplay(), 37, 66 GetSubmapData(), 92 GetSubMapData(), 119 GetTabInfo(), 92, 120 GetTaskEditData(), 37, 69 GetTaskGif(), 72 GetWFTypeName(), 134, 137 GetWFTypes(), 134, 138 ListScripts(), 82 ListTemplates(), 82 LoadStartTaskWorkData(), 87, 104 LoadTableValues(), 105 LoadTaskWorkData(), 88, 106 LoadWorkData(), 88, 107 NewPerformer(), 38, 73 PutMapData(), 35, 52, 90, 114 PutReviewData(), 74 PutSubmapData(), 92 PutSubMapData(), 121 ReadyTaskForInitiation(), 33, 43 ReassignStep(), 75 RemoveWorkData(), 88, 107 SaveData(), 92, 122 SaveTableValues(), 108 SaveWorkData(), 88, 109 SetReviewData(), 88, 109 SetSubWorkData(), 88, 110 SetSubWorkReturnData(), 88, 110 SetTaskDefaults(), 33, 45 SetTaskRecFromMap(), 33 SetTaskRecFromMapTask(), 46 StartWF(), 134, 138 subworkflow(), 156
Index
213
Request Handlers
UpdateSubWorkData(), 111 UpdateTableValues(), 112 SetReviewData() script, 88, 109 SetSubWorkData() script, 88, 110 SetSubWorkReturnData() script, 88, 110 SetTaskDefaults() script, 33, 45 SetTaskRecFromMap() script, 33 SetTaskRecFromMapTask() script, 46 StandardTasks object adding task types, 32 Start step, 5, 16 StartWF() script, 134, 138 status Detailed Status page, 7 Workflow Status page, 7 status and display adding task types, 36 Step Icon Palette, 5 storage workflow management information, 14 submap event trigger scripts, 154, 156 Submap step, 5, 17 submap_tablevalues.html, 123 SubmapCallbackScripts object, 154, 156 subsystems event trigger scripts, 142 subworkflow() script, 156 sub-workflows on the fly example 6, 156
U
UpdateSubWorkData() script, 111 UpdateTableValues() script, 112 User step, 5, 17
W
WAPI Livelink Workflow architecture, 12 workflow status, 13 WAPI.MAPTASK_FLAG_AUTODONE, 16 WAPI.MAPTASK_FLAG_MILESTONE, 16 WAPIMAP, 20 WebModule object, 27 WFCustomScriptPkg object, 79, 134 WFDataTypes object adding data types, 86 WFPackage object defining the Web information, 91 defining Workflow Painter information, 89 WFPTableValues object defining the data types Workflow Painter information, 113 WFTask object defining status and display, 36 defining Workflow Painter information, 34 work package adding data types, 85 components, 6 definition, 2 workflow API, 12 definition, 1, 2 saving, 6 terminology, 2 workflow attributes example 4, 146 workflow events, 144 workflow maps definition, 2 saving, 6 storage, 19 Workflow Painter, 4 Function Window, 6 Map Overview Window, 6 Step Icon Palette, 5 workflow paths. See workflow routes workflow properties, 6 workflow relationships, 3 workflow roles. See workflow relationships workflow routes, 2 workflow status Detailed Status page, 7 WAPI and OScript, 13
T
t_tablevalues.html, 115 t_user.html, 56 Table Values data type example 2, 94 tablevalues.html, 126 task types adding custom scripts and templates, 83 adding the Custom Display task, 39 API object, 32 creating the form task type, 165 CustomDisplayPaint object, 48 CustomDisplayWork object, 61 example 1, 39 Livelink Workflow architecture, 16 overview, 9, 31 status and display, 36 Step Icon Palette, 5 WFCustomScriptPkg object, 79 Workflow Painter information, 34 terminology, 2
214
Request Handlers
Workflow Status page, 7 workflow types adding new, 133 CBExecute(), 136
example 3, 135 Livelink Workflow architecture, 19 overview, 9, 133 WorkTableValues object, 117
Index
215
Request Handlers
216