delete did not reorder the existing activities. This would allow duplicate sequenceNumbers in the db. The upgrade script will resequence and fix all activities in all workflows.
539 lines
13 KiB
Perl
539 lines
13 KiB
Perl
package WebGUI::Workflow;
|
|
|
|
|
|
=head1 LEGAL
|
|
|
|
-------------------------------------------------------------------
|
|
WebGUI is Copyright 2001-2006 Plain Black Corporation.
|
|
-------------------------------------------------------------------
|
|
Please read the legal notices (docs/legal.txt) and the license
|
|
(docs/license.txt) that came with this distribution before using
|
|
this software.
|
|
-------------------------------------------------------------------
|
|
http://www.plainblack.com info@plainblack.com
|
|
-------------------------------------------------------------------
|
|
|
|
=cut
|
|
|
|
use strict;
|
|
use WebGUI::Workflow::Activity;
|
|
|
|
=head1 NAME
|
|
|
|
Package WebGUI::Workflow
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This package provides the API for manipulating workflows.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use WebGUI::Workflow;
|
|
|
|
=head1 METHODS
|
|
|
|
These methods are available from this class:
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 addActivity ( class [, id ] )
|
|
|
|
Adds an activity to this workflow. Returns a reference to the new activity object.
|
|
|
|
=head3 class
|
|
|
|
The classname of the activity to add.
|
|
|
|
=head3 id
|
|
|
|
Normally an ID will be generated for you, but if you want to override this and provide your own 22 character id, then you can specify it here.
|
|
|
|
=cut
|
|
|
|
sub addActivity {
|
|
my $self = shift;
|
|
my $class = shift;
|
|
my $id = shift;
|
|
return WebGUI::Workflow::Activity->create($self->session, $self->getId, $id, $class);
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 create ( session, properties, [, id ] )
|
|
|
|
Creates a new instance of a workflow.
|
|
|
|
=head3 session
|
|
|
|
A reference to the current session.
|
|
|
|
=head3 properties
|
|
|
|
A hash reference of properties to set for this workflow. See set() for details.
|
|
|
|
=head3 id
|
|
|
|
Normally an ID will be generated for you, but if you want to override this and provide your own 22 character id, then you can specify it here.
|
|
|
|
=cut
|
|
|
|
sub create {
|
|
my $class = shift;
|
|
my $session = shift;
|
|
my $properties = shift;
|
|
my $id = shift;
|
|
my $workflowId = $session->db->setRow("Workflow","workflowId",{workflowId=>"new",enabled=>0},$id);
|
|
my $self = $class->new($session, $workflowId);
|
|
$self->set($properties);
|
|
return $self;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 delete ( )
|
|
|
|
Removes this workflow and everything associated with it..
|
|
|
|
=cut
|
|
|
|
sub delete {
|
|
my $self = shift;
|
|
|
|
foreach my $cron (@{$self->getCrons}) {
|
|
$cron->delete;
|
|
}
|
|
|
|
foreach my $instance (@{$self->getInstances}) {
|
|
$instance->delete;
|
|
}
|
|
|
|
foreach my $activity (@{$self->getActivities}) {
|
|
$activity->delete;
|
|
}
|
|
|
|
$self->session->db->deleteRow("Workflow","workflowId",$self->getId);
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 deleteActivity ( activityId )
|
|
|
|
Removes an activity from this workflow. This is just a convenience method, so you don't have to manually load and construct activity objects when you're already working with a workflow.
|
|
|
|
=head3 activityId
|
|
|
|
The unique id of the activity to remove.
|
|
|
|
=cut
|
|
|
|
sub deleteActivity {
|
|
my $self = shift;
|
|
my $activityId = shift;
|
|
my $activity = $self->getActivity($activityId);
|
|
if ($activity) {
|
|
$activity->delete;
|
|
$self->reorderActivities;
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 demoteActivity ( activityId )
|
|
|
|
Moves an activity down one position in the execution order.
|
|
|
|
=head3 activityId
|
|
|
|
The id of the activity to move.
|
|
|
|
=cut
|
|
|
|
sub demoteActivity {
|
|
my $self = shift;
|
|
my $thisId = shift;
|
|
my ($thisSeq) = $self->session->db->quickArray("select sequenceNumber from WorkflowActivity where activityId=?",[$thisId]);
|
|
my ($otherId) = $self->session->db->quickArray("select activityId from WorkflowActivity where workflowId=? and sequenceNumber=?",[$self->getId, $thisSeq+1]);
|
|
if ($otherId ne "") {
|
|
$self->session->db->write("update WorkflowActivity set sequenceNumber=sequenceNumber+1 where activityId=?", [$thisId]);
|
|
$self->session->db->write("update WorkflowActivity set sequenceNumber=sequenceNumber-1 where activityId=?", [$otherId]);
|
|
$self->reorderActivities;
|
|
}
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 DESTROY ( )
|
|
|
|
Deconstructor.
|
|
|
|
=cut
|
|
|
|
sub DESTROY {
|
|
my $self = shift;
|
|
undef $self;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 get ( name )
|
|
|
|
Returns the value for a given property.
|
|
|
|
=cut
|
|
|
|
sub get {
|
|
my $self = shift;
|
|
my $name = shift;
|
|
return $self->{_data}{$name};
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getActivity ( activityId )
|
|
|
|
Retrieves an activity object. This is just a convenience method, so you don't have to manually load and construct activity objects when you're already working with a workflow.
|
|
|
|
=head3 activityId
|
|
|
|
The unique id of the activity.
|
|
|
|
=cut
|
|
|
|
sub getActivity {
|
|
my $self = shift;
|
|
my $activityId = shift;
|
|
return WebGUI::Workflow::Activity->new($self->session, $activityId);
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getActivities ( )
|
|
|
|
Returns an array reference of the activity objects associated with this workflow.
|
|
|
|
=cut
|
|
|
|
sub getActivities {
|
|
my $self = shift;
|
|
my @activities = ();
|
|
my $rs = $self->session->db->prepare("select activityId from WorkflowActivity where workflowId=? order by sequenceNumber");
|
|
$rs->execute([$self->getId]);
|
|
while (my ($activityId) = $rs->array) {
|
|
push(@activities, $self->getActivity($activityId));
|
|
}
|
|
return \@activities;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getInstances ( )
|
|
|
|
Returns an array reference of the instance objects currently existing for this workflow.
|
|
|
|
=cut
|
|
|
|
sub getInstances {
|
|
my $self = shift;
|
|
my @instances = ();
|
|
my $rs = $self->session->db->read("SELECT instanceId FROM WorkflowInstance WHERE workflowId = ?", [$self->getId]);
|
|
while (my ($instanceId) = $rs->array) {
|
|
push(@instances, WebGUI::Workflow::Instance->new($self->session, $instanceId));
|
|
}
|
|
return \@instances;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getCrons ( )
|
|
|
|
Returns an array reference of the cron objects that trigger this workflow.
|
|
|
|
=cut
|
|
|
|
sub getCrons {
|
|
my $self = shift;
|
|
my @crons = ();
|
|
my $rs = $self->session->db->read("SELECT taskId FROM WorkflowSchedule WHERE workflowId = ?", [$self->getId]);
|
|
while (my ($taskId) = $rs->array) {
|
|
push(@crons, WebGUI::Workflow::Cron->new($self->session, $taskId));
|
|
}
|
|
return \@crons;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getId ( )
|
|
|
|
Returns the ID of this workflow.
|
|
|
|
=cut
|
|
|
|
sub getId {
|
|
my $self = shift;
|
|
return $self->{_id};
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getList ( session, [ type, includeRealtime ] )
|
|
|
|
Returns a hash reference of workflowId/title pairs of all enabled workflows. This is a class method.
|
|
|
|
=head3 session
|
|
|
|
A reference to the current session.
|
|
|
|
=head3 type
|
|
|
|
If specified this will limit the list to a certain type of workflow based upon the object type that the workflow is set up to handle.
|
|
|
|
=head3 includeRealtime
|
|
|
|
If set to 1 the list returned will include workflows with a mode of "realtime", otherwise it won't.
|
|
|
|
=cut
|
|
|
|
sub getList {
|
|
my $class = shift;
|
|
my $session = shift;
|
|
my $type = shift;
|
|
my $includeRealtime = shift;
|
|
my $sql = "select workflowId, title from Workflow where enabled=1";
|
|
my @params;
|
|
if ($type) {
|
|
$sql .= " and type=?";
|
|
push(@params, $type);
|
|
}
|
|
unless ($includeRealtime) {
|
|
$sql .= " and mode<>'realtime'";
|
|
}
|
|
return $session->db->buildHashRef($sql, \@params);
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getNextActivity ( [ activityId ] )
|
|
|
|
Returns the next activity in the workflow after the activity specified. If no activity id is specified, then the first workflow will be returned.
|
|
|
|
=head3 activityId
|
|
|
|
The unique id of an activity in this workflow.
|
|
|
|
=cut
|
|
|
|
sub getNextActivity {
|
|
my $self = shift;
|
|
my $activityId = shift;
|
|
my ($sequenceNumber) = $self->session->db->quickArray("select sequenceNumber from WorkflowActivity where activityId=?", [$activityId]);
|
|
$sequenceNumber++;
|
|
my ($id) = $self->session->db->quickArray("select activityId from WorkflowActivity where workflowId=?
|
|
and sequenceNumber>=? order by sequenceNumber", [$self->getId, $sequenceNumber]);
|
|
return $self->getActivity($id);
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 isParallel ( )
|
|
|
|
Returns 1 if the mode is set to "parallel".
|
|
|
|
=cut
|
|
|
|
sub isParallel {
|
|
my $self = shift;
|
|
return ($self->get("mode") eq "parallel") ? 1 : 0;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 isRealtime ( )
|
|
|
|
Returns 1 if the mode is set to "realtime".
|
|
|
|
=cut
|
|
|
|
sub isRealtime {
|
|
my $self = shift;
|
|
return ($self->get("mode") eq "realtime") ? 1 : 0;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 isSerial ( )
|
|
|
|
Returns 1 if the mode is set to "serial".
|
|
|
|
=cut
|
|
|
|
sub isSerial {
|
|
my $self = shift;
|
|
return ($self->get("mode") eq "serial") ? 1 : 0;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 isSingleton ( )
|
|
|
|
Returns 1 if the mode is set to "singleton".
|
|
|
|
=cut
|
|
|
|
sub isSingleton {
|
|
my $self = shift;
|
|
return ($self->get("mode") eq "singleton") ? 1 : 0;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 new ( session, workflowId )
|
|
|
|
Constructor.
|
|
|
|
=head3 session
|
|
|
|
A reference to the current session.
|
|
|
|
=head3 workflowId
|
|
|
|
The unique id of this workflow.
|
|
|
|
=cut
|
|
|
|
sub new {
|
|
my $class = shift;
|
|
my $session = shift;
|
|
my $workflowId = shift;
|
|
my $data = $session->db->getRow("Workflow","workflowId", $workflowId);
|
|
return undef unless $data->{workflowId};
|
|
bless {_session=>$session, _id=>$workflowId, _data=>$data}, $class;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 promoteActivity ( activityId )
|
|
|
|
Moves an activity up one position in the execution order.
|
|
|
|
=head3 activityId
|
|
|
|
The id of the activity to move.
|
|
|
|
=cut
|
|
|
|
sub promoteActivity {
|
|
my $self = shift;
|
|
my $thisId = shift;
|
|
my ($thisSeq) = $self->session->db->quickArray("select sequenceNumber from WorkflowActivity where activityId=?",[$thisId]);
|
|
my ($otherId) = $self->session->db->quickArray("select activityId from WorkflowActivity where workflowId=? and sequenceNumber=?",[$self->getId, $thisSeq-1]);
|
|
if ($otherId ne "") {
|
|
$self->session->db->write("update WorkflowActivity set sequenceNumber=sequenceNumber-1 where activityId=?", [$thisId]);
|
|
$self->session->db->write("update WorkflowActivity set sequenceNumber=sequenceNumber+1 where activityId=?", [$otherId]);
|
|
$self->reorderActivities;
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head3 reorderActivities ( )
|
|
|
|
Reorders the acitivities to make sure they're consecutive.
|
|
|
|
=cut
|
|
|
|
sub reorderActivities {
|
|
my $self = shift;
|
|
my $sth = $self->session->db->read("select activityId from WorkflowActivity where workflowId=? order by sequenceNumber",[$self->getId]);
|
|
my $i = 0;
|
|
while (my ($id) = $sth->array) {
|
|
$i++;
|
|
$self->session->db->write("update WorkflowActivity set sequenceNumber=? where activityId=?",[$i, $id]);
|
|
}
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 session ( )
|
|
|
|
Returns a reference to the current session.
|
|
|
|
=cut
|
|
|
|
sub session {
|
|
my $self = shift;
|
|
return $self->{_session};
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 set ( properties )
|
|
|
|
Sets a variable for this workflow.
|
|
|
|
=head3 properties
|
|
|
|
A hash reference containing the properties to set.
|
|
|
|
=head4 title
|
|
|
|
A title indicating what this workflow does. It should be short and descriptive as it will appear in dropdown forms.
|
|
|
|
=head4 description
|
|
|
|
A longer description of the workflow.
|
|
|
|
=head4 enabled
|
|
|
|
A boolean indicating whether this workflow may be executed right now.
|
|
|
|
=head4 type
|
|
|
|
A string indicating the type of object this workflow will be operating on. Valid values are "None", or any object type, like "WebGUI::VersionTag".
|
|
|
|
=head4 mode
|
|
|
|
A string containing one of "singleton", "parallel", "serial", or "realtime". Parallel is the default and should be
|
|
used in most situations. Singltons will allow only one instance of the workflow to be created at one time. New
|
|
instances will be destroyed immediately if a one instance of a singleton already exists. Serial workflows will run
|
|
instances sequentially in FIFO. Realtime workflows will run immediately without being handed off to Spectre for
|
|
governance.
|
|
|
|
=cut
|
|
|
|
sub set {
|
|
my $self = shift;
|
|
my $properties = shift;
|
|
|
|
# depricated. replaced by mode.
|
|
if ($properties->{isSerial} == 1) {
|
|
$properties->{mode} = "serial";
|
|
}
|
|
if ($properties->{isSingleton} == 1) {
|
|
$properties->{mode} = "singleton";
|
|
}
|
|
|
|
$self->{_data}{mode} = $properties->{mode} || "parallel";
|
|
$self->{_data}{enabled} = ($properties->{enabled} == 1) ? 1 : 0;
|
|
$self->{_data}{title} = $properties->{title} || $self->{_data}{title} || "Untitled";
|
|
$self->{_data}{description} = (exists $properties->{description}) ? $properties->{description} : $self->{_data}{description};
|
|
$self->{_data}{type} = $properties->{type} || $self->{_data}{type} || "None";
|
|
$self->session->db->setRow("Workflow","workflowId",$self->{_data});
|
|
}
|
|
|
|
|
|
1;
|
|
|
|
|