Zikula_Core
[ class tree: Zikula_Core ] [ index: Zikula_Core ] [ all elements ]

Source for file WorkflowUtil.class.php

Documentation is available at WorkflowUtil.class.php

  1. <?php
  2. /**
  3.  * @copyright (c) 2002, Zikula Development Team
  4.  * @link http://www.zikula.org
  5.  * @version $Id: WorkflowUtil.class.php 24342 2008-06-06 12:03:14Z markwest $
  6.  * @license GNU/LGPL - http://www.gnu.org/copyleft/lgpl.html
  7.  * @author Drak, drak@hostnuke.com Feb 2006
  8.  *  Inspired by the Pagesetter workflow system by Jørn Wildt
  9.  * @package Zikula_Core
  10.  */
  11.  
  12. /**
  13.  * WorkflowUtil Class
  14.  * From a developers standpoint, we only use this class to address workflows
  15.  * as the rest is for internal use by the workflow engine.
  16.  *
  17.  * @package Zikula_Core
  18.  * @subpackage WorkflowUtil
  19.  */
  20. {
  21.     /**
  22.      * load xml workflow
  23.      *
  24.      * @param string $schema name of workflow scheme
  25.      * @param string $module name of module
  26.      *
  27.      * @return mixed string of XML, or false
  28.      */
  29.     function loadSchema($schema 'standard'$module null)
  30.     {
  31.         static $workflows;
  32.  
  33.         if (!isset($workflows)) {
  34.             $workflows array();
  35.         }
  36.  
  37.         // if no module specified, default to calling module
  38.         if (empty($module)) {
  39.             $module pnModGetName();
  40.         }
  41.  
  42.         // workflow caching
  43.         if (isset($workflows[$module][$schema])) {
  44.             return $workflows[$module][$schema];
  45.         }
  46.  
  47.         // Get module info
  48.         $modinfo pnModGetInfo(pnModGetIDFromName($module));
  49.         if (!$modinfo){
  50.             return pn_exit("$module module specified doesnt exist");
  51.         }
  52.  
  53.         $path WorkflowUtil::_findpath("$schema.xml"$module);
  54.         if ($path{
  55.             $workflowXML file_get_contents($path);
  56.         else {
  57.             return pn_exit("couldnt find workflow file: $path");
  58.         }
  59.  
  60.         // instanciate Workflow Parser
  61.         $parser new pnWorkflowParser();
  62.  
  63.         // parse workflow and return workflow object
  64.         $workflowSchema $parser->parse($workflowXML$schema$module);
  65.  
  66.         // destroy parser
  67.         unset($parser);
  68.  
  69.         // cache workflow
  70.         $workflows[$module][$schema$workflowSchema;
  71.  
  72.         // return workflow object
  73.         return $workflows[$module][$schema];
  74.     }
  75.  
  76.  
  77.     /**
  78.      * Find the path of the file by searching overrides and the module location
  79.      *
  80.      * @access private
  81.      * @param string $file name of file to find (can include relative path)
  82.      * @param string $module 
  83.      * @return mixed string of path or bool false
  84.      */
  85.     function _findpath($file$module=null)
  86.     {
  87.         // if no module specified, default to calling module
  88.         if (empty($module)) {
  89.             $module pnModGetName();
  90.         }
  91.  
  92.         // Get module info
  93.         $modinfo pnModGetInfo(pnModGetIDFromName($module));
  94.         if (!$modinfo){
  95.             return pn_exit("$module module specified doesnt exist");
  96.         }
  97.  
  98.         $moduledir $modinfo['directory'];
  99.  
  100.         // determine which folder to look in (system or modules)
  101.         if ($modinfo['type'== 3// system module
  102.              $modulepath "system/$moduledir";
  103.         }
  104.         else
  105.         if ($modinfo['type'== 2// non system module
  106.             $modulepath "modules/$moduledir";
  107.         else {
  108.             return pn_exit("unsupported module type");
  109.         }
  110.  
  111.         // ensure module is active
  112.         if (!$modinfo['state'== 3{
  113.             return pn_exit("module is not active");
  114.         }
  115.  
  116.         $themedir ThemeUtil::getInfo(ThemeUtil::getIDFromName(pnUserGetTheme()));
  117.         $themepath DataUtil::formatForOS("themes/$themedir/workflows/$moduledir/$file");
  118.         $configpath DataUtil::formatForOS("config/workflows/$moduledir/$file");
  119.         $modulepath DataUtil::formatForOS("$modulepath/workflows/$file");
  120.  
  121.         // find the file in themes or config (for overrides), else module dir
  122.         if (is_readable($themepath)) {
  123.             return $themepath;
  124.         }
  125.         else
  126.         if (is_readable($configpath)) {
  127.             return $configpath;
  128.         }
  129.         else
  130.         if (is_readable($modulepath)) {
  131.             return $modulepath;
  132.         else {
  133.             return false;
  134.         }
  135.     }
  136.  
  137.  
  138.     /**
  139.      * Execute action
  140.      *
  141.      * @param string $schema name of workflow schema
  142.      * @param array $obj data object
  143.      * @param string $actionID action to perform
  144.      * @param string $table table where data will be stored (default = null)
  145.      * @param string $module name of module (defaults calling module)
  146.      * @param int $id ID column of table
  147.      * @return mixed 
  148.      */
  149.     function executeAction($schema&$obj$actionID$table=null$module=null$idcolumn='id')
  150.     {
  151.         if (!isset($obj)) {
  152.             return pn_exit('$obj not set');
  153.         }
  154.  
  155.         if (!is_array($obj))
  156.         {
  157.             return pn_exit('$obj needs to be an array');
  158.         }
  159.  
  160.         if (empty($schema)) {
  161.             return pn_exit('$schema needs to be named');
  162.         }
  163.  
  164.         if (empty($module)) {
  165.             // default to calling module
  166.             $module pnModGetName();
  167.         }
  168.  
  169.         $stateID WorkflowUtil::getWorkflowState($obj$table$idcolumn);
  170.         if (!$stateID{
  171.             $stateID 'initial';
  172.         }
  173.  
  174.         // instanciate workflow
  175.         $workflow new pnWorkflow($schema$module);
  176.  
  177.         return $workflow->executeAction($actionID$obj$stateID);
  178.     }
  179.  
  180.  
  181.     /**
  182.      * delete workflows for module (used module uninstall time)
  183.      *
  184.      * @param string $module 
  185.      * @return bool 
  186.      */
  187.     function deleteWorkflowsForModule($module)
  188.     {
  189.         if (!isset($module)) {
  190.             $module pnModGetName();
  191.         }
  192.  
  193.         if (!pnModDBInfoLoad('Workflow')) {
  194.             return false;
  195.         }
  196.  
  197.         // this is a cheat to delete all items in table with value $module
  198.         return (bool)DBUtil::deleteObjectByID('workflows'$module'module');
  199.     }
  200.  
  201.  
  202.     /**
  203.      * delete a workflow and associated data (by magic)
  204.      *
  205.      * @param array $obj 
  206.      * @return bool 
  207.      */
  208.     function deleteWorkflow($obj)
  209.     {
  210.         $table $obj['__WORKFLOW__']['obj_table'];
  211.         $wfid $obj['__WORKFLOW__']['id'];
  212.         if (!DBUtil::deleteObjectByID($table$obj['id'])) {
  213.             return false;
  214.         }
  215.  
  216.         return (bool)DBUtil::deleteObjectByID('workflows'$wfid);
  217.     }
  218.  
  219.  
  220.     /**
  221.      * get Actions by State
  222.      *
  223.      * @param string $schemaName 
  224.      * @param string $module 
  225.      * @param string $state default = 'initial'
  226.      * @param array $obj 
  227.      * @return mixed array or bool false
  228.      */
  229.     function getActionsByState($schemaName$module=null$state='initial'$obj=array())
  230.     {
  231.         if (!isset($module)) {
  232.             $module pnModGetName();
  233.         }
  234.  
  235.         // load up schema
  236.         $schema WorkflowUtil::loadSchema($schemaName$module);
  237.         if (!$schema{
  238.             return false;
  239.         }
  240.  
  241.         $actions $schema['actions'][$state];
  242.         $allowedActions array();
  243.         foreach($actions as $action{
  244.             if (WorkflowUtil::permissionCheck($module$schemaName$obj$action['permission']$action['id'])) {
  245.                 $allowedActions[$action['id']] $action['id'];
  246.             }
  247.         }
  248.  
  249.         return $allowedActions;
  250.     }
  251.  
  252.  
  253.     /**
  254.      * get possible actions for a given item of data in it's current workflow state
  255.      *
  256.      * @param array $obj 
  257.      * @param string $dbTable 
  258.      * @param mixed $idcolumn id field default = 'id'
  259.      * @param string $module module name (defaults to current module)
  260.      *
  261.      * @return mixed array of actions or bool false
  262.      */
  263.     function getActionsForObject(&$obj$dbTable$idcolumn='id'$module=null)
  264.     {
  265.         if (!is_array($obj)) {
  266.             return pn_exit('object is not an array');
  267.         }
  268.  
  269.         if (!isset($dbTable)) {
  270.             return pn_exit('$dbTable not specified');
  271.         }
  272.  
  273.         if (empty($module)) {
  274.             $module pnModGetName();
  275.         }
  276.  
  277.         if (!WorkflowUtil::getWorkflowForObject($obj$dbTable$idcolumn$module)) {
  278.             return false;
  279.         }
  280.  
  281.         $workflow $obj['__WORKFLOW__'];
  282.         return WorkflowUtil::getActionsByState($workflow['schemaname']$workflow['module']$workflow['state']$obj);
  283.     }
  284.  
  285.  
  286.     /**
  287.      * Load workflow for object
  288.      * will attach array '__PNWORKFLOW__' to the object
  289.      *
  290.      * @param array $obj 
  291.      * @param string $dbTable name of table where object is or will be stored
  292.      * @param string $id name of ID column of object
  293.      * @param string $module module name (defaults to current module)
  294.      * @return bool 
  295.      */
  296.     function getWorkflowForObject(&$obj$dbTable$idcolumn='id'$module=null)
  297.     {
  298.         if (empty($module)) {
  299.             $module pnModGetName();
  300.         }
  301.  
  302.         if (!isset($dbTable)) {
  303.             return pn_exit('$dbTable must be specified');
  304.         }
  305.  
  306.         if (!isset($obj|| !is_array($obj)) {
  307.             return pn_exit('$obj must be an array.');
  308.         }
  309.  
  310.         // get workflow data from DB
  311.         if (!pnModDBInfoLoad('Workflow')) {
  312.             return false;
  313.         }
  314.  
  315.         $pntables pnDBGetTables();
  316.         $workflows_column $pntables['workflows_column'];
  317.         $where "WHERE $workflows_column[module]='".DataUtil::formatForStore($module)."'
  318.                     AND $workflows_column[obj_table]='".DataUtil::formatForStore($dbTable)."'
  319.                     AND $workflows_column[obj_idcolumn]='".DataUtil::formatForStore($idcolumn)."'
  320.                     AND $workflows_column[obj_id]='".DataUtil::formatForStore($obj[$idcolumn])."'";
  321.         $workflow DBUtil::selectObject('workflows'$where);
  322.  
  323.         if (!$workflow{
  324.             $workflow array('state'        => 'initial',
  325.                               'obj_table'    => $dbTable,
  326.                               'obj_idcolumn' => $idcolumn,
  327.                               'obj_id'       => $obj[$idcolumn]);
  328.         }
  329.  
  330.         // attach workflow to object
  331.         $obj['__WORKFLOW__'$workflow;
  332.         return true;
  333.     }
  334.  
  335.  
  336.     /**
  337.      * get workflow state of object
  338.      *
  339.      * @param array $obj 
  340.      * @param string $table 
  341.      * @param string $idcolumn name of ID column
  342.      * @param string $module module name (defaults to current module)
  343.      *
  344.      * @return mixed string workflow state name or false
  345.      */
  346.     function getWorkflowState(&$obj$table$idcolumn='id'$module=null)
  347.     {
  348.         if (empty($module)) {
  349.             $module pnModGetName();
  350.         }
  351.  
  352.         if (!isset($obj['__WORKFLOW__'])) {
  353.             if (!WorkflowUtil::getWorkflowForObject($obj$table$idcolumn$module)) {
  354.                 return false;
  355.             }
  356.         }
  357.  
  358.         $workflow $obj['__WORKFLOW__'];
  359.         return $workflow['state'];
  360.     }
  361.  
  362.  
  363.     /**
  364.      * Check permission of action
  365.      *
  366.      * @param string $module 
  367.      * @param string $schema 
  368.      * @param array $obj 
  369.      * @param int $permLevel 
  370.      * @param int $actionId 
  371.      * @return bool 
  372.      */
  373.     function permissionCheck($module$schema$obj=array()$permLevel$actionId=null)
  374.     {
  375.         // translate permission to something meaningful
  376.         $permLevel WorkflowUtil::translatePermission($permLevel);
  377.  
  378.         // test conversion worked
  379.         if (!$permLevel{
  380.             return false;
  381.         }
  382.  
  383.         // get current user
  384.         $currentUser pnUserGetVar('uid');
  385.         // no user then assume anon
  386.         if (empty($currentUser)) {
  387.             $currentUser = -1;
  388.         }
  389.  
  390.         $function "{$module}_workflow_{$schema}_permissioncheck";
  391.         if (function_exists($function)) {
  392.            // function already exists
  393.            return $function($obj$permLevel$currentUser$actionId);
  394.         }
  395.  
  396.         // test operation file exists
  397.         $path WorkflowUtil::_findpath("function.{$schema}_permissioncheck.php"$module);
  398.         if (!$path{
  399.             return pn_exit("permission check file: function.{$schema}_permissioncheck.php : does not exist");
  400.         }
  401.  
  402.         // load file and test if function exists
  403.         Loader::includeOnce($path);
  404.         if (!function_exists($function)) {
  405.             return pn_exit("permission check function: $function: not defined");
  406.         }
  407.  
  408.         // function must be loaded so now we can execute the function
  409.         return $function($obj$permLevel$currentUser);
  410.     }
  411.  
  412.  
  413.     /**
  414.      * translates workflow permission to pn permission define
  415.      *
  416.      * @param string $permission 
  417.      * @return mixed int or false
  418.      */
  419.     function translatePermission($permission)
  420.     {
  421.         $permission strtolower($permission);
  422.         switch($permission{
  423.             case 'invalid' :
  424.                 return ACCESS_INVALID;
  425.             case 'overview' :
  426.                 return ACCESS_OVERVIEW;
  427.             case 'read' :
  428.                 return ACCESS_READ;
  429.             case 'comment' :
  430.                 return ACCESS_COMMENT;
  431.             case 'moderate' :
  432.                 return ACCESS_MODERATE;
  433.             case 'moderator' :
  434.                 return ACCESS_MODERATE;
  435.             case 'edit' :
  436.                 return ACCESS_EDIT;
  437.             case 'editor' :
  438.                 return ACCESS_EDIT;
  439.             case 'add' :
  440.                 return ACCESS_ADD;
  441.             case 'author' :
  442.                 return ACCESS_ADD;
  443.             case 'delete' :
  444.                 return ACCESS_DELETE;
  445.             case 'admin' :
  446.                 return ACCESS_ADMIN;
  447.             default :
  448.                 return false;
  449.         }
  450.     }
  451. }
  452.  
  453.  
  454. /**
  455.  * pnWorkflow class
  456.  *
  457.  * @package Zikula_Core
  458.  * @subpackage WorkflowUtil
  459.  */
  460. class pnWorkflow
  461. {
  462.     /**
  463.      * Enter description here...
  464.      *
  465.      * @param $id 
  466.      * @param $title 
  467.      * @param $description 
  468.      * @param $states 
  469.      * @param $actions 
  470.      * @param $configurations 
  471.      * @return object pnWorkflow 
  472.      */
  473.     function pnWorkflow($schema$module)
  474.     {
  475.         // load workflow schema
  476.         $schema WorkflowUtil::loadSchema($schema$module);
  477.  
  478.         $this->id             = $schema['workflow']['id'];
  479.         $this->title          = $schema['workflow']['title'];
  480.         $this->description    = $schema['workflow']['description'];
  481.         $this->module         = $module;
  482.         $this->actionMap      = $schema['actions'];
  483.         $this->stateMap       = $schema['states'];
  484.         $this->workflowData   = null;
  485.     }
  486.  
  487.  
  488.     /**
  489.      * register workflow by $metaId
  490.      *
  491.      * @param object $workflow 
  492.      * @param array $data 
  493.      * @param string $state default=null;
  494.      * @return bool 
  495.      */
  496.     function registerWorkflow(&$obj$stateID=null)
  497.     {
  498.         $workflowData $obj['__WORKFLOW__'];
  499.         $idcolumn $workflowData['obj_idcolumn'];
  500.         $insertObj array('obj_table'    => $workflowData['obj_table'],
  501.                            'obj_idcolumn' => $workflowData['obj_idcolumn'],
  502.                            'obj_id'       => $obj[$idcolumn],
  503.                            'module'       => $this->getModule(),
  504.                            'schemaname'   => $this->id,
  505.                            'state'        => $stateID);
  506.  
  507.         if (!pnModDBInfoLoad('Workflow')) {
  508.             return false;
  509.         }
  510.  
  511.         if (!DBUtil::insertObject($insertObj'workflows')) {
  512.             return false;
  513.         }
  514.  
  515.         $this->workflowData = $insertObj;
  516.         $obj['__WORKFLOW__'$insertObj;
  517.         return true;
  518.     }
  519.  
  520.  
  521.     /**
  522.      * update workflow state
  523.      *
  524.      * @param string $stateID 
  525.      * @param string $debug 
  526.      * @return bool 
  527.      */
  528.     function updateWorkflowState($stateID$debug=null)
  529.     {
  530.         $obj array('id'    => $this->workflowData['id'],
  531.                      'state' => $stateID);
  532.  
  533.         if (isset($debug)) {
  534.             $obj['debug'$debug;
  535.         }
  536.  
  537.         if (!pnModDBInfoLoad('Workflow')) {
  538.             return false;
  539.         }
  540.  
  541.         return (bool)DBUtil::updateObject($obj'workflows');
  542.     }
  543.  
  544.     /**
  545.      * execute workflow action
  546.      *
  547.      * @param string $actionID 
  548.      * @param array $obj 
  549.      * @param string $stateID 
  550.      * @return mixed array or false
  551.      */
  552.     function executeAction($actionID&$obj$stateID 'initial')
  553.     {
  554.         // check if state exists
  555.         if (!isset($this->actionMap[$stateID])) {
  556.             return pn_exit("STATE: $stateID not found");
  557.         }
  558.  
  559.         // check the action exists for given state
  560.         if (!isset($this->actionMap[$stateID][$actionID])) {
  561.             return pn_exit("Action: $actionID not available in this State: $stateID");
  562.         }
  563.  
  564.         $action $this->actionMap[$stateID][$actionID];
  565.  
  566.         // permission check
  567.         if (!WorkflowUtil::permissionCheck($this->module$this->id$obj$action['permission'])) {
  568.             return pn_exit("no permission to execute action: $action[permission]");
  569.         }
  570.  
  571.         // commit workflow to object
  572.         $this->workflowData = $obj['__WORKFLOW__'];
  573.  
  574.         // get operations
  575.         $operations $action['operations'];
  576.         $nextState (isset($action['nextState']$action['nextState'$stateID);
  577.  
  578.         foreach($operations as $operation{
  579.             $result[$operation['name']] $this->executeOperation($operation$obj$nextState);
  580.             if (!$result[$operation['name']]{
  581.                 // if an operation fails here, do not process further and return false
  582.                 return false;
  583.             }
  584.         }
  585.  
  586.         // test if state needs to be updated
  587.         if ($nextState == $stateID{
  588.             return $result;
  589.         }
  590.  
  591.         // if this is an initial object then we need to register with the DB
  592.         if ($stateID == 'initial'{
  593.             $this->registerWorkflow($obj$stateID);
  594.         }
  595.  
  596.         // change the workflow state
  597.         if (!$this->updateWorkflowState($nextState)) {
  598.             return false;
  599.         }
  600.  
  601.         // return result of all operations (possibly okay to just return true here)
  602.         return $result;
  603.     }
  604.  
  605.  
  606.     /**
  607.      * execute workflow operation within action
  608.      *
  609.      * @param  string $operation 
  610.      * @param  array $data 
  611.      * @return mixed or false
  612.      */
  613.     function executeOperation($operation&$obj$nextState)
  614.     {
  615.         $operationName $operation['name'];
  616.         $operationParams $operation['parameters'];
  617.  
  618.         // test operation file exists
  619.         $path WorkflowUtil::_findpath("operations/function.{$operationName}.php"$this->module);
  620.         if (!$path{
  621.             return pn_exit("operation file: $operationName: does not exist");
  622.         }
  623.  
  624.         // load file and test if function exists
  625.         Loader::includeOnce($path);
  626.         $function "{$this->module}_operation_{$operationName}";
  627.         if (!function_exists($function)) {
  628.             return pn_exit("operation function: $function: not defined");
  629.         }
  630.  
  631.         // execute operation and return result
  632.         return $function($obj, $operationParams);
  633.     }
  634.  
  635.  
  636.    /**
  637.      * get workflow ID
  638.      *
  639.      * @return string workflow schema name
  640.      */
  641.     function getID()
  642.     {
  643.         return $this->id;
  644.     }
  645.  
  646.  
  647.     /**
  648.      * get workflow title
  649.      *
  650.      * @return string title
  651.      */
  652.     function getTitle()
  653.     {
  654.         return $this->title;
  655.     }
  656.  
  657.  
  658.     /**
  659.      * get workflow description
  660.      *
  661.      * @return string description
  662.      */
  663.     function getDescription()
  664.     {
  665.         return $this->description;
  666.     }
  667.  
  668.  
  669.     /**
  670.      * get workflow Module
  671.      *
  672.      * @return string module name
  673.      */
  674.     function getModule()
  675.     {
  676.         return $this->module;
  677.     }
  678.     var $module;
  679.     var $id;
  680.     var $title;
  681.     var $description;
  682.     var $stateMap;
  683.     var $actionMap;
  684.     var $workflowData;
  685. }
  686.  
  687.  
  688. /**
  689.  * Workflow Parser
  690.  * Parse workflow schema into associative arrays
  691.  *
  692.  * @author Jørn Wildt
  693.  * @author Drak
  694.  * @package Zikula_Core
  695.  * @subpackage WorkflowUtil
  696.  */
  697. class pnWorkflowParser
  698. {
  699.     /**
  700.      * parse workflow into array format
  701.      *
  702.      */
  703.     function pnWorkflowParser()
  704.     {
  705.         $this->workflow array('state' => 'initial');
  706.  
  707.         // create xml parser
  708.         $this->parser xml_parser_create();
  709.         xml_set_object($this->parser$this);
  710.         xml_set_element_handler($this->parser"startElement""endElement");
  711.         xml_set_character_data_handler($this->parser"characterData");
  712.     }
  713.  
  714.  
  715.     /**
  716.      * parse xml
  717.      *
  718.      * @param string $xmldata
  719.      * @param string $schemaName
  720.      * @param string $module
  721.      * @param string $schemaPath
  722.      * @return mixed associative array of workflow or false
  723.      */
  724.     function parse($xmldata, $schemaName, $module)
  725.     {
  726.         // parse XML
  727.         if (!xml_parse($this->parser$xmldatatrue))
  728.         {
  729.             return pn_exit(
  730.             "Unable to parse XML workflow (line "
  731.             . xml_get_current_line_number($this->parser","
  732.             . xml_get_current_column_number($this->parser"): "
  733.             . xml_error_string($this->parser));
  734.             xml_parser_free($this->parser);
  735.             return false;
  736.         }
  737.  
  738.         // close parser
  739.         xml_parser_free($this->parser);
  740.  
  741.         // check for errors
  742.         if ($this->workflow['state'== 'error'{
  743.             return LogUtil::registerError($this->workflow['errorMessage']);
  744.         }
  745.  
  746.         $this->mapWorkflow();
  747.  
  748.         if (!$this->validate()) {
  749.             return false;
  750.         }
  751.  
  752.         $this->workflow['workflow']['module'$module;
  753.         $this->workflow['workflow']['id'$schemaName;
  754.  
  755.         return $this->workflow;
  756.     }
  757.  
  758.  
  759.     /**
  760.      * Map workflow
  761.      * marshall data in to meaningful associative arrays
  762.      *
  763.      */
  764.     function mapWorkflow()
  765.     {
  766.         $states = $this->workflow['states'];
  767.         $actions $this->workflow['actions'];
  768.  
  769.         // create associative arrays maps
  770.         $actionMap array();
  771.         $stateMap array();
  772.  
  773.         foreach($states as $state{
  774.             $stateMap[$state['id']] = array($state['id'], $state['title'], $state['description']);
  775.             foreach($actions as $action) {
  776.                 if (($action['state'] == 'initial') ||
  777.                    ($action['state'] == null) ||
  778.                    ($action['state'] == $state['id'])) {
  779.                     if ($action['state'] == 'initial' || $action['state'] == null) {
  780.                         $stateID = 'initial';
  781.                     }
  782.                     else
  783.                     if (($action['state']) == $state['id']) {
  784.                         $stateID = $state['id'];
  785.                     }
  786.  
  787.                     // change the case of array keys for parameter variables
  788.                     $operations = &$action['operations'];
  789.                     $ak = array_keys($operations);
  790.                     foreach($ak as $key) {
  791.                         $parameters = &$operations[$key]['parameters'];
  792.                         $parameters = array_change_key_case($parameters, CASE_LOWER);
  793.                     }
  794.  
  795.                     // commit results
  796.                     $actionID = $action['id'];
  797.                     $actionMap[$stateID][$actionID] = $action;
  798.                 }
  799.             }
  800.         }
  801.  
  802.         // commit new array to workflow
  803.         $this->workflow['actions'$actionMap;
  804.         $this->workflow['states'$stateMap;
  805.     }
  806.  
  807.  
  808.     /**
  809.      * validate workflow actions
  810.      *
  811.      */
  812.     function validate()
  813.     {
  814.         $stateMap = $this->workflow['states'];
  815.         $states $this->workflow['actions'];
  816.         $ak array_keys($states);
  817.         foreach ($ak as $stateID{
  818.             $actions = $this->workflow['actions'][$stateID];
  819.             foreach ($actions as $action{
  820.                 $stateName = $action['state'];
  821.                 if ($stateName != null) {
  822.                     if (!isset($stateMap[$stateName]))
  823.                         return LogUtil::registerError("Unknown state name '$stateName' in action '" . $action['title'] ."'");
  824.                 }
  825.  
  826.                 if (isset($action['nextState'])) {
  827.                     $nextStateName = $action['nextState'];
  828.                 }
  829.  
  830.                 if (isset($nextStateName)) {
  831.                     if (!isset($stateMap[$nextStateName]))
  832.                     return LogUtil::registerError("Unknown next-state name '$nextStateName' in action '" . $action['title'] ."'");
  833.                 }
  834.  
  835.                 foreach($action['operations'] as $operation) {
  836.                     if (isset($operation['parameters']['NEXTSTATE'])) {
  837.                         $stateName = $operation['parameters']['NEXTSTATE'];
  838.                         if (!isset($stateMap[$stateName]))
  839.                         return LogUtil::registerError("Unknown state name '$stateName' in action '" . $action['title'] . "' -  operation '$operation[name]'");
  840.                     }
  841.                 }
  842.             }
  843.         }
  844.         return true;
  845.     }
  846.  
  847.  
  848.     /**
  849.      * XML start element handler
  850.      *
  851.      * @access private
  852.      * @param  object $parser
  853.      * @param  string $name
  854.      * @param  array $attribs
  855.      */
  856.     function startElement($parser, $name, $attribs)
  857.     {
  858.         $state = &$this->workflow['state'];
  859.  
  860.         if ($state == 'initial'{
  861.             if ($name == 'WORKFLOW') {
  862.                 $state = 'workflow';
  863.                 $this->workflow['workflow'array();
  864.             } else {
  865.                 $state = 'error';
  866.                 $this->workflow['errorMessage'$this->unexpectedXMLError($name$state." ".__LINE__);
  867.             }
  868.         }
  869.         else
  870.         if ($state == 'workflow') {
  871.             if ($name == 'TITLE'  ||  $name == 'DESCRIPTION') {
  872.                 $this->workflow['value''';
  873.             }
  874.             else
  875.             if ($name == 'STATES') {
  876.                 $state = 'states';
  877.                 $this->workflow['states'array();
  878.             }
  879.             else
  880.             if ($name == 'ACTIONS') {
  881.                 $state = 'actions';
  882.                 $this->workflow['actions'array();
  883.             } else {
  884.                 $this->workflow['errorMessage'$this->unexpectedXMLError($name$state." ".__LINE__);
  885.                 $state 'error';
  886.             }
  887.         }
  888.         else
  889.         if ($state == 'states') {
  890.             if ($name == 'STATE') {
  891.                 $this->workflow['stateValue'array('id' => trim($attribs['ID']));
  892.                 $state 'state';
  893.             } else {
  894.                 $this->workflow['errorMessage'$this->unexpectedXMLError($name$state." ".__LINE__);
  895.                 $state 'error';
  896.             }
  897.         }
  898.         else
  899.         if ($state == 'state') {
  900.             if ($name == 'TITLE'  ||  $name == 'DESCRIPTION') {
  901.                 $this->workflow['value''';
  902.             } else {
  903.                 $this->workflow['errorMessage'$this->unexpectedXMLError($name$state." ".__LINE__);
  904.                 $state 'error';
  905.             }
  906.         }
  907.         else
  908.         if ($state == 'actions') {
  909.             if ($name == 'ACTION') {
  910.                 $this->workflow['action'array('id' => trim($attribs['ID'])'operations' => array()'state' => null);
  911.                 $state 'action';
  912.             } else {
  913.                 $this->workflow['errorMessage'$this->unexpectedXMLError($name$state." ".__LINE__);
  914.                 $state 'error';
  915.             }
  916.         }
  917.         else
  918.         if ($state == 'action') {
  919.             if ($name == 'TITLE'  ||  $name == 'DESCRIPTION'  ||  $name == 'PERMISSION'  ||  $name == 'STATE'  ||  $name == 'NEXTSTATE') {
  920.                 $this->workflow['value''';
  921.             }
  922.             else
  923.             if ($name == 'OPERATION') {
  924.                 $this->workflow['value''';
  925.                 $this->workflow['operationParameters'$attribs;
  926.             } else {
  927.                 $this->workflow['errorMessage'$this->unexpectedXMLError($name$state." ".__LINE__);
  928.                 $state 'error';
  929.             }
  930.         }
  931.         else
  932.         if ($state == '') {
  933.             if ($name == '') {
  934.                 $state = '';
  935.             } else {
  936.                 $this->workflow['errorMessage'$this->unexpectedXMLError($name$state." ".__LINE__);
  937.                 $state 'error';
  938.             }
  939.         }
  940.         else
  941.         if ($state == 'error') {
  942.            ; // ignore
  943.         } else {
  944.             $this->workflow['errorMessage'_PNWF_STATEERROR " '$state" . " '$name'";
  945.             $state = 'error';
  946.         }
  947.     }
  948.  
  949.  
  950.     /**
  951.      * XML end element handler
  952.      *
  953.      * @access private
  954.      * @param  object $parser
  955.      * @param  string $name
  956.      */
  957.     function endElement($parser, $name)
  958.     {
  959.         $state = &$this->workflow['state'];
  960.  
  961.         if ($state == 'workflow'{
  962.             if ($name == 'TITLE') {
  963.                 $this->workflow['workflow']['title'$this->workflow['value'];
  964.             }
  965.             else
  966.             if ($name == 'DESCRIPTION') {
  967.                 $this->workflow['workflow']['description'$this->workflow['value'];
  968.             }
  969.         }
  970.         else
  971.         if ($state == 'state') {
  972.             if ($name == 'TITLE') {
  973.                 $this->workflow['stateValue']['title'$this->workflow['value'];
  974.             }
  975.             else
  976.             if ($name == 'DESCRIPTION') {
  977.                 $this->workflow['stateValue']['description'$this->workflow['value'];
  978.             }
  979.             else
  980.             if ($name == 'STATE') {
  981.                 $this->workflow['states'][$this->workflow['stateValue'];
  982.                 $this->workflow['stateValue'null;
  983.                 $state 'states';
  984.             }
  985.         }
  986.         else
  987.         if ($state == 'action') {
  988.             if ($name == 'TITLE') {
  989.                 $this->workflow['action']['title'$this->workflow['value'];
  990.             }
  991.             else
  992.             if ($name == 'DESCRIPTION') {
  993.                 $this->workflow['action']['description'$this->workflow['value'];
  994.             }
  995.             else
  996.             if ($name == 'PERMISSION') {
  997.                 $this->workflow['action']['permission'trim($this->workflow['value']);
  998.             }
  999.             else
  1000.             if ($name == 'STATE') {
  1001.                 $this->workflow['action']['state'trim($this->workflow['value']);
  1002.             }
  1003.             else
  1004.             if ($name == 'OPERATION') {
  1005.                 $this->workflow['action']['operations'][array('name' => trim($this->workflow['value']),
  1006.                 'parameters' => $this->workflow['operationParameters']);
  1007.                 $this->workflow['operation'null;
  1008.             }
  1009.             else
  1010.             if ($name == 'NEXTSTATE') {
  1011.                 $this->workflow['action']['nextState'trim($this->workflow['value']);
  1012.             }
  1013.             else
  1014.             if ($name == 'ACTION') {
  1015.                 $this->workflow['actions'][$this->workflow['action'];
  1016.                 $this->workflow['action'null;
  1017.                 $state 'actions';
  1018.             }
  1019.         }
  1020.         else
  1021.         if ($state == 'actions') {
  1022.             if ($name == 'ACTIONS') {
  1023.                 $state = 'workflow';
  1024.             }
  1025.         }
  1026.         else
  1027.         if ($state == 'states') {
  1028.             if ($name == 'STATES') {
  1029.                 $state = 'workflow';
  1030.             }
  1031.         }
  1032.  
  1033.     }
  1034.  
  1035.  
  1036.     /**
  1037.      * XML data element handler
  1038.      *
  1039.      * @access private
  1040.      * @param  object $parser parser object
  1041.      * @param  string $data
  1042.      */
  1043.     function characterData($parser, $data)
  1044.     {
  1045.         $value = &$this->workflow['value'];
  1046.         $value .= $data;
  1047.     }
  1048.  
  1049.  
  1050.     /**
  1051.      * hander for unexpected XML errors
  1052.      *
  1053.      * @param string $name
  1054.      * @param string $state
  1055.      * @return string
  1056.      */
  1057.     function unexpectedXMLError($name, $state)
  1058.     {
  1059.         return "Unexpected $name tag in $state state";
  1060.     }
  1061.  
  1062.     // Declare object variables;
  1063.     var $parser;
  1064.     var $workflow;

Documentation generated on Fri, 18 Jul 2008 21:59:17 +0200 by phpDocumentor 1.4.1