source: branches/1.2/workflow/inc/engine/src/ProcessManager/ActivityManager.php @ 1349

Revision 1349, 46.7 KB checked in by niltonneto, 15 years ago (diff)

Ticket #561 - Inclusão do módulo Workflow faltante nessa versão.

  • Property svn:executable set to *
Line 
1<?php
2require_once(GALAXIA_LIBRARY.SEP.'src'.SEP.'ProcessManager'.SEP.'BaseManager.php');
3/**
4 * Handles process activities and transitions.
5 * This class is used to add, remove, modify and list
6 * activities used in the Workflow engine.
7 * Activities are managed in a per-process level, each
8 * activity belongs to some process.
9 *
10 * @package Galaxia
11 * @license http://www.gnu.org/copyleft/gpl.html GPL
12 * @todo Build real process graph
13 */
14class ActivityManager extends BaseManager {
15  /**
16   * @var object $process_manager ProcessManager instance used sometimes by this object
17   * @access public
18   */
19  var $process_manager;   
20
21  /**
22   * Constructor
23   *
24   * @param object &$db ADOdb Used to manipulate activities in the database
25   * @return object ActivityManager instance
26   * @access public
27   */
28  function ActivityManager(&$db)
29  {
30    parent::BaseManager($db);
31    $this->child_name = 'ActivityManager';
32    require_once(GALAXIA_LIBRARY.SEP.'src'.SEP.'ProcessManager' .SEP . 'ProcessManager.php');
33    //$this->process_manager is not set here to avoid object A loading objetc B loading object A, etc...
34   
35  }
36
37  /**
38   * Collect errors from all linked objects which could have been used by this object.
39   * Each child class should instantiate this function with her linked objetcs, calling get_error(true)
40   *
41   * @param bool $debug False by default, if true debug messages can be added to 'normal' messages
42   * @param string $prefix Appended to the debug message
43   * @return void
44   * @access public
45   */
46  function collect_errors($debug=false, $prefix='')
47  {
48    parent::collect_errors($debug, $prefix);
49    if (isset($this->process_manager)) $this->error[] = $this->process_manager->get_error(false, $debug, $prefix);
50  }
51
52  /**
53   * Binds an activity to a role
54   *
55   * @param int $activityId
56   * @param int $roleId
57   * @param bool $readonly False by default, if true the role will be in read-only mode
58   * @access public
59   * @return void
60   */
61  function add_activity_role($activityId, $roleId, $readonly = false)
62  {
63    $query = 'delete from '.GALAXIA_TABLE_PREFIX.'activity_roles where wf_activity_id=? and wf_role_id=?';
64    $this->query($query,array($activityId, $roleId));
65    $query = 'insert into '.GALAXIA_TABLE_PREFIX.'activity_roles (wf_activity_id,wf_role_id,wf_readonly) values(?,?,?)';
66    $this->query($query,array($activityId, $roleId, (int)$readonly));
67  }
68 
69  /**
70   * Gets the roles bound to an activity
71   *
72   * @param int $activityId
73   * @return array
74   * @access public
75   */
76  function get_activity_roles($activityId) {
77    $query = 'select wf_activity_id,roles.wf_role_id,roles.wf_name, wf_readonly
78              from '.GALAXIA_TABLE_PREFIX.'activity_roles gar
79              INNER JOIN '.GALAXIA_TABLE_PREFIX.'roles roles on gar.wf_role_id = roles.wf_role_id
80              where wf_activity_id=?';
81    $result = $this->query($query,array($activityId));
82    $ret = Array();
83    if (!(empty($result)))
84    {
85      while($res = $result->fetchRow())
86      {
87        $ret[] = $res;
88      }
89    }
90    return $ret;
91  }
92 
93  /**
94   * Unbinds a role from an activity
95   *
96   * @param int $activityId 
97   * @param int $roleId
98   * @return void
99   * @access public
100   */
101  function remove_activity_role($activityId, $roleId)
102  {
103    $query = 'delete from '.GALAXIA_TABLE_PREFIX.'activity_roles
104              where wf_activity_id=? and wf_role_id=?';
105    $this->query($query, array($activityId,$roleId));
106  }
107
108  /**
109   * Removes an user from all fields where he could be on every activities.
110   *
111   * @param int $user User id
112   * @return void
113   * @access public
114   */
115  function remove_user($user) 
116  {
117    $query = 'update '.GALAXIA_TABLE_PREFIX.'activities set wf_default_user=? where wf_default_user=?';
118    $this->query($query,array('',$user));
119  }
120 
121  /**
122   * Transfers all references to one user to another one in the activities
123   *
124   * @param array $user_array Associative, keys are: 'old_user' is current user id and 'new_user' the new user id
125   * @return void
126   * @access public
127   */
128  function transfer_user($user_array)
129  {
130    $query = 'update '.GALAXIA_TABLE_PREFIX.'activities set wf_default_user=? where wf_default_user=?';
131    $this->query($query,array($user_array['new_user'],$user_array['old_user']));
132  } 
133
134  /**
135   * Checks if a transition exists
136   *
137   * @param int $pid Process id
138   * @param int    $actFromId Id for starting activity
139   * @param int    $actToId Id for end activity
140   * @return bool
141   * @access public
142   */
143  function transition_exists($pid,$actFromId,$actToId)
144  {
145    return($this->getOne('select count(*) from
146      '.GALAXIA_TABLE_PREFIX.'transitions where wf_p_id=? and wf_act_from_id=? and wf_act_to_id=?',
147      array($pid,$actFromId,$actToId)
148    ));
149  }
150 
151  /**
152   * Adds a transition
153   *
154   * @param int $pId Process id
155   * @param int    $actFromId Id for starting activity
156   * @param int    $actToId Id for end activity
157   * @return bool
158   * @access public
159   */ 
160  function add_transition($pId, $actFromId, $actToId)
161  {
162    // No circular transitions allowed
163    if($actFromId == $actToId) {
164        $this->error[] = tra('No circular transitions allowed.');
165        return false;
166    }
167   
168    // Rule: if act is not spl-x or spl-a it can't have more than
169    // 1 outbound transition.
170    $a1 = $this->get_activity($actFromId);
171    $a2 = $this->get_activity($actToId);
172    if(!$a1 || !$a2) {
173        $this->error[] = tra('No activites');
174        return false;
175    }
176    if($a1['wf_type'] != 'switch' && $a1['wf_type'] != 'split') {
177      if($this->getOne('select count(*) from '.GALAXIA_TABLE_PREFIX.'transitions where wf_act_from_id=?', array($actFromId)) > 1) {
178        $this->error[] = tra('Cannot add transition only split or switch activities can have more than one outbound transition');
179        return false;
180      }
181    }
182   
183    // Rule: if act is standalone or view no transitions allowed
184    if(($a1['wf_type'] == 'standalone' || $a2['wf_type']=='standalone') || ($a1['wf_type'] == 'view' || $a2['wf_type']=='view') )
185    {
186        $this->error[] = tra('No transitions allowed for standalone or view activities');
187        return false;
188    }
189    // No inbound to start
190    if($a2['wf_type'] == 'start') {
191        $this->error[] = tra('No inbound for start activity');
192        return false;
193    }
194    // No outbound from end
195    if($a1['wf_type'] == 'end') {
196        $this->error[] = tra('No outbound for end activity');
197        return false;
198    }
199     
200   
201    $query = 'delete from `'.GALAXIA_TABLE_PREFIX.'transitions` where `wf_act_from_id`=? and `wf_act_to_id`=?';
202    $this->query($query,array($actFromId, $actToId));
203    $query = 'insert into `'.GALAXIA_TABLE_PREFIX.'transitions`(`wf_p_id`,`wf_act_from_id`,`wf_act_to_id`) values(?,?,?)';
204    $this->query($query,array($pId, $actFromId, $actToId));
205
206    return true;
207  }
208 
209  /**
210   * Removes a transition
211   *
212   * @param int    $actFromId Id for starting activity
213   * @param int    $actToId Id for end activity
214   * @return bool
215   * @access public
216   */ 
217  function remove_transition($actFromId, $actToId)
218  {
219    $query = 'delete from '.GALAXIA_TABLE_PREFIX.'transitions where wf_act_from_id=? and wf_act_to_id=?';
220    $this->query($query, array($actFromId,$actToId));
221    return true;
222  }
223 
224  /**
225   * Removes all activity transitions
226   *
227   * @param int    $pId Process id
228   * @param int    $aid Activity id
229   * @return void
230   * @access public
231   */   
232  function remove_activity_transitions($pId, $aid)
233  {
234    $query = 'delete from '.GALAXIA_TABLE_PREFIX.'transitions where wf_p_id=? and (wf_act_from_id=? or wf_act_to_id=?)';
235    $this->query($query,array($pId,$aid,$aid));
236  }
237 
238  /**
239   * Returns all process's transitions
240   *
241   * @param int    $pId Process id
242   * @param int    $actid Activity id
243   * @return array
244   * @access public
245   */ 
246  function get_process_transitions($pId,$actid=0)
247  {
248    $bindvars = Array();
249    $bindvars[] = $pId;
250    if(!$actid) {
251        $query = 'select a1.wf_name as wf_act_from_name, a2.wf_name as wf_act_to_name, wf_act_from_id, wf_act_to_id
252          from '.GALAXIA_TABLE_PREFIX.'transitions gt
253          INNER JOIN '.GALAXIA_TABLE_PREFIX.'activities a1 ON gt.wf_act_from_id = a1.wf_activity_id
254          INNER JOIN '.GALAXIA_TABLE_PREFIX.'activities a2 ON gt.wf_act_to_id = a2.wf_activity_id
255          where gt.wf_p_id = ?';
256    } else {
257        $query = 'select a1.wf_name as wf_act_from_name, a2.wf_name as wf_act_to_name, wf_act_from_id, wf_act_to_id
258        from '.GALAXIA_TABLE_PREFIX.'transitions gt
259        INNER JOIN '.GALAXIA_TABLE_PREFIX.'activities a1 ON gt.wf_act_from_id = a1.wf_activity_id
260        INNER JOIN '.GALAXIA_TABLE_PREFIX.'activities a2 ON gt.wf_act_to_id = a2.wf_activity_id
261        where gt.wf_p_id = ?
262        and (wf_act_from_id = ?)';
263        $bindvars[] = $actid;
264    }
265    $result = $this->query($query, $bindvars, -1, -1, true, 'a1.wf_flow_num');
266    $ret = Array();
267    if (!(empty($result)))
268    {
269      while($res = $result->fetchRow())
270      {
271        $ret[] = $res;
272      }
273    }
274    return $ret;
275  }
276 
277  /**
278   * Indicates whether an activity is autoRouted
279   *
280   * @param int $actid Activity id
281   * @return bool
282   * @access public
283   */
284  function activity_is_auto_routed($actid)
285  {
286    return($this->getOne('select count(*) from '.GALAXIA_TABLE_PREFIX.'activities where wf_activity_id=? and wf_is_autorouted=?', array($actid,'y')));
287  }
288 
289  /**
290   * Returns process's activities
291   *
292   * @param int pId    Process id
293   * @return array   
294   * @access public
295   */
296  function get_process_activities($pId)
297  {
298    $query = 'select * from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id=?';
299    $result = $this->query($query, array($pId));
300    $ret = Array();
301    if (!(empty($result)))
302    {
303      while($res = $result->fetchRow())
304      {
305        $ret[] = $res;
306      }
307    }
308    return $ret;
309  }
310    /**
311    * Returns a set of activities that may have transitions, i.e., non-standalone/view activities
312    *
313    * @param int $pId Process id
314    * @param bool $type_exclusion Type to exclude from the result
315    * @return array Activities set in ['data'] and count in ['cant']
316    * @access public
317    */
318  function &get_transition_activities($pId, $type_exclusion = false)
319  {
320    $where = '';
321    $wheres = array();
322    $wheres[] = "wf_type <> 'standalone'";
323    $wheres[] = "wf_type <> 'view'";
324
325    if( $type_exclusion )
326    {
327      $wheres[] = "wf_type <> '".$type_exclusion."'";
328    }
329    $where = implode(' and ', $wheres);
330     
331    return $this->list_activities($pId, 0, -1, 'wf_flow_num__asc', '', $where);
332  }
333 
334  /**
335  * Returns a set of activities having transitions
336  *
337  * @param int $pId Process id
338  * @return array Activities set, with activities set in ['data'] and count in ['cant']
339  * @access public
340  */
341  function get_process_activities_with_transitions($pId)
342  {
343    $query = 'select distinct a1.wf_name as wf_name, a1.wf_activity_id as wf_activity_id, a1.wf_flow_num
344      from '.GALAXIA_TABLE_PREFIX.'transitions gt
345      INNER JOIN '.GALAXIA_TABLE_PREFIX.'activities a1 ON gt.wf_act_from_id = a1.wf_activity_id
346      INNER JOIN '.GALAXIA_TABLE_PREFIX.'activities a2 ON gt.wf_act_to_id = a2.wf_activity_id
347      where gt.wf_p_id = ? ';
348    $result = $this->query($query, array($pId),  -1, -1, true, ' a1.wf_flow_num');
349    $ret = Array();
350    if (!(empty($result)))
351    {
352      while($res = $result->fetchRow())
353      {
354        $ret[] = $res;
355      }
356    }
357    $retval = Array();
358    $retval['data'] = $ret;
359    $retval['cant'] = count($ret);
360    return $retval;
361  }
362
363  /**
364   * Builds process graph
365   *
366   * @param int $pId Process id
367   * @return bool
368   * @access public
369   */
370  function build_process_graph($pId)
371  {
372    if (!(isset($this->process_manager))) $this->process_manager = new ProcessManager($this->db);
373    $attributes = Array(
374   
375    );
376    $graph = new Process_GraphViz(true,$attributes);
377    $name = $this->process_manager->_get_normalized_name($pId);
378    $graph->set_pid($name);
379   
380    //Get the config of the workflow where we'll have some tips for the graph
381    $myconfneeds = array(
382      'draw_roles' => true,
383      'font_size' => 12,
384      );
385    $configs = $this->process_manager->getConfigValues($pId,true,true,$myconfneeds);
386
387    // Nodes are process activities so get
388    // the activities and add nodes as needed
389    $nodes = $this->get_process_activities($pId);
390   
391    $standaloneActivities = array();
392    foreach($nodes as $node)
393    {
394      if ($node['wf_type'] == 'standalone')
395        $standaloneActivities[] = $node['wf_name'];
396      if($node['wf_is_interactive']=='y')
397      {
398        $color='black';
399        $fillcolor='0.6,0.6,0.9'; //blue TLS values
400      }
401      else
402      {
403        $color='black';
404        $fillcolor='0.25,1,0.8';//green in TLS values
405      }
406      // get the fontsize, defined in the process
407      $fontsize = $configs['font_size'];
408      // define a fontname
409      $fontname = 'serif';
410      // if asked add roles on the graph
411      if ($configs['draw_roles'])
412      {
413        $activity_roles = $this->get_activity_roles($node['wf_activity_id']);
414      }
415      // fill activity roles
416      $act_role= '';
417      if (isset($activity_roles))
418      {
419        foreach ($activity_roles as $role)
420        {
421          // the / is escaped and space seems to be necessary,
422          //issues with some special characters if no spaces between this char and the /
423          $act_role = $act_role." \\n[".$role['wf_name']."]";
424        }
425      }
426      $auto[$node['wf_name']] = $node['wf_is_autorouted'];
427      $graph->addNode($node['wf_name'],array('URL'=>"foourl?wf_activity_id=".$node['wf_activity_id'],
428                                      'label'=>$node['wf_name'].$act_role,
429                                      'shape' => $this->_get_activity_shape($node['wf_type']),
430                                      'color' => $color,
431                                      'fillcolor'=> $fillcolor,
432                                      'style' => 'filled',
433                                      'fontsize' => $fontsize,
434                                      'fontname' => $fontname
435                                      )
436                     );
437    }
438   
439    // Now add edges, edges are transitions,
440    // get the transitions and add the edges
441    $edges = $this->get_process_transitions($pId);
442    foreach($edges as $edge)
443    {
444      if($auto[$edge['wf_act_from_name']] == 'y') {
445        $color = '0.25,1,0.28'; #dark green in TLS values
446        $arrowsize = 1;
447      } else {
448        $color = 'red2'; #blue in TLS values
449        $arrowsize= 1;
450      }
451        $graph->addEdge(array($edge['wf_act_from_name'] => $edge['wf_act_to_name']), array('color'=>$color,arrowsize=>$arrowsize));   
452    }
453
454    /* group the standalone activities vertically (workaround) */
455    $from = $to = null;
456    foreach ($standaloneActivities as $sa)
457    {
458      list($from, $to) = array($to, $sa);
459      if (!is_null($from))
460        $graph->addEdge(array($from => $to), array('color'=>'white'));
461    }
462   
463   
464    // Save the map image and the image graph
465    $graph->image_and_map();
466    unset($graph);
467    return true;   
468  }
469 
470 
471  /**
472   * Validates if a process can be activated checking the
473   * process activities and transitions the rules are:
474   * 0) No circular activities
475   * 1) Must have only one a start and end activity
476   * 2) End must be reachable from start
477   * 3) Interactive activities must have a role assigned
478   * 4) Roles should be mapped
479   * 5) Standalone and view activities cannot have transitions
480   * 6) Non intractive activities non-auto routed must have some role so the user can "send" the activity
481   * 7) start activities must be autorouted and interactive
482   *
483   * @param int $pId Process id
484   * @return bool
485   * @access public
486   */
487  function validate_process_activities($pId)
488  {
489    $errors = Array();
490    $warnings = Array();
491    // Pre rule no cricular activities
492    $cant = $this->getOne('select count(*) from '.GALAXIA_TABLE_PREFIX.'transitions where wf_p_id=? and wf_act_from_id=wf_act_to_id'
493      , array($pId));
494    if($cant) {
495      $errors[] = tra('Circular reference found some activity has a transition leading to itself');
496    }
497
498    // Rule 1 must have exactly one start and end activity
499    $cant = $this->getOne('select count(*) from '
500      .GALAXIA_TABLE_PREFIX.'activities where wf_p_id=? and wf_type=?', array($pId,'start'));
501    if($cant < 1) {
502      $errors[] = tra('Process does not have a start activity');
503    }
504    $cant = $this->getOne('select count(*) from '
505      .GALAXIA_TABLE_PREFIX.'activities where wf_p_id=? and wf_type=?', array($pId,'end'));
506    if($cant != 1) {
507      $errors[] = tra('Process does not have exactly one end activity');
508    }
509   
510    // Rule 2 end must be reachable from start
511    // and Rule 7 start activities must be autorouted and interactive
512    $nodes = Array();
513    $endId = $this->getOne('select wf_activity_id from '
514      .GALAXIA_TABLE_PREFIX.'activities where wf_p_id=? and wf_type=?', array($pId,'end'));
515    if ((!isset($endId)) || ($endId=='') || ($endId == 0))
516    {
517      //no end
518      $errors[] = tra('this process has no end activity');
519      $endId = 0;
520    }
521   
522    $aux['id']=$endId;
523    $aux['visited']=false;
524    $nodes[] = $aux;
525   
526    $query = 'select wf_is_autorouted,wf_is_interactive,wf_activity_id from '
527      .GALAXIA_TABLE_PREFIX.'activities where wf_p_id=? and wf_type=?';
528    $result = $this->query($query, array($pId,'start'));
529    while($res = $result->fetchRow()) {
530      $start_node['id'] = $res['wf_activity_id'];
531      if(!($res['wf_is_interactive'] == 'y')) {
532            $errors[] = tra('start activities must be interactive');
533      }
534      if(!($res['wf_is_autorouted'] == 'y')) {
535            $errors[] = tra('start activities must be autorouted');
536      }
537    }
538    $start_node['visited']=true;   
539   
540    while($this->_list_has_unvisited_nodes($nodes) && !$this->_node_in_list($start_node,$nodes)) {
541      for($i=0;$i<count($nodes);$i++) {
542        $node=&$nodes[$i];
543        if(!$node['visited']) {
544          $node['visited']=true;         
545          $query = 'select wf_act_from_id from '.GALAXIA_TABLE_PREFIX.'transitions where wf_act_to_id=?';
546          $result = $this->query($query, array($node['id']));
547          $ret = Array();
548          while($res = $result->fetchRow()) { 
549            $aux['id'] = $res['wf_act_from_id'];
550            $aux['visited']=false;
551            if(!$this->_node_in_list($aux,$nodes)) {
552              $nodes[] = $aux;
553            }
554          }
555        }
556      }
557    }
558   
559    if(!$this->_node_in_list($start_node,$nodes)) {
560      // Start node is NOT reachable from the end node
561      $errors[] = tra('End activity is not reachable from start activity');
562    }
563   
564    //Rule 3: interactive activities must have a role
565    //assigned.
566    //Rule 5: standalone and view activities can't have transitions
567    $query = 'select * from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id = ?';
568    $result = $this->query($query, array($pId));
569    while($res = $result->fetchRow()) { 
570      $aid = $res['wf_activity_id'];
571      if($res['wf_is_interactive'] == 'y') {
572          $cant = $this->getOne('select count(*) from '
573            .GALAXIA_TABLE_PREFIX.'activity_roles where wf_activity_id=?', array($res['wf_activity_id']));
574          if(!$cant) {
575            $errors[] = tra('Activity %1 is interactive but has no role assigned', $res['wf_name']);
576          }
577      } else {
578        if( $res['wf_type'] != 'end' && $res['wf_is_autorouted'] == 'n') {
579          $cant = $this->getOne('select count(*) from '
580            .GALAXIA_TABLE_PREFIX.'activity_roles where wf_activity_id=?', array($res['wf_activity_id']));
581          if(!$cant)
582          {
583              $errors[] = tra('Activity %1 is non-interactive and non-autorouted but has no role assigned', $res['wf_name']);
584          }
585        }
586      }
587      if(($res['wf_type']=='standalone')||($res['wf_type']=='view')) {
588        if($this->getOne('select count(*) from '
589          .GALAXIA_TABLE_PREFIX.'transitions where wf_act_from_id=? or wf_act_to_id=?', array($aid,$aid)))
590        {
591           $errors[] = tra('Activity %1 is standalone or view but has transitions', $res['wf_name']);
592        }
593      }
594
595    }
596   
597   
598    //Rule4: roles should be mapped
599    $query = 'select * from '.GALAXIA_TABLE_PREFIX.'roles where wf_p_id = ?';
600    $result = $this->query($query, array($pId));
601    while($res = $result->fetchRow()) {     
602        $cant = $this->getOne('select count(*) from '
603          .GALAXIA_TABLE_PREFIX.'user_roles where wf_role_id=?', array($res['wf_role_id']));
604        if(!$cant) {
605          $warnings[] = tra('Role %1 is not mapped', $res['wf_name']);
606        }       
607    }
608   
609   
610    // End of rules
611
612    // Validate process sources
613    $serrors=$this->validate_process_sources($pId);
614    $errors = array_merge($errors,$serrors['errors']);
615    $warnings = array_merge($warnings,$serrors['warnings']);
616   
617    $this->error = array_merge ($this->error, $errors);
618    $this->warning = array_merge ($this->warning, $warnings);
619   
620   
621   
622    $isValid = (count($errors)==0) ? 'y' : 'n';
623
624    $query = 'update '.GALAXIA_TABLE_PREFIX.'processes set wf_is_valid=? where wf_p_id=?';
625    $this->query($query, array($isValid,$pId));
626   
627    $this->_label_nodes($pId);   
628   
629    return ($isValid=='y');
630   
631  }
632 
633  /**
634   * Validates process sources
635   * Rules:
636   * 1) Interactive activities (non-standalone or view) must use complete()
637   * 2) Standalone activities must not use $instance
638   * 3) Switch activities must use setNextActivity
639   * 4) Non-interactive activities cannot use complete()
640   * 5) View activities cannot use $instance->set
641   *
642   * @param int $pid Process id
643   * @return bool
644   * @access public
645   */
646  function validate_process_sources($pid)
647  {
648    $errors=Array();
649    $errors['errors'] = Array();
650    $errors['warnings'] = Array();
651    $wf_procname= $this->getOne('select wf_normalized_name from '.GALAXIA_TABLE_PREFIX.'processes where wf_p_id=?', array($pid));
652   
653    $query = 'select * from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id=?';
654    $result = $this->query($query, array($pid));
655    while($res = $result->fetchRow()) {         
656      $actname = $res['wf_normalized_name'];
657      $source = GALAXIA_PROCESSES.SEP.$wf_procname.SEP.'code'.SEP.'activities'.SEP.$actname.'.php';
658      if (!file_exists($source)) {
659          $errors['errors'][] = tra('source code file for activity %1 is not avaible', $actname);
660          continue;
661      }
662      $fp = fopen($source,'r');
663      if (!$fp)
664      {
665        $errors['errors'][] = tra('source code for activity %1 is not avaible', $actname);
666      }
667      else
668      {
669        $data='';
670        while(!feof($fp))
671        {
672          $data.=fread($fp,8192);
673        }
674        fclose($fp);
675      }
676      if($res['wf_type']=='standalone') {
677          if(strstr($data,'$instance')) {
678            $errors['warnings'][] = tra('Activity %1 is standalone and is using the $instance object', $res['wf_name']);
679          }   
680      }
681      else
682      {
683        if($res['wf_type']=='view')
684        {
685          if(strstr($data,'$instance->set'))
686          {
687            $errors['errors'][] = tra('Activity %1 is view and is using the $instance object in write mode', $res['wf_name']);
688          }
689        }
690        else
691        { // for all others than standalone or view ...
692          if($res['wf_is_interactive']=='y')
693          {
694            if(!strstr($data,'$instance->complete()'))
695            {
696              $errors['warnings'][] = tra('Activity %1 is interactive so it must use the $instance->complete() method', $res['wf_name']);
697            }
698          }
699          else
700          { // not interactive ...
701            if(strstr($data,'$instance->complete()'))
702            {
703              $errors['errors'][] = tra('Activity %1 is non-interactive so it must not use the $instance->complete() method', $res['wf_name']);
704            }
705          }
706          if($res['wf_type']=='switch')
707          {
708            if(!strstr($data,'$instance->setNextActivity('))
709            {
710              $errors['warnings'][] = tra('Activity %1 is switch so it must use $instance->setNextActivity($actname) method', $res['wf_name']);
711            }
712          }
713        }
714      }   
715    }
716    return $errors;
717  }
718 
719  /**
720   * Indicates if an activity with the same name exists.
721   * If you gives an activityId this activity won't be checked (if the activity already exists give this activity id)
722   *
723   * @param int $pId Process id
724   * @param string $name Activity name to be checked
725   * @param int $activityId
726   * @return bool
727   * @access public
728   */
729  function activity_name_exists($pId,$name, $activityId=0)
730  {
731    $name = addslashes($this->_normalize_name($name));
732    $array_args = array($pId, $name, $activityId);
733    $number = $this->getOne('select count(*) from '.GALAXIA_TABLE_PREFIX.'activities
734            where wf_p_id=? and wf_normalized_name=? and not(wf_activity_id=?)', $array_args);
735    return !($number==0);
736  }
737 
738  /**
739   * Gets activity information fields
740   * Warning: get_activity requires no more processId, an activity id is far enough to return informations about an activity
741   *
742   * @param int $activityId
743   * @return array description
744   * @access public
745   */
746  function get_activity($activityId)
747  {
748    require_once(GALAXIA_LIBRARY.SEP.'src'.SEP.'API'.SEP . 'BaseActivity.php');
749    $act = new BaseActivity($this->db);
750    //Warning, we now use the BaseActivity object for it, interactivity and autorouting is now true/fales, not y/n
751    return ($act->getActivity($activityId,false,true, true));
752  /*
753    $query = "select * from ".GALAXIA_TABLE_PREFIX."activities where wf_activity_id=?";
754    $result = $this->query($query, array($activityId));
755    $res = False;
756    if (!(empty($result)))
757    {
758      $res = $result->fetchRow();
759    }
760    $res['toto']=$toto;
761    return $res;
762  */
763  }
764 
765  /**
766   * Lists activities at a per-process level
767   *
768   * @param int $pId Process id
769   * @param int $offset First row number (see $maxRecords)
770   * @param int $maxRecords Maximum number of records returned
771   * @param string $sort_mode Sort order
772   * @param string $find Searched name or description of the activity
773   * @param string $where SQL string appended
774   * @param bool $count_roles True by default and is adding stat queries results about number of roles concerned by the activity
775   * @return array Associative having two keys, 'cant' for the number of total activities and 'data' containing an associative array with the activities content
776   * @access public
777   */
778  function &list_activities($pId,$offset,$maxRecords,$sort_mode,$find,$where='', $count_roles=true)
779  {
780    $sort_mode = str_replace("__"," ",$sort_mode);
781    if($find) {
782      $findesc = '%'.$find.'%';
783      $mid=' where wf_p_id=? and ((wf_name like ?) or (wf_description like ?))';
784      $bindvars = array($pId,$findesc,$findesc);
785    } else {
786      $mid=' where wf_p_id=? ';
787      $bindvars = array($pId);
788    }
789    if($where) {
790      $mid.= " and ($where) ";
791    }
792    $query = 'select * from '.GALAXIA_TABLE_PREFIX."activities $mid";
793    $query_cant = 'select count(*) from '.GALAXIA_TABLE_PREFIX."activities $mid";
794    $result = $this->query($query,$bindvars,$maxRecords,$offset,true,$sort_mode);
795    $cant = $this->getOne($query_cant,$bindvars);
796    $ret = Array();
797    while($res = $result->fetchRow()) {
798      if ($count_roles) {
799        $res['wf_roles'] = $this->getOne('select count(*) from '
800        .GALAXIA_TABLE_PREFIX.'activity_roles where wf_activity_id=?',array($res['wf_activity_id']));
801      }
802      $ret[] = $res;
803    }
804    $retval = Array();
805    $retval['data'] = $ret;
806    $retval['cant'] = $cant;
807    return $retval;
808  }
809 
810 
811 
812  /**
813  * Removes an activity.
814  *
815  * This will also remove transitions concerning the activity, roles associations, agents associations
816  * anassociated agents data
817  * @param int $pId is the process id
818  * @param int $activityId is the activity id
819  * @param bool $transaction is optional and true by default, it will permit to encapsulate the different deletes
820  * in a transaction, if you already started a transaction encapsulating this one use this paramater to prevent
821  * us to open a new one.
822  * @return bool true if it was ok, false if nothing was done
823  * @access public
824  */
825  function remove_activity($pId, $activityId, $transaction = true)
826  {
827    if (!(isset($this->process_manager))) $this->process_manager = new ProcessManager($this->db);
828    $proc_info = $this->process_manager->get_process($pId);
829    $actname = $this->_get_normalized_name($activityId);
830   
831    // start a transaction
832    $this->db->StartTrans();
833     
834    //the activity
835    $query = 'delete from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id=? and wf_activity_id=?';
836    $this->query($query,array($pId,$activityId));
837   
838    //transitions
839    $query = 'select wf_act_from_id,wf_act_to_id from '
840      .GALAXIA_TABLE_PREFIX.'transitions where wf_act_from_id=? or wf_act_to_id=?';
841    $result = $this->query($query, array($activityId,$activityId));
842    while($res = $result->fetchRow()) {
843      $this->remove_transition($res['wf_act_from_id'], $res['wf_act_to_id']);
844    }
845   
846    //roles
847    $query = 'delete from '.GALAXIA_TABLE_PREFIX.'activity_roles where wf_activity_id=?';
848    $this->query($query, array($activityId));
849   
850    //agents
851    $query = 'select wf_agent_id, wf_agent_type from '.GALAXIA_TABLE_PREFIX.'activity_agents where wf_activity_id=?';
852    $result = $this->query($query, array($activityId));
853    if (!(empty($result)))
854    {
855      while ($res = $result->fetchRow())
856      {
857        //delete the associated agent
858        $query = 'delete from '.GALAXIA_TABLE_PREFIX.'agent_'.$res['wf_agent_type'].' where wf_agent_id=?';
859        $this->query($query, array($res['wf_agent_id']));
860      }
861      //now we can delete the association table
862      $query = 'delete from '.GALAXIA_TABLE_PREFIX.'activity_agents where wf_activity_id=?';
863      $this->query($query, array($activityId));
864    }
865   
866    // And we have to remove the user files
867    // for this activity
868    $wf_procname = $proc_info['wf_normalized_name'];
869    unlink(GALAXIA_PROCESSES.SEP.$wf_procname.SEP.'code'.SEP.'activities'.SEP.$actname.'.php');
870    if (file_exists(GALAXIA_PROCESSES.SEP.$wf_procname.SEP.'code'.SEP.'templates'.SEP.$actname.'.tpl')) {
871      @unlink(GALAXIA_PROCESSES.SEP.$wf_procname.SEP.'code'.SEP.'templates'.$actname.'.tpl');
872    }
873   
874    // perform commit (return true) or Rollback (return false)
875    return $this->db->CompleteTrans();
876  }
877 
878  /**
879   * Updates or inserts a new activity in the database, $vars is an associative
880   * array containing the fields to update or to insert as needed.
881   * @param int $pId is the processId
882   * @param int $activityId is the activityId
883   * @param array $vars
884   * @param bool $create_files
885   * @return int The new activity id
886   * @access public
887  */
888  function replace_activity($pId, $activityId, $vars, $create_files=true)
889  {
890    if (!(isset($this->process_manager))) $this->process_manager = new ProcessManager($this->db);
891    $TABLE_NAME = GALAXIA_TABLE_PREFIX.'activities';
892    $now = date("U");
893    $vars['wf_last_modif']=$now;
894    $vars['wf_p_id']=$pId;
895    $vars['wf_normalized_name'] = $this->_normalize_name($vars['wf_name']);   
896
897    $proc_info = $this->process_manager->get_process($pId);
898   
899   
900    foreach($vars as $key=>$value)
901    {
902      $vars[$key]=addslashes($value);
903    }
904 
905    if($activityId) {
906      $oldname = $this->_get_normalized_name($activityId);
907      // update mode
908      $first = true;
909      $bindvars = Array();
910      $query ="update $TABLE_NAME set";
911      foreach($vars as $key=>$value) {
912        if(!$first) $query.= ',';
913        if(!is_numeric($value)) $value="'".$value."'";
914        $query.= " $key=$value ";
915        $first = false;
916      }
917      $query .= ' where wf_p_id=? and wf_activity_id=? ';
918      $bindvars[] = $pId;
919      $bindvars[] = $activityId;
920      $this->query($query, $bindvars);
921     
922      $newname = $vars['wf_normalized_name'];
923      // if the activity is changing name then we
924      // should rename the user_file for the activity
925     
926      $user_file_old = GALAXIA_PROCESSES.SEP.$proc_info['wf_normalized_name'].SEP.'code'.SEP.'activities'.SEP.$oldname.'.php';
927      $user_file_new = GALAXIA_PROCESSES.SEP.$proc_info['wf_normalized_name'].SEP.'code'.SEP.'activities'.SEP.$newname.'.php';
928      rename($user_file_old, $user_file_new);
929
930      $user_file_old = GALAXIA_PROCESSES.SEP.$proc_info['wf_normalized_name'].SEP.'code'.SEP.'templates'.SEP.$oldname.'.tpl';
931      $user_file_new = GALAXIA_PROCESSES.SEP.$proc_info['wf_normalized_name'].SEP.'code'.SEP.'templates'.SEP.$newname.'.tpl';
932      if ($user_file_old != $user_file_new) {
933        @rename($user_file_old, $user_file_new);
934      }
935    } else {
936     
937      // When inserting activity names can't be duplicated
938      if($this->activity_name_exists($pId, $vars['wf_name'])) {
939          return false;
940      }
941      unset($vars['wf_activity_id']);
942      // insert mode
943      $first = true;
944      $query = "insert into $TABLE_NAME(";
945      foreach(array_keys($vars) as $key) {
946        if(!$first) $query.= ',';
947        $query.= "$key";
948        $first = false;
949      }
950      $query .=") values(";
951      $first = true;
952      foreach(array_values($vars) as $value) {
953        if(!$first) $query.= ',';
954        if(!is_numeric($value)) $value="'".$value."'";
955        $query.= "$value";
956        $first = false;
957      }
958      $query .=")";
959      $this->query($query);
960      //commented, last_modif time can be far away on high load! And if your database is not incrementing Ids it is not a database
961      //$activityId = $this->getOne('select max(wf_activity_id) from '.$TABLE_NAME.' where wf_p_id=? and wf_last_modif=?', array($pId,$now));
962      $activityId = $this->getOne('select max(wf_activity_id) from '.$TABLE_NAME.' where wf_p_id=? ', array($pId));
963      $ret = $activityId;
964      /* seems to be a debug code
965      if(!$activityId) {
966         print("select max(wf_activity_id) from $TABLE_NAME where wf_p_id=$pId and wf_last_modif=$now");
967         die;     
968      }
969      */
970      // Should create the code file
971      if ($create_files) {
972          $wf_procname = $proc_info["wf_normalized_name"];
973          $fw = fopen(GALAXIA_PROCESSES.SEP.$wf_procname.SEP.'code'.SEP.'activities'.SEP.$vars['wf_normalized_name'].'.php','w');
974            fwrite($fw,'<'.'?'.'php'."\n".'?'.'>');
975            fclose($fw);
976           
977             if($vars['wf_is_interactive']=='y') {
978                $fw = fopen(GALAXIA_PROCESSES.SEP.$wf_procname.SEP.'code'.SEP.'templates'.SEP.$vars['wf_normalized_name'].'.tpl','w');
979                if (defined('GALAXIA_TEMPLATE_HEADER') && GALAXIA_TEMPLATE_HEADER) {
980                  fwrite($fw,GALAXIA_TEMPLATE_HEADER . "\n");
981                }
982                fclose($fw);
983            }
984      }
985    }
986    // Get the id
987    return $activityId;
988  }
989
990  /**
991  * Associates an activity with an agent type and create or retrieve the associated agent
992  * if the agent of this type for this activity already exists we return his id.
993  * @param int activityId: the activity id
994  * @param string $agentType: The type of the agent
995  * @return agent_id created or retrieved after this association was done or false in case of problems
996  * @access public
997  */
998  function add_activity_agent($activityId, $agentType)
999  {
1000    $agent_id = 0;
1001    //this will retrieve info directly from the agent table, not on the recorded activity-agent association table
1002    $agents = $this->get_activity_agents($activityId);
1003    foreach ($agents as $agent)
1004    {
1005      if ($agent['wf_type']==$agentType)
1006      {
1007        //we found an agent which were previously associated with the activity
1008        $agent_id = $agent['wf_agent_id'];
1009        //but we still need to ensure it is still associated with the activity
1010        $actualAssoc = $this->getOne('select wf_activity_id from '.GALAXIA_TABLE_PREFIX.'activity_agents where wf_activity_id=?', array($activityId));
1011        if (!($actualAssoc == $activityId))
1012        {
1013          $query = 'insert into '.GALAXIA_TABLE_PREFIX.'activity_agents (wf_activity_id,wf_agent_id,wf_agent_type) values(?,?,?)';
1014          $this->query($query,array($activityId, $agentId, $agentType));
1015        }
1016        return $agent_id;
1017      }
1018    }
1019    //if we are here we did not find this type of agent for this activity
1020      //TODO: check agent type is in autorized list
1021    //add a new agent record
1022    $query = 'insert into '.GALAXIA_TABLE_PREFIX.'agent_'.$agentType.' (wf_agent_id) values(DEFAULT)';
1023    $this->query($query);
1024    $query = 'select max(wf_agent_id) from '.GALAXIA_TABLE_PREFIX.'agent_'.$agentType;
1025    $agentId = $this->getOne($query);
1026    //record the association
1027    $query = 'insert into '.GALAXIA_TABLE_PREFIX.'activity_agents (wf_activity_id,wf_agent_id,wf_agent_type) values(?,?,?)';
1028    $this->query($query,array($activityId, $agentId, $agentType));
1029    return $agentId;
1030  }
1031 
1032  /**
1033  * Gets the agents (id and type) associated to an activity
1034  * @param int $activityId is the activity id
1035  * @return array an associative array which can be empty
1036  * @access public
1037  */
1038  function get_activity_agents($activityId)
1039  {
1040    $query = 'select wf_agent_id, wf_agent_type
1041              from '.GALAXIA_TABLE_PREFIX.'activity_agents
1042              where wf_activity_id=?';
1043    $result = $this->query($query,array($activityId));
1044    $ret = Array();
1045    if (!(empty($result)))
1046    {
1047      while($res = $result->fetchRow())
1048      {
1049        $ret[] = $res;
1050      }
1051    }
1052   
1053    return $ret;
1054  }
1055
1056  /**
1057  * Gets the agent datas (with id and type as well) associated to an activity
1058  * from the agent table
1059  * @param int $activityId is the activity id
1060  * @param string $agentType is the agent type, giving the table name for the engine
1061  * @return array an associative array which can be empty if we did not find the agentType
1062  * for this activity
1063  * @access public
1064  */
1065  function get_activity_agent_data($activityId, $agentType)
1066  {
1067    $query = 'select wf_agent_id
1068              from '.GALAXIA_TABLE_PREFIX.'activity_agents
1069              where wf_activity_id=? and wf_agent_type=?';
1070    $agent_id = $this->getOne($query,array($activityId,$agentType));
1071    $ret = Array();
1072    if ($agent_id==0)
1073    {
1074      return $ret;
1075    }
1076    else
1077    {
1078      $query = 'select * from '.GALAXIA_TABLE_PREFIX.'agent_'.$agentType.' where wf_agent_id=?';
1079      $result = $this->query($query,array($agent_id));
1080      if (!(empty($result)))
1081      {
1082        while($res = $result->fetchRow())
1083        {
1084          $ret[] = $res;
1085        }
1086        //fake agent_type column, we know what it should be and that we should have only one record
1087        $ret[0]['wf_agent_type'] = $agentType;
1088      }
1089    }
1090    return $ret[0];
1091  }
1092 
1093  /**
1094  * Removes an agent from an activity
1095  * @param int activityId the activity id
1096  * @param int $agentId The id of the agent
1097  * @param bool $removeagent is false by default, if true the agent himself is destroyed, that means if you
1098  * re-associate the activity with the same agent type all previous configuration will be lost
1099  * @return void
1100  * @access public
1101  */
1102  function remove_activity_agent($activityId, $agentId, $removeagent=false)
1103  {
1104   
1105    if ($removeagent)
1106    {
1107      $query = 'select wf_agent_type from '.GALAXIA_TABLE_PREFIX.'activity_agents
1108          where wf_activity_id=? and wf_agent_id=?';
1109      $agent_type = $this->getOne($query, array($activityId, $agentId));
1110      $this->remove_agent($agentId, $agent_type);
1111    }
1112    $query = 'delete from '.GALAXIA_TABLE_PREFIX.'activity_agents
1113            where wf_activity_id=? and wf_agent_id=?';
1114    $this->query($query, array($activityId, $agentId));
1115  }
1116
1117  /**
1118  * Remove an agent.
1119  * @param int $agentId is the agent id
1120  * @param string $agent_type is the agent_type
1121  * @return void
1122  * @access public
1123  */
1124  function remove_agent($agentId, $agent_type)
1125  {
1126    $query = 'delete from '.GALAXIA_TABLE_PREFIX.'agent_'.$agent_type.'
1127      where wf_agent_id=?';
1128    $this->query($query, array($agentId));
1129  }
1130
1131  /**
1132  * Sets if an activity is interactive or not
1133  * @param int $pId The process id
1134  * @param int $actid The activity id
1135  * @param string $value y or n
1136  * @access public
1137  */
1138  function set_interactivity($pId, $actid, $value)
1139  {
1140    $query = 'update '.GALAXIA_TABLE_PREFIX.'activities set wf_is_interactive=? where wf_p_id=? and wf_activity_id=?';
1141    $this->query($query, array($value, $pId, $actid));
1142  }
1143
1144  /**
1145  * Sets if an activity is auto routed or not
1146  * @param int $pId The processo id
1147  * @param int $actid The activity id
1148  * @param string $value y or n
1149  * @return void
1150  * @access public
1151  */
1152  function set_autorouting($pId, $actid, $value)
1153  {
1154    $query = 'update '.GALAXIA_TABLE_PREFIX.'activities set wf_is_autorouted=? where wf_p_id=? and wf_activity_id=?';
1155    $this->query($query, array($value, $pId, $actid));
1156  }
1157
1158  /**
1159   * Sets the default user for an activity
1160   * @param int $activityId The activity id
1161   * @param mixed $default_user This param can be the id of a user or '' for all
1162   * @return void
1163   * @access public
1164  */
1165  function set_default_user($activityId, $default_user)
1166  {
1167    if ($default_user=='')
1168    {
1169      $default_user='*';
1170    }
1171    $query  = 'update '.GALAXIA_TABLE_PREFIX.'activities set wf_default_user=? where wf_activity_id=?';
1172    $this->query($query, array($default_user, $activityId));
1173  }
1174
1175  /**
1176   * Gets the default user for an activity
1177   * if performAccessCheck is true then this function will check if this user as really access granted
1178   * to the given activity.
1179   * If wrong or no default user or the user has no access grant and performAccessCheck was asked, '*' is returned
1180   *
1181   * @param int $activityId Activity Id
1182   * @param boll $performAccessCheck
1183   * @return mixed User Id or '*' if no default user is set
1184   * @access public
1185  */
1186  function get_default_user($activityId, $performAccessCheck=true)
1187  {
1188    $query  = 'Select wf_default_user from '.GALAXIA_TABLE_PREFIX.'activities where wf_activity_id=?';
1189    $result = $this->getOne($query,array($activityId));
1190    if (!(isset($result)) || ($result=='') || ($result==false))
1191    {
1192      $result='*';
1193    }
1194    //if we had a user and if asked we'll try to see if he has really access granted
1195    elseif ( (!($result=='*')) && $performAccessCheck)
1196    {
1197      $wf_security = new WfSecurity($this->db);
1198      // perform the check
1199      if (!($wf_security->checkUserAccess($result,$activityId)))
1200      {
1201        // bad check, we ignore this default_user
1202        $result='*';
1203      }
1204    }
1205    return $result;
1206  }
1207 
1208  /**
1209   * Returns activity id by pid,name (activity names are unique)
1210   * @param int $pid The process id
1211   * @param string $name The name of the activity
1212   * @return int The id of the activity
1213   * @access private
1214  */
1215  function _get_activity_id_by_name($pid,$name)
1216  {
1217    $name = addslashes($name);
1218    $bindarray = array($pid,$name);
1219    if($this->getOne('select count(*) from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id=? and wf_name=?', $bindarray))
1220    {
1221      return($this->getOne('select wf_activity_id from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id=? and wf_name=?', $bindarray));   
1222    }
1223    else
1224    {
1225      return '';
1226    }
1227  }
1228 
1229  /**
1230   * Returns the activity shape
1231   * @param string $type The type of the activity: start, end, activity, split,
1232   * switch, join, standalone, view
1233   * @return string The shape of the image representing a activity: circle,
1234   * doublecircle, box, triangle, diamond, invtriangle, hexagon
1235   * @access private
1236  */
1237  function _get_activity_shape($type)
1238  {
1239    switch($type) {
1240      case 'start':
1241          return 'circle';
1242      case 'end':
1243          return 'doublecircle';
1244      case 'activity':
1245          return 'box';
1246      case 'split':
1247          return 'triangle';
1248      case 'switch':
1249        return 'diamond';
1250      case 'join':
1251          return 'invtriangle';
1252      case 'standalone':
1253          return 'hexagon';
1254      case 'view':
1255          return 'hexagon';
1256      default:
1257          return 'egg';           
1258     
1259    }
1260
1261  }
1262
1263 
1264  /**
1265   * Returns true if a list contains unvisited nodes.
1266   * @param array $list List members are assoc arrays containing id and visited
1267   * @return bool
1268   * @access private   
1269  */
1270  function _list_has_unvisited_nodes($list)
1271  {
1272    foreach($list as $node) {
1273      if(!$node['visited']) return true;
1274    }
1275    return false;
1276  }
1277 
1278  /**
1279   * Returns true if a node is in a list
1280   * @param array $node
1281   * @param array $list List members are assoc arrays containing id and visited
1282   * @return bool
1283   * @access private
1284  */
1285  function _node_in_list($node,$list)
1286  {
1287    foreach($list as $a_node) {
1288      if($node['id'] == $a_node['id']) return true;
1289    }
1290    return false;
1291  }
1292 
1293  /**
1294   * Normalizes an activity name
1295   * @param string $name
1296   * @return string
1297   * @access private
1298  */
1299  function _normalize_name($name)
1300  {
1301    $name = str_replace(" ","_",$name);
1302    $name = preg_replace("/[^A-Za-z_0-9]/",'',$name);
1303    return $name;
1304  }
1305 
1306  /**
1307   * Returns normalized name of an activity
1308   * @param int $activityId The activity id
1309   * @return string The activity name normalized. Spaces are turned to underscore, and special chars are suppressed.
1310   * @access private
1311  */
1312  function _get_normalized_name($activityId)
1313  {
1314    return $this->getOne('select wf_normalized_name from '
1315      .GALAXIA_TABLE_PREFIX.'activities where wf_activity_id=?', array($activityId));
1316  }
1317 
1318  /**
1319   * Labels nodes. Return false if something was wrong
1320   * @access private
1321   * @param int $pId The process id
1322   * @return array 
1323  */ 
1324  function _label_nodes($pId)
1325  {
1326    ///an empty list of nodes starts the process
1327    $nodes = Array();
1328    // the end activity id
1329    $endId = $this->getOne('select wf_activity_id from '
1330      .GALAXIA_TABLE_PREFIX.'activities where wf_p_id=? and wf_type=?',array($pId,'end'));
1331    if ((!isset($endId)) || ($endId=='') || ($endId == 0))
1332    {
1333      //no end
1334      return false;
1335    }
1336   
1337    // and the number of total nodes (=activities)
1338    $cant = $this->getOne('select count(*) from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id=?', array($pId));
1339    $nodes[] = $endId;
1340    $label = $cant;
1341    $num = $cant;
1342   
1343    $query = 'update '.GALAXIA_TABLE_PREFIX.'activities set wf_flow_num=? where wf_p_id=?';
1344    $this->query($query,array($cant+1,$pId));
1345    $seen = array();
1346    while(count($nodes)) {
1347      $newnodes = Array();
1348      foreach($nodes as $node) {
1349        // avoid endless loops
1350        if (isset($seen[$node])) continue;
1351        $seen[$node] = 1;
1352        $query = 'update '.GALAXIA_TABLE_PREFIX.'activities set wf_flow_num=? where wf_activity_id=?';
1353        $this->query($query, array($num,$node));
1354        $query = 'select wf_act_from_id from '.GALAXIA_TABLE_PREFIX.'transitions where wf_act_to_id=?';
1355        $result = $this->query($query, array($node));
1356        $ret = Array();
1357        while($res = $result->fetchRow()) { 
1358          $newnodes[] = $res['wf_act_from_id'];
1359        }
1360      }
1361      $num--;
1362      $nodes=Array();
1363      $nodes=$newnodes;
1364     
1365    }
1366   
1367    $min = $this->getOne('select min(wf_flow_num) from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id=?', array($pId));
1368    $query = 'update '.GALAXIA_TABLE_PREFIX."activities set wf_flow_num=wf_flow_num-$min where wf_p_id=?";
1369    $this->query($query, array($pId));
1370   
1371    //$query = "update ".GALAXIA_TABLE_PREFIX."activities set flowNum=0 where flowNum=$cant+1";
1372    //$this->query($query);
1373    return true;
1374  }
1375}
1376?>
Note: See TracBrowser for help on using the repository browser.