source: trunk/workflow/inc/engine/src/ProcessManager/ActivityManager.php @ 7655

Revision 7655, 46.8 KB checked in by douglasz, 11 years ago (diff)

Ticket #3236 - Melhorias de performance no codigo do Expresso.

  • 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()
29  {
30    parent::BaseManager();
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 = &Factory::newInstance('ProcessManager');
373    $attributes = Array(
374   
375    );
376    $graph = &Factory::newInstance('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      $nodes_count = count($nodes);
542      for($i=0;$i<$nodes_count;++$i) {
543        $node=&$nodes[$i];
544        if(!$node['visited']) {
545          $node['visited']=true;         
546          $query = 'select wf_act_from_id from '.GALAXIA_TABLE_PREFIX.'transitions where wf_act_to_id=?';
547          $result = $this->query($query, array($node['id']));
548          $ret = Array();
549          while($res = $result->fetchRow()) { 
550            $aux['id'] = $res['wf_act_from_id'];
551            $aux['visited']=false;
552            if(!$this->_node_in_list($aux,$nodes)) {
553              $nodes[] = $aux;
554            }
555          }
556        }
557      }
558    }
559   
560    if(!$this->_node_in_list($start_node,$nodes)) {
561      // Start node is NOT reachable from the end node
562      $errors[] = tra('End activity is not reachable from start activity');
563    }
564   
565    //Rule 3: interactive activities must have a role
566    //assigned.
567    //Rule 5: standalone and view activities can't have transitions
568    $query = 'select * from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id = ?';
569    $result = $this->query($query, array($pId));
570    while($res = $result->fetchRow()) { 
571      $aid = $res['wf_activity_id'];
572      if($res['wf_is_interactive'] == 'y') {
573          $cant = $this->getOne('select count(*) from '
574            .GALAXIA_TABLE_PREFIX.'activity_roles where wf_activity_id=?', array($res['wf_activity_id']));
575          if(!$cant) {
576            $errors[] = tra('Activity %1 is interactive but has no role assigned', $res['wf_name']);
577          }
578      } else {
579        if( $res['wf_type'] != 'end' && $res['wf_is_autorouted'] == 'n') {
580          $cant = $this->getOne('select count(*) from '
581            .GALAXIA_TABLE_PREFIX.'activity_roles where wf_activity_id=?', array($res['wf_activity_id']));
582          if(!$cant)
583          {
584              $errors[] = tra('Activity %1 is non-interactive and non-autorouted but has no role assigned', $res['wf_name']);
585          }
586        }
587      }
588      if(($res['wf_type']=='standalone')||($res['wf_type']=='view')) {
589        if($this->getOne('select count(*) from '
590          .GALAXIA_TABLE_PREFIX.'transitions where wf_act_from_id=? or wf_act_to_id=?', array($aid,$aid)))
591        {
592           $errors[] = tra('Activity %1 is standalone or view but has transitions', $res['wf_name']);
593        }
594      }
595
596    }
597   
598   
599    //Rule4: roles should be mapped
600    $query = 'select * from '.GALAXIA_TABLE_PREFIX.'roles where wf_p_id = ?';
601    $result = $this->query($query, array($pId));
602    while($res = $result->fetchRow()) {     
603        $cant = $this->getOne('select count(*) from '
604          .GALAXIA_TABLE_PREFIX.'user_roles where wf_role_id=?', array($res['wf_role_id']));
605        if(!$cant) {
606          $warnings[] = tra('Role %1 is not mapped', $res['wf_name']);
607        }       
608    }
609   
610   
611    // End of rules
612
613    // Validate process sources
614    $serrors=$this->validate_process_sources($pId);
615    $errors = array_merge($errors,$serrors['errors']);
616    $warnings = array_merge($warnings,$serrors['warnings']);
617   
618    $this->error = array_merge ($this->error, $errors);
619    $this->warning = array_merge ($this->warning, $warnings);
620   
621   
622   
623    $isValid = (count($errors)==0) ? 'y' : 'n';
624
625    $query = 'update '.GALAXIA_TABLE_PREFIX.'processes set wf_is_valid=? where wf_p_id=?';
626    $this->query($query, array($isValid,$pId));
627   
628    $this->_label_nodes($pId);   
629   
630    return ($isValid=='y');
631   
632  }
633 
634  /**
635   * Validates process sources
636   * Rules:
637   * 1) Interactive activities (non-standalone or view) must use complete()
638   * 2) Standalone activities must not use $instance
639   * 3) Switch activities must use setNextActivity
640   * 4) Non-interactive activities cannot use complete()
641   * 5) View activities cannot use $instance->set
642   *
643   * @param int $pid Process id
644   * @return bool
645   * @access public
646   */
647  function validate_process_sources($pid)
648  {
649    $errors=Array();
650    $errors['errors'] = Array();
651    $errors['warnings'] = Array();
652    $wf_procname= $this->getOne('select wf_normalized_name from '.GALAXIA_TABLE_PREFIX.'processes where wf_p_id=?', array($pid));
653   
654    $query = 'select * from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id=?';
655    $result = $this->query($query, array($pid));
656    while($res = $result->fetchRow()) {         
657      $actname = $res['wf_normalized_name'];
658      $source = GALAXIA_PROCESSES.SEP.$wf_procname.SEP.'code'.SEP.'activities'.SEP.$actname.'.php';
659      if (!file_exists($source)) {
660          $errors['errors'][] = tra('source code file for activity %1 is not avaible', $actname);
661          continue;
662      }
663      $fp = fopen($source,'r');
664      if (!$fp)
665      {
666        $errors['errors'][] = tra('source code for activity %1 is not avaible', $actname);
667      }
668      else
669      {
670        $data='';
671        while(!feof($fp))
672        {
673          $data.=fread($fp,8192);
674        }
675        fclose($fp);
676      }
677      if($res['wf_type']=='standalone') {
678          if(strstr($data,'$instance')) {
679            $errors['warnings'][] = tra('Activity %1 is standalone and is using the $instance object', $res['wf_name']);
680          }   
681      }
682      else
683      {
684        if($res['wf_type']=='view')
685        {
686          if(strstr($data,'$instance->set'))
687          {
688            $errors['errors'][] = tra('Activity %1 is view and is using the $instance object in write mode', $res['wf_name']);
689          }
690        }
691        else
692        { // for all others than standalone or view ...
693          if($res['wf_is_interactive']=='y')
694          {
695            if(!strstr($data,'$instance->complete()'))
696            {
697              $errors['warnings'][] = tra('Activity %1 is interactive so it must use the $instance->complete() method', $res['wf_name']);
698            }
699          }
700          else
701          { // not interactive ...
702            if(strstr($data,'$instance->complete()'))
703            {
704              $errors['errors'][] = tra('Activity %1 is non-interactive so it must not use the $instance->complete() method', $res['wf_name']);
705            }
706          }
707          if($res['wf_type']=='switch')
708          {
709            if(!strstr($data,'$instance->setNextActivity('))
710            {
711              $errors['warnings'][] = tra('Activity %1 is switch so it must use $instance->setNextActivity($actname) method', $res['wf_name']);
712            }
713          }
714        }
715      }   
716    }
717    return $errors;
718  }
719 
720  /**
721   * Indicates if an activity with the same name exists.
722   * If you gives an activityId this activity won't be checked (if the activity already exists give this activity id)
723   *
724   * @param int $pId Process id
725   * @param string $name Activity name to be checked
726   * @param int $activityId
727   * @return bool
728   * @access public
729   */
730  function activity_name_exists($pId,$name, $activityId=0)
731  {
732    $name = addslashes($this->_normalize_name($name));
733    $array_args = array($pId, $name, $activityId);
734    $number = $this->getOne('select count(*) from '.GALAXIA_TABLE_PREFIX.'activities
735            where wf_p_id=? and wf_normalized_name=? and not(wf_activity_id=?)', $array_args);
736    return !($number==0);
737  }
738 
739  /**
740   * Gets activity information fields
741   * Warning: get_activity requires no more processId, an activity id is far enough to return informations about an activity
742   *
743   * @param int $activityId
744   * @return array description
745   * @access public
746   */
747  function get_activity($activityId)
748  {
749    require_once(GALAXIA_LIBRARY.SEP.'src'.SEP.'API'.SEP . 'BaseActivity.php');
750    $act = &Factory::newInstance('BaseActivity');
751    //Warning, we now use the BaseActivity object for it, interactivity and autorouting is now true/fales, not y/n
752    return ($act->getActivity($activityId,false,true, true));
753  /*
754    $query = "select * from ".GALAXIA_TABLE_PREFIX."activities where wf_activity_id=?";
755    $result = $this->query($query, array($activityId));
756    $res = False;
757    if (!(empty($result)))
758    {
759      $res = $result->fetchRow();
760    }
761    $res['toto']=$toto;
762    return $res;
763  */
764  }
765 
766  /**
767   * Lists activities at a per-process level
768   *
769   * @param int $pId Process id
770   * @param int $offset First row number (see $maxRecords)
771   * @param int $maxRecords Maximum number of records returned
772   * @param string $sort_mode Sort order
773   * @param string $find Searched name or description of the activity
774   * @param string $where SQL string appended
775   * @param bool $count_roles True by default and is adding stat queries results about number of roles concerned by the activity
776   * @return array Associative having two keys, 'cant' for the number of total activities and 'data' containing an associative array with the activities content
777   * @access public
778   */
779  function &list_activities($pId,$offset,$maxRecords,$sort_mode,$find,$where='', $count_roles=true)
780  {
781    $sort_mode = str_replace("__"," ",$sort_mode);
782    if($find) {
783      $findesc = '%'.$find.'%';
784      $mid=' where wf_p_id=? and ((wf_name like ?) or (wf_description like ?))';
785      $bindvars = array($pId,$findesc,$findesc);
786    } else {
787      $mid=' where wf_p_id=? ';
788      $bindvars = array($pId);
789    }
790    if($where) {
791      $mid.= " and ($where) ";
792    }
793    $query = 'select * from '.GALAXIA_TABLE_PREFIX."activities $mid";
794    $query_cant = 'select count(*) from '.GALAXIA_TABLE_PREFIX."activities $mid";
795    $result = $this->query($query,$bindvars,$maxRecords,$offset,true,$sort_mode);
796    $cant = $this->getOne($query_cant,$bindvars);
797    $ret = Array();
798    while($res = $result->fetchRow()) {
799      if ($count_roles) {
800        $res['wf_roles'] = $this->getOne('select count(*) from '
801        .GALAXIA_TABLE_PREFIX.'activity_roles where wf_activity_id=?',array($res['wf_activity_id']));
802      }
803      $ret[] = $res;
804    }
805    $retval = Array();
806    $retval['data'] = $ret;
807    $retval['cant'] = $cant;
808    return $retval;
809  }
810 
811 
812 
813  /**
814  * Removes an activity.
815  *
816  * This will also remove transitions concerning the activity, roles associations, agents associations
817  * anassociated agents data
818  * @param int $pId is the process id
819  * @param int $activityId is the activity id
820  * @param bool $transaction is optional and true by default, it will permit to encapsulate the different deletes
821  * in a transaction, if you already started a transaction encapsulating this one use this paramater to prevent
822  * us to open a new one.
823  * @return bool true if it was ok, false if nothing was done
824  * @access public
825  */
826  function remove_activity($pId, $activityId, $transaction = true)
827  {
828    if (!(isset($this->process_manager))) $this->process_manager = &Factory::newInstance('ProcessManager');
829    $proc_info = $this->process_manager->get_process($pId);
830    $actname = $this->_get_normalized_name($activityId);
831   
832    // start a transaction
833    $this->db->StartTrans();
834     
835    //the activity
836    $query = 'delete from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id=? and wf_activity_id=?';
837    $this->query($query,array($pId,$activityId));
838   
839    //transitions
840    $query = 'select wf_act_from_id,wf_act_to_id from '
841      .GALAXIA_TABLE_PREFIX.'transitions where wf_act_from_id=? or wf_act_to_id=?';
842    $result = $this->query($query, array($activityId,$activityId));
843    while($res = $result->fetchRow()) {
844      $this->remove_transition($res['wf_act_from_id'], $res['wf_act_to_id']);
845    }
846   
847    //roles
848    $query = 'delete from '.GALAXIA_TABLE_PREFIX.'activity_roles where wf_activity_id=?';
849    $this->query($query, array($activityId));
850   
851    //agents
852    $query = 'select wf_agent_id, wf_agent_type from '.GALAXIA_TABLE_PREFIX.'activity_agents where wf_activity_id=?';
853    $result = $this->query($query, array($activityId));
854    if (!(empty($result)))
855    {
856      while ($res = $result->fetchRow())
857      {
858        //delete the associated agent
859        $query = 'delete from '.GALAXIA_TABLE_PREFIX.'agent_'.$res['wf_agent_type'].' where wf_agent_id=?';
860        $this->query($query, array($res['wf_agent_id']));
861      }
862      //now we can delete the association table
863      $query = 'delete from '.GALAXIA_TABLE_PREFIX.'activity_agents where wf_activity_id=?';
864      $this->query($query, array($activityId));
865    }
866   
867    // And we have to remove the user files
868    // for this activity
869    $wf_procname = $proc_info['wf_normalized_name'];
870    unlink(GALAXIA_PROCESSES.SEP.$wf_procname.SEP.'code'.SEP.'activities'.SEP.$actname.'.php');
871    if (file_exists(GALAXIA_PROCESSES.SEP.$wf_procname.SEP.'code'.SEP.'templates'.SEP.$actname.'.tpl')) {
872      @unlink(GALAXIA_PROCESSES.SEP.$wf_procname.SEP.'code'.SEP.'templates'.$actname.'.tpl');
873    }
874   
875    // perform commit (return true) or Rollback (return false)
876    return $this->db->CompleteTrans();
877  }
878 
879  /**
880   * Updates or inserts a new activity in the database, $vars is an associative
881   * array containing the fields to update or to insert as needed.
882   * @param int $pId is the processId
883   * @param int $activityId is the activityId
884   * @param array $vars
885   * @param bool $create_files
886   * @return int The new activity id
887   * @access public
888  */
889  function replace_activity($pId, $activityId, $vars, $create_files=true)
890  {
891    if (!(isset($this->process_manager))) $this->process_manager = &Factory::newInstance('ProcessManager');
892    $TABLE_NAME = GALAXIA_TABLE_PREFIX.'activities';
893    $now = date("U");
894    $vars['wf_last_modif']=$now;
895    $vars['wf_p_id']=$pId;
896    $vars['wf_normalized_name'] = $this->_normalize_name($vars['wf_name']);   
897
898    $proc_info = $this->process_manager->get_process($pId);
899   
900   
901    foreach($vars as $key=>$value)
902    {
903      $vars[$key]=addslashes($value);
904    }
905 
906    if($activityId) {
907      $oldname = $this->_get_normalized_name($activityId);
908      // update mode
909      $first = true;
910      $bindvars = Array();
911      $query ="update $TABLE_NAME set";
912      foreach($vars as $key=>$value) {
913        if(!$first) $query.= ',';
914        if(!is_numeric($value)) $value="'".$value."'";
915        $query.= " $key=$value ";
916        $first = false;
917      }
918      $query .= ' where wf_p_id=? and wf_activity_id=? ';
919      $bindvars[] = $pId;
920      $bindvars[] = $activityId;
921      $this->query($query, $bindvars);
922     
923      $newname = $vars['wf_normalized_name'];
924      // if the activity is changing name then we
925      // should rename the user_file for the activity
926     
927      $user_file_old = GALAXIA_PROCESSES.SEP.$proc_info['wf_normalized_name'].SEP.'code'.SEP.'activities'.SEP.$oldname.'.php';
928      $user_file_new = GALAXIA_PROCESSES.SEP.$proc_info['wf_normalized_name'].SEP.'code'.SEP.'activities'.SEP.$newname.'.php';
929      rename($user_file_old, $user_file_new);
930
931      $user_file_old = GALAXIA_PROCESSES.SEP.$proc_info['wf_normalized_name'].SEP.'code'.SEP.'templates'.SEP.$oldname.'.tpl';
932      $user_file_new = GALAXIA_PROCESSES.SEP.$proc_info['wf_normalized_name'].SEP.'code'.SEP.'templates'.SEP.$newname.'.tpl';
933      if ($user_file_old != $user_file_new) {
934        @rename($user_file_old, $user_file_new);
935      }
936    } else {
937     
938      // When inserting activity names can't be duplicated
939      if($this->activity_name_exists($pId, $vars['wf_name'])) {
940          return false;
941      }
942      unset($vars['wf_activity_id']);
943      // insert mode
944      $first = true;
945      $query = "insert into $TABLE_NAME(";
946      foreach(array_keys($vars) as $key) {
947        if(!$first) $query.= ',';
948        $query.= "$key";
949        $first = false;
950      }
951      $query .=") values(";
952      $first = true;
953      foreach(array_values($vars) as $value) {
954        if(!$first) $query.= ',';
955        if(!is_numeric($value)) $value="'".$value."'";
956        $query.= "$value";
957        $first = false;
958      }
959      $query .=")";
960      $this->query($query);
961      //commented, last_modif time can be far away on high load! And if your database is not incrementing Ids it is not a database
962      //$activityId = $this->getOne('select max(wf_activity_id) from '.$TABLE_NAME.' where wf_p_id=? and wf_last_modif=?', array($pId,$now));
963      $activityId = $this->getOne('select max(wf_activity_id) from '.$TABLE_NAME.' where wf_p_id=? ', array($pId));
964      $ret = $activityId;
965      /* seems to be a debug code
966      if(!$activityId) {
967         print("select max(wf_activity_id) from $TABLE_NAME where wf_p_id=$pId and wf_last_modif=$now");
968         die;     
969      }
970      */
971      // Should create the code file
972      if ($create_files) {
973          $wf_procname = $proc_info["wf_normalized_name"];
974          $fw = fopen(GALAXIA_PROCESSES.SEP.$wf_procname.SEP.'code'.SEP.'activities'.SEP.$vars['wf_normalized_name'].'.php','w');
975            fwrite($fw,'<'.'?'.'php'."\n".'?'.'>');
976            fclose($fw);
977           
978             if($vars['wf_is_interactive']=='y') {
979                $fw = fopen(GALAXIA_PROCESSES.SEP.$wf_procname.SEP.'code'.SEP.'templates'.SEP.$vars['wf_normalized_name'].'.tpl','w');
980                if (defined('GALAXIA_TEMPLATE_HEADER') && GALAXIA_TEMPLATE_HEADER) {
981                  fwrite($fw,GALAXIA_TEMPLATE_HEADER . "\n");
982                }
983                fclose($fw);
984            }
985      }
986    }
987    // Get the id
988    return $activityId;
989  }
990
991  /**
992  * Associates an activity with an agent type and create or retrieve the associated agent
993  * if the agent of this type for this activity already exists we return his id.
994  * @param int activityId: the activity id
995  * @param string $agentType: The type of the agent
996  * @return agent_id created or retrieved after this association was done or false in case of problems
997  * @access public
998  */
999  function add_activity_agent($activityId, $agentType)
1000  {
1001    $agent_id = 0;
1002    //this will retrieve info directly from the agent table, not on the recorded activity-agent association table
1003    $agents = $this->get_activity_agents($activityId);
1004    foreach ($agents as $agent)
1005    {
1006      if ($agent['wf_type']==$agentType)
1007      {
1008        //we found an agent which were previously associated with the activity
1009        $agent_id = $agent['wf_agent_id'];
1010        //but we still need to ensure it is still associated with the activity
1011        $actualAssoc = $this->getOne('select wf_activity_id from '.GALAXIA_TABLE_PREFIX.'activity_agents where wf_activity_id=?', array($activityId));
1012        if (!($actualAssoc == $activityId))
1013        {
1014          $query = 'insert into '.GALAXIA_TABLE_PREFIX.'activity_agents (wf_activity_id,wf_agent_id,wf_agent_type) values(?,?,?)';
1015          $this->query($query,array($activityId, $agentId, $agentType));
1016        }
1017        return $agent_id;
1018      }
1019    }
1020    //if we are here we did not find this type of agent for this activity
1021      //TODO: check agent type is in autorized list
1022    //add a new agent record
1023    $query = 'insert into '.GALAXIA_TABLE_PREFIX.'agent_'.$agentType.' (wf_agent_id) values(DEFAULT)';
1024    $this->query($query);
1025    $query = 'select max(wf_agent_id) from '.GALAXIA_TABLE_PREFIX.'agent_'.$agentType;
1026    $agentId = $this->getOne($query);
1027    //record the association
1028    $query = 'insert into '.GALAXIA_TABLE_PREFIX.'activity_agents (wf_activity_id,wf_agent_id,wf_agent_type) values(?,?,?)';
1029    $this->query($query,array($activityId, $agentId, $agentType));
1030    return $agentId;
1031  }
1032 
1033  /**
1034  * Gets the agents (id and type) associated to an activity
1035  * @param int $activityId is the activity id
1036  * @return array an associative array which can be empty
1037  * @access public
1038  */
1039  function get_activity_agents($activityId)
1040  {
1041    $query = 'select wf_agent_id, wf_agent_type
1042              from '.GALAXIA_TABLE_PREFIX.'activity_agents
1043              where wf_activity_id=?';
1044    $result = $this->query($query,array($activityId));
1045    $ret = Array();
1046    if (!(empty($result)))
1047    {
1048      while($res = $result->fetchRow())
1049      {
1050        $ret[] = $res;
1051      }
1052    }
1053   
1054    return $ret;
1055  }
1056
1057  /**
1058  * Gets the agent datas (with id and type as well) associated to an activity
1059  * from the agent table
1060  * @param int $activityId is the activity id
1061  * @param string $agentType is the agent type, giving the table name for the engine
1062  * @return array an associative array which can be empty if we did not find the agentType
1063  * for this activity
1064  * @access public
1065  */
1066  function get_activity_agent_data($activityId, $agentType)
1067  {
1068    $query = 'select wf_agent_id
1069              from '.GALAXIA_TABLE_PREFIX.'activity_agents
1070              where wf_activity_id=? and wf_agent_type=?';
1071    $agent_id = $this->getOne($query,array($activityId,$agentType));
1072    $ret = Array();
1073    if ($agent_id==0)
1074    {
1075      return $ret;
1076    }
1077    else
1078    {
1079      $query = 'select * from '.GALAXIA_TABLE_PREFIX.'agent_'.$agentType.' where wf_agent_id=?';
1080      $result = $this->query($query,array($agent_id));
1081      if (!(empty($result)))
1082      {
1083        while($res = $result->fetchRow())
1084        {
1085          $ret[] = $res;
1086        }
1087        //fake agent_type column, we know what it should be and that we should have only one record
1088        $ret[0]['wf_agent_type'] = $agentType;
1089      }
1090    }
1091    return $ret[0];
1092  }
1093 
1094  /**
1095  * Removes an agent from an activity
1096  * @param int activityId the activity id
1097  * @param int $agentId The id of the agent
1098  * @param bool $removeagent is false by default, if true the agent himself is destroyed, that means if you
1099  * re-associate the activity with the same agent type all previous configuration will be lost
1100  * @return void
1101  * @access public
1102  */
1103  function remove_activity_agent($activityId, $agentId, $removeagent=false)
1104  {
1105   
1106    if ($removeagent)
1107    {
1108      $query = 'select wf_agent_type from '.GALAXIA_TABLE_PREFIX.'activity_agents
1109          where wf_activity_id=? and wf_agent_id=?';
1110      $agent_type = $this->getOne($query, array($activityId, $agentId));
1111      $this->remove_agent($agentId, $agent_type);
1112    }
1113    $query = 'delete from '.GALAXIA_TABLE_PREFIX.'activity_agents
1114            where wf_activity_id=? and wf_agent_id=?';
1115    $this->query($query, array($activityId, $agentId));
1116  }
1117
1118  /**
1119  * Remove an agent.
1120  * @param int $agentId is the agent id
1121  * @param string $agent_type is the agent_type
1122  * @return void
1123  * @access public
1124  */
1125  function remove_agent($agentId, $agent_type)
1126  {
1127    $query = 'delete from '.GALAXIA_TABLE_PREFIX.'agent_'.$agent_type.'
1128      where wf_agent_id=?';
1129    $this->query($query, array($agentId));
1130  }
1131
1132  /**
1133  * Sets if an activity is interactive or not
1134  * @param int $pId The process id
1135  * @param int $actid The activity id
1136  * @param string $value y or n
1137  * @access public
1138  */
1139  function set_interactivity($pId, $actid, $value)
1140  {
1141    $query = 'update '.GALAXIA_TABLE_PREFIX.'activities set wf_is_interactive=? where wf_p_id=? and wf_activity_id=?';
1142    $this->query($query, array($value, $pId, $actid));
1143  }
1144
1145  /**
1146  * Sets if an activity is auto routed or not
1147  * @param int $pId The processo id
1148  * @param int $actid The activity id
1149  * @param string $value y or n
1150  * @return void
1151  * @access public
1152  */
1153  function set_autorouting($pId, $actid, $value)
1154  {
1155    $query = 'update '.GALAXIA_TABLE_PREFIX.'activities set wf_is_autorouted=? where wf_p_id=? and wf_activity_id=?';
1156    $this->query($query, array($value, $pId, $actid));
1157  }
1158
1159  /**
1160   * Sets the default user for an activity
1161   * @param int $activityId The activity id
1162   * @param mixed $default_user This param can be the id of a user or '' for all
1163   * @return void
1164   * @access public
1165  */
1166  function set_default_user($activityId, $default_user)
1167  {
1168    if ($default_user=='')
1169    {
1170      $default_user='*';
1171    }
1172    $query  = 'update '.GALAXIA_TABLE_PREFIX.'activities set wf_default_user=? where wf_activity_id=?';
1173    $this->query($query, array($default_user, $activityId));
1174  }
1175
1176  /**
1177   * Gets the default user for an activity
1178   * if performAccessCheck is true then this function will check if this user as really access granted
1179   * to the given activity.
1180   * If wrong or no default user or the user has no access grant and performAccessCheck was asked, '*' is returned
1181   *
1182   * @param int $activityId Activity Id
1183   * @param boll $performAccessCheck
1184   * @return mixed User Id or '*' if no default user is set
1185   * @access public
1186  */
1187  function get_default_user($activityId, $performAccessCheck=true)
1188  {
1189    $query  = 'Select wf_default_user from '.GALAXIA_TABLE_PREFIX.'activities where wf_activity_id=?';
1190    $result = $this->getOne($query,array($activityId));
1191    if (!(isset($result)) || ($result=='') || ($result==false))
1192    {
1193      $result='*';
1194    }
1195    //if we had a user and if asked we'll try to see if he has really access granted
1196    elseif ( (!($result=='*')) && $performAccessCheck)
1197    {
1198      $wf_security = &Factory::getInstance('WfSecurity');
1199      // perform the check
1200      if (!($wf_security->checkUserAccess($result,$activityId)))
1201      {
1202        // bad check, we ignore this default_user
1203        $result='*';
1204      }
1205    }
1206    return $result;
1207  }
1208 
1209  /**
1210   * Returns activity id by pid,name (activity names are unique)
1211   * @param int $pid The process id
1212   * @param string $name The name of the activity
1213   * @return int The id of the activity
1214   * @access private
1215  */
1216  function _get_activity_id_by_name($pid,$name)
1217  {
1218    $name = addslashes($name);
1219    $bindarray = array($pid,$name);
1220    if($this->getOne('select count(*) from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id=? and wf_name=?', $bindarray))
1221    {
1222      return($this->getOne('select wf_activity_id from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id=? and wf_name=?', $bindarray));   
1223    }
1224    else
1225    {
1226      return '';
1227    }
1228  }
1229 
1230  /**
1231   * Returns the activity shape
1232   * @param string $type The type of the activity: start, end, activity, split,
1233   * switch, join, standalone, view
1234   * @return string The shape of the image representing a activity: circle,
1235   * doublecircle, box, triangle, diamond, invtriangle, hexagon
1236   * @access private
1237  */
1238  function _get_activity_shape($type)
1239  {
1240    switch($type) {
1241      case 'start':
1242          return 'circle';
1243      case 'end':
1244          return 'doublecircle';
1245      case 'activity':
1246          return 'box';
1247      case 'split':
1248          return 'triangle';
1249      case 'switch':
1250        return 'diamond';
1251      case 'join':
1252          return 'invtriangle';
1253      case 'standalone':
1254          return 'hexagon';
1255      case 'view':
1256          return 'hexagon';
1257      default:
1258          return 'egg';           
1259     
1260    }
1261
1262  }
1263
1264 
1265  /**
1266   * Returns true if a list contains unvisited nodes.
1267   * @param array $list List members are assoc arrays containing id and visited
1268   * @return bool
1269   * @access private   
1270  */
1271  function _list_has_unvisited_nodes($list)
1272  {
1273    foreach($list as $node) {
1274      if(!$node['visited']) return true;
1275    }
1276    return false;
1277  }
1278 
1279  /**
1280   * Returns true if a node is in a list
1281   * @param array $node
1282   * @param array $list List members are assoc arrays containing id and visited
1283   * @return bool
1284   * @access private
1285  */
1286  function _node_in_list($node,$list)
1287  {
1288    foreach($list as $a_node) {
1289      if($node['id'] == $a_node['id']) return true;
1290    }
1291    return false;
1292  }
1293 
1294  /**
1295   * Normalizes an activity name
1296   * @param string $name
1297   * @return string
1298   * @access private
1299  */
1300  function _normalize_name($name)
1301  {
1302    $name = str_replace(" ","_",$name);
1303    $name = preg_replace("/[^A-Za-z_0-9]/",'',$name);
1304    return $name;
1305  }
1306 
1307  /**
1308   * Returns normalized name of an activity
1309   * @param int $activityId The activity id
1310   * @return string The activity name normalized. Spaces are turned to underscore, and special chars are suppressed.
1311   * @access private
1312  */
1313  function _get_normalized_name($activityId)
1314  {
1315    return $this->getOne('select wf_normalized_name from '
1316      .GALAXIA_TABLE_PREFIX.'activities where wf_activity_id=?', array($activityId));
1317  }
1318 
1319  /**
1320   * Labels nodes. Return false if something was wrong
1321   * @access private
1322   * @param int $pId The process id
1323   * @return array 
1324  */ 
1325  function _label_nodes($pId)
1326  {
1327    ///an empty list of nodes starts the process
1328    $nodes = Array();
1329    // the end activity id
1330    $endId = $this->getOne('select wf_activity_id from '
1331      .GALAXIA_TABLE_PREFIX.'activities where wf_p_id=? and wf_type=?',array($pId,'end'));
1332    if ((!isset($endId)) || ($endId=='') || ($endId == 0))
1333    {
1334      //no end
1335      return false;
1336    }
1337   
1338    // and the number of total nodes (=activities)
1339    $cant = $this->getOne('select count(*) from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id=?', array($pId));
1340    $nodes[] = $endId;
1341    $label = $cant;
1342    $num = $cant;
1343   
1344    $query = 'update '.GALAXIA_TABLE_PREFIX.'activities set wf_flow_num=? where wf_p_id=?';
1345    $this->query($query,array($cant+1,$pId));
1346    $seen = array();
1347    while(count($nodes)) {
1348      $newnodes = Array();
1349      foreach($nodes as $node) {
1350        // avoid endless loops
1351        if (isset($seen[$node])) continue;
1352        $seen[$node] = 1;
1353        $query = 'update '.GALAXIA_TABLE_PREFIX.'activities set wf_flow_num=? where wf_activity_id=?';
1354        $this->query($query, array($num,$node));
1355        $query = 'select wf_act_from_id from '.GALAXIA_TABLE_PREFIX.'transitions where wf_act_to_id=?';
1356        $result = $this->query($query, array($node));
1357        $ret = Array();
1358        while($res = $result->fetchRow()) { 
1359          $newnodes[] = $res['wf_act_from_id'];
1360        }
1361      }
1362      $num--;
1363      $nodes=Array();
1364      $nodes=$newnodes;
1365     
1366    }
1367   
1368    $min = $this->getOne('select min(wf_flow_num) from '.GALAXIA_TABLE_PREFIX.'activities where wf_p_id=?', array($pId));
1369    $query = 'update '.GALAXIA_TABLE_PREFIX."activities set wf_flow_num=wf_flow_num-$min where wf_p_id=?";
1370    $this->query($query, array($pId));
1371   
1372    //$query = "update ".GALAXIA_TABLE_PREFIX."activities set flowNum=0 where flowNum=$cant+1";
1373    //$this->query($query);
1374    return true;
1375  }
1376}
1377?>
Note: See TracBrowser for help on using the repository browser.