2014-02-05 12:33:36 -04:00
< ? php
namespace ProcessMaker\Project\Adapter ;
use ProcessMaker\Project ;
use ProcessMaker\Util\Hash ;
2014-02-06 11:00:15 -04:00
/**
* Class BpmnWorkflow
2014-02-10 13:09:50 -04:00
*
2014-02-06 11:00:15 -04:00
* @ package ProcessMaker\Project\Adapter
2014-02-10 13:09:50 -04:00
* @ author Erik Amaru Ortiz < aortiz . erik @ gmail . com , erik @ colosa . com >
2014-02-06 11:00:15 -04:00
*/
2014-02-05 12:33:36 -04:00
class BpmnWorkflow extends Project\Bpmn
{
2014-02-06 16:21:46 -04:00
/**
* @ var \ProcessMaker\Project\Workflow
*/
protected $wp ;
2014-02-21 19:26:55 -04:00
const BPMN_GATEWAY_COMPLEX = " COMPLEX " ;
const BPMN_GATEWAY_PARALLEL = " PARALLEL " ;
const BPMN_GATEWAY_INCLUSIVE = " INCLUSIVE " ;
const BPMN_GATEWAY_EXCLUSIVE = " EXCLUSIVE " ;
2014-02-05 14:09:48 -04:00
/**
* OVERRIDES
*/
2014-02-05 12:33:36 -04:00
2014-02-06 16:21:46 -04:00
public static function load ( $prjUid )
{
2014-02-13 16:40:00 -04:00
2014-02-06 16:21:46 -04:00
$parent = parent :: load ( $prjUid );
2014-02-13 16:40:00 -04:00
//return new BpmnWorkflow();
2014-02-06 16:21:46 -04:00
2014-02-13 16:40:00 -04:00
$me = new BpmnWorkflow ();
2014-02-06 16:21:46 -04:00
$me -> project = $parent -> project ;
$me -> prjUid = $parent -> project -> getPrjUid ();
$me -> wp = Project\Workflow :: load ( $me -> prjUid );
return $me ;
}
2014-02-05 14:09:48 -04:00
public function create ( $data )
{
try {
parent :: create ( $data );
} catch ( \Exception $e ) {
throw new \RuntimeException ( sprintf ( " Can't create Bpmn Project. " . PHP_EOL . $e -> getMessage ()));
}
try {
$wpData = array ();
$wpData [ " PRO_UID " ] = $this -> getUid ();
if ( array_key_exists ( " PRJ_NAME " , $data )) {
$wpData [ " PRO_TITLE " ] = $data [ " PRJ_NAME " ];
}
if ( array_key_exists ( " PRJ_DESCRIPTION " , $data )) {
$wpData [ " PRO_DESCRIPTION " ] = $data [ " PRJ_DESCRIPTION " ];
}
if ( array_key_exists ( " PRJ_AUTHOR " , $data )) {
$wpData [ " PRO_CREATE_USER " ] = $data [ " PRJ_AUTHOR " ];
}
2014-02-10 16:35:24 -04:00
$this -> wp = new Project\Workflow ();
$this -> wp -> create ( $wpData );
2014-02-05 14:09:48 -04:00
} catch ( \Exception $e ) {
$prjUid = $this -> getUid ();
$this -> remove ();
throw new \RuntimeException ( sprintf (
" Can't create Bpmn Project with prj_uid: %s, workflow creation fails. " . PHP_EOL . $e -> getMessage ()
, $prjUid
));
}
}
public static function getList ( $start = null , $limit = null , $filter = " " , $changeCaseTo = CASE_UPPER )
{
$bpmnProjects = parent :: getList ( $start , $limit , $filter , $changeCaseTo );
$workflowProjects = Project\Workflow :: getList ( $start , $limit , " " , " " );
$workflowProjectsUids = array ();
foreach ( $workflowProjects as $workflowProject ) {
$workflowProjectsUids [] = $workflowProject [ " PRO_UID " ];
}
$prjUidKey = $changeCaseTo == CASE_UPPER ? " PRJ_UID " : " prj_uid " ;
$list = array ();
foreach ( $bpmnProjects as $bpmnProject ) {
if ( in_array ( $bpmnProject [ $prjUidKey ], $workflowProjectsUids )) {
$list [] = $bpmnProject ;
}
}
return $list ;
}
2014-02-06 16:21:46 -04:00
public function addActivity ( $data )
{
$taskData = array ();
2014-02-10 16:35:24 -04:00
$taskData [ " TAS_UID " ] = parent :: addActivity ( $data );
2014-02-06 16:21:46 -04:00
if ( array_key_exists ( " ACT_NAME " , $data )) {
$taskData [ " TAS_TITLE " ] = $data [ " ACT_NAME " ];
}
if ( array_key_exists ( " ACT_NAME " , $data )) {
$taskData [ " TAS_POSX " ] = $data [ " BOU_X " ];
}
if ( array_key_exists ( " ACT_NAME " , $data )) {
$taskData [ " TAS_POSY " ] = $data [ " BOU_Y " ];
}
$this -> wp -> addTask ( $taskData );
}
2014-02-06 19:17:11 -04:00
public function updateActivity ( $actUid , $data )
{
parent :: updateActivity ( $actUid , $data );
$taskData = array ();
if ( array_key_exists ( " ACT_NAME " , $data )) {
$taskData [ " TAS_TITLE " ] = $data [ " ACT_NAME " ];
}
if ( array_key_exists ( " ACT_NAME " , $data )) {
$taskData [ " TAS_POSX " ] = $data [ " BOU_X " ];
}
if ( array_key_exists ( " ACT_NAME " , $data )) {
$taskData [ " TAS_POSY " ] = $data [ " BOU_Y " ];
}
$this -> wp -> updateTask ( $actUid , $taskData );
}
2014-02-06 16:21:46 -04:00
public function removeActivity ( $actUid )
{
parent :: removeActivity ( $actUid );
$this -> wp -> removeTask ( $actUid );
}
2014-02-06 21:26:06 -04:00
2014-02-21 19:26:55 -04:00
public function addFlow ( $data , $diagram )
2014-02-06 21:26:06 -04:00
{
2014-02-21 19:26:55 -04:00
$flows = $diagram [ " flows " ];
$gateways = $diagram [ " gateways " ];
$events = $diagram [ " events " ];
2014-02-06 21:26:06 -04:00
parent :: addFlow ( $data );
2014-02-10 19:43:54 -04:00
// to add a workflow route
// - activity -> activity ==> route
// - activity -> gateway -> activity ==> selection, evaluation, parallel or parallel by evaluation route
2014-02-11 20:32:20 -04:00
$routes = self :: mapBpmnFlowsToWorkflowRoute ( $data , $flows , $gateways , $events );
2014-02-07 15:42:24 -04:00
2014-02-11 20:32:20 -04:00
if ( $routes !== null ) {
foreach ( $routes as $routeData ) {
$this -> wp -> addRoute ( $routeData [ " from " ], $routeData [ " to " ], $routeData [ " type " ]);
}
2014-02-10 19:43:54 -04:00
return true ;
2014-02-07 15:42:24 -04:00
}
2014-02-07 08:41:11 -04:00
2014-02-10 19:43:54 -04:00
// to add start event->activity as initial or end task
switch ( $data [ " FLO_ELEMENT_ORIGIN_TYPE " ]) {
case " bpmnEvent " :
switch ( $data [ " FLO_ELEMENT_DEST_TYPE " ]) {
case " bpmnActivity " :
$event = \BpmnEventPeer :: retrieveByPK ( $data [ " FLO_ELEMENT_ORIGIN " ]);
2014-02-07 08:41:11 -04:00
2014-02-10 19:43:54 -04:00
switch ( $event && $event -> getEvnType ()) {
case " START " :
// then set that activity/task as "Start Task"
2014-02-12 17:01:19 -04:00
$this -> wp -> setStartTask ( $data [ " FLO_ELEMENT_DEST " ]);
2014-02-10 19:43:54 -04:00
break ;
}
break ;
}
2014-02-06 21:26:06 -04:00
break ;
2014-02-10 19:43:54 -04:00
}
}
2014-02-06 21:26:06 -04:00
2014-02-18 17:41:45 -04:00
public function updateFlow ( $floUid , $data )
{
if ( ! self :: isModified ( " flow " , $floUid , $data )) {
self :: log ( " Update Flow: $floUid (No Changes) " );
return false ;
}
parent :: updateFlow ( $floUid , $data );
}
2014-02-12 17:01:19 -04:00
public function removeFlow ( $floUid )
{
2014-02-13 16:40:00 -04:00
$flow = \BpmnFlowPeer :: retrieveByPK ( $floUid );
2014-02-12 17:01:19 -04:00
parent :: removeFlow ( $floUid );
2014-02-13 16:40:00 -04:00
// verify case: event(start) -> activity
// => find the corresponding task and unset it as start task
if ( $flow -> getFloElementOriginType () == " bpmnEvent " &&
$flow -> getFloElementDestType () == " bpmnActivity "
) {
$event = \BpmnEventPeer :: retrieveByPK ( $flow -> getFloElementOrigin ());
if ( ! is_null ( $event ) && $event -> getEvnType () == " START " ) {
$activity = \BpmnActivityPeer :: retrieveByPK ( $flow -> getFloElementDest ());
$this -> wp -> setStartTask ( $activity -> getActUid (), false );
}
} elseif ( $flow -> getFloElementOriginType () == " bpmnActivity " &&
$flow -> getFloElementDestType () == " bpmnEvent " ) {
// verify case: activity -> event(end)
// => find the corresponding task and unset it as start task
$event = \BpmnEventPeer :: retrieveByPK ( $flow -> getFloElementDest ());
if ( ! is_null ( $event ) && $event -> getEvnType () == " END " ) {
$activity = \BpmnActivityPeer :: retrieveByPK ( $flow -> getFloElementOrigin ());
if ( ! is_null ( $activity )) {
$this -> wp -> setEndTask ( $activity -> getActUid (), false );
}
}
}
2014-02-12 17:01:19 -04:00
}
2014-02-07 08:41:11 -04:00
2014-02-10 19:43:54 -04:00
public function addEvent ( $data )
{
if ( ! array_key_exists ( " EVN_TYPE " , $data )) {
throw new \RuntimeException ( " Required param \" EVN_TYPE \" is missing. " );
}
parent :: addEvent ( $data );
}
public function removeEvent ( $evnUid )
{
2014-02-13 16:40:00 -04:00
// $event = \BpmnEventPeer::retrieveByPK($evnUid);
//
// switch ($event->getEvnType()) {
// case "START":
// $flow = \BpmnFlow::findOneBy(\BpmnFlowPeer::FLO_ELEMENT_ORIGIN, $event->getEvnUid());
// if (! is_null($flow) && $flow->getFloElementDestType() == "bpmnActivity") {
// $activity = \BpmnActivityPeer::retrieveByPK($flow->getFloElementDest());
// if (! is_null($activity)) {
// $this->wp->setStartTask($activity->getActUid(), false);
// }
// }
// break;
// case "END":
// $flow = \BpmnFlow::findOneBy(\BpmnFlowPeer::FLO_ELEMENT_DEST, $event->getEvnUid());
// if (! is_null($flow) && $flow->getFloElementOriginType() == "bpmnActivity") {
// $activity = \BpmnActivityPeer::retrieveByPK($flow->getFloElementOrigin());
// if (! is_null($activity)) {
// $this->wp->setEndTask($activity->getActUid(), false);
// }
// }
// break;
// }
2014-02-10 19:43:54 -04:00
parent :: removeEvent ( $evnUid );
2014-02-06 21:26:06 -04:00
}
2014-02-07 08:41:11 -04:00
public static function mapBpmnFlowsToWorkflowRoute ( $flow , $flows , $gateways , $events )
{
$fromUid = $flow [ 'FLO_ELEMENT_ORIGIN' ];
2014-02-11 20:32:20 -04:00
$result = array ();
2014-02-07 08:41:11 -04:00
2014-02-07 15:42:24 -04:00
if ( $flow [ 'FLO_ELEMENT_ORIGIN_TYPE' ] != " bpmnActivity " ) {
// skip flows that comes from a element that is not an Activity
self :: log ( " Skip map FlowsToWorkflowRoute for -> flow with FLO_UID: { $flow [ 'FLO_UID' ] } , that have FLO_ELEMENT_ORIGIN: { $flow [ 'FLO_ELEMENT_ORIGIN_TYPE' ] } : $fromUid " );
return null ;
}
2014-02-07 08:41:11 -04:00
if ( $flow [ 'FLO_TYPE' ] != 'SEQUENCE' ) {
throw new \LogicException ( sprintf (
" Unsupported flow type: %s, ProcessMaker only support type '', Given: '%s' " ,
'SEQUENCE' , $flow [ 'FLO_TYPE' ]
));
}
switch ( $flow [ 'FLO_ELEMENT_DEST_TYPE' ]) {
case 'bpmnActivity' :
// the most easy case, when the flow is connecting a activity with another activity
2014-02-11 20:32:20 -04:00
$result [] = array ( " from " => $fromUid , " to " => $flow [ 'FLO_ELEMENT_DEST' ], " type " => 'SEQUENTIAL' );
2014-02-07 08:41:11 -04:00
break ;
case 'bpmnGateway' :
$gatUid = $flow [ 'FLO_ELEMENT_DEST' ];
2014-02-11 16:55:50 -04:00
2014-02-07 08:41:11 -04:00
// if it is a gateway it can fork one or more routes
2014-02-11 20:32:20 -04:00
$gatFlows = self :: findInArray ( $gatUid , " FLO_ELEMENT_ORIGIN " , $flows );
foreach ( $gatFlows as $gatFlow ) {
switch ( $gatFlow [ 'FLO_ELEMENT_DEST_TYPE' ]) {
case 'bpmnActivity' :
// getting gateway properties
$gateways = self :: findInArray ( $gatUid , " GAT_UID " , $gateways );
if ( ! empty ( $gateways )) {
$gateway = $gateways [ 0 ];
2014-02-21 19:26:55 -04:00
$routeType = " " ;
2014-02-11 20:32:20 -04:00
switch ( $gateway [ 'GAT_TYPE' ]) {
2014-02-21 19:26:55 -04:00
case self :: BPMN_GATEWAY_COMPLEX :
2014-02-11 20:32:20 -04:00
$routeType = 'SELECT' ;
break ;
2014-02-21 19:26:55 -04:00
case self :: BPMN_GATEWAY_EXCLUSIVE :
2014-02-11 20:32:20 -04:00
$routeType = 'EVALUATE' ;
break ;
2014-02-21 19:26:55 -04:00
case self :: BPMN_GATEWAY_INCLUSIVE :
switch ( $gateway [ 'GAT_DIRECTION' ]) {
case " DIVERGING " :
$routeType = 'PARALLEL-BY-EVALUATION' ;
break ;
case " CONVERGING " :
$routeType = 'SEC-JOIN' ;
break ;
default :
throw new \LogicException ( sprintf ( " Unsupported Gateway direction: %s " , $gateway [ 'GAT_DIRECTION' ]));
}
2014-02-11 20:32:20 -04:00
break ;
2014-02-21 19:26:55 -04:00
case self :: BPMN_GATEWAY_PARALLEL :
switch ( $gateway [ 'GAT_DIRECTION' ]) {
case " DIVERGING " :
$routeType = 'PARALLEL' ;
break ;
case " CONVERGING " :
$routeType = 'SEC-JOIN' ;
break ;
default :
throw new \LogicException ( sprintf ( " Unsupported Gateway direction: %s " , $gateway [ 'GAT_DIRECTION' ]));
}
2014-02-11 20:32:20 -04:00
break ;
default :
throw new \LogicException ( sprintf ( " Unsupported Gateway type: %s " , $gateway [ 'GAT_TYPE' ]));
}
$result [] = array ( " from " => $fromUid , " to " => $gatFlow [ 'FLO_ELEMENT_DEST' ], " type " => $routeType );
}
break ;
default :
// for processmaker is only allowed flows between "gateway -> activity"
// any another flow is considered invalid
throw new \LogicException ( sprintf (
" For ProcessMaker is only allowed flows between \" gateway -> activity \" " . PHP_EOL .
" Given: bpmnGateway -> " . $gatFlow [ 'FLO_ELEMENT_DEST_TYPE' ]
));
}
2014-02-07 08:41:11 -04:00
}
break ;
case 'bpmnEvent' :
$evnUid = $flow [ 'FLO_ELEMENT_DEST' ];
2014-02-11 20:32:20 -04:00
$events = self :: findInArray ( $evnUid , " EVN_UID " , $events );
2014-02-07 08:41:11 -04:00
2014-02-13 16:40:00 -04:00
2014-02-11 20:32:20 -04:00
if ( ! empty ( $events )) {
$event = $events [ 0 ];
2014-02-07 08:41:11 -04:00
2014-02-11 20:32:20 -04:00
switch ( $event [ 'EVN_TYPE' ]) {
case 'END' :
$routeType = 'SEQUENTIAL' ;
$result [] = array ( " from " => $fromUid , " to " => " -1 " , " type " => $routeType );
break ;
default :
throw new \LogicException ( " Invalid connection to Event object type " );
}
}
2014-02-07 08:41:11 -04:00
break ;
}
2014-02-11 20:32:20 -04:00
return empty ( $result ) ? null : $result ;
2014-02-07 08:41:11 -04:00
}
protected static function findInArray ( $value , $key , $list )
{
2014-02-11 20:32:20 -04:00
$result = array ();
2014-02-07 08:41:11 -04:00
foreach ( $list as $item ) {
2014-02-11 20:32:20 -04:00
if ( array_key_exists ( $key , $item ) && $item [ $key ] == $value ) {
$result [] = $item ;
2014-02-07 08:41:11 -04:00
}
}
2014-02-11 20:32:20 -04:00
return $result ;
2014-02-07 08:41:11 -04:00
}
2014-02-13 16:40:00 -04:00
// public function getActivities()
// {
// return parent::getActivities();
// }
2014-02-10 16:35:24 -04:00
public function remove ()
{
parent :: remove ();
$this -> wp -> remove ();
}
2014-02-13 16:40:00 -04:00
2014-02-05 12:33:36 -04:00
}