Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
trigger_manager.inc
1 <?php
17 // determines if tree_location refers to all assets under itself, or itself only
18 define('SQ_TRIG_TREE_PROPAGATE', '01');
19 define('SQ_TRIG_TREE_NO_PROPAGATE','00');
20 
21 // trigger status
22 define('SQ_TRIG_STATUS_ACTIVE', '1');
23 define('SQ_TRIG_STATUS_INACTIVE', '0');
24 define('SQ_TRIG_STATUS_DEFAULT', SQ_TRIG_STATUS_INACTIVE);
25 
26 // used to determine if a trigger has failed, and if so, how
27 define('SQ_TRIG_RESULT_SUCCESS', 1);
28 define('SQ_TRIG_RESULT_FALSE', -1);
29 define('SQ_TRIG_RESULT_INVALID', -2);
30 define('SQ_TRIG_RESULT_FAILURE', 0);
31 
32 require_once SQ_INCLUDE_PATH.'/asset.inc';
33 require_once SQ_CORE_PACKAGE_PATH.'/interfaces/bridge/bridge.inc';
34 require_once 'hash.inc';
35 
36 register_implementation('trigger_manager', 'bridge');
37 
51 class Trigger_Manager extends Asset implements Bridge
52 {
53 
54  // Triggers will be stored in a array structure as category hierarchy
55  // e.g $_trigger_tree['category1']['category2'][0] represents first trigger in category1/category2
56  var $_trigger_tree;
57 
58 
66  function Trigger_Manager($assetid=0)
67  {
68  $this->Asset($assetid);
69 
70  // include base classes for conditions, actions, events
71  $this->_loadComponent('trigger_condition');
72  $this->_loadComponent('trigger_action');
73  $this->_loadComponent('trigger_event');
74 
75  }//end constructor
76 
77 
86  function __wakeup()
87  {
88  // include base classes for conditions, actions, events
89  $this->_loadComponent('trigger_condition');
90  $this->_loadComponent('trigger_action');
91  $this->_loadComponent('trigger_event');
92 
93  }//end __wakeup()
94 
95 
105  function create(Array &$link)
106  {
107  require_once SQ_CORE_PACKAGE_PATH.'/system/system_asset_fns.inc';
108  if (!system_asset_fns_create_pre_check($this)) {
109  return FALSE;
110  }
111 
112  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
113  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
114 
115  if ($linkid = parent::create($link)) {
116  if (!system_asset_fns_create_cleanup($this)) {
117  $linkid = FALSE;
118  }
119  }
120 
121  if ($linkid) {
122  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
123  } else {
124  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
125  }
126 
127  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
128  return $linkid;
129 
130  }//end create()
131 
132 
140  function _getAllowedLinks()
141  {
142  $tm_links = Array();
143 
144  $tm_links[SQ_LINK_TYPE_2]['trigger'] = Array(
145  'card' => 'M',
146  'exclusive' => FALSE,
147  );
148 
149  return $tm_links;
150 
151  }//end _getAllowedLinks()
152 
153 
160  function canClone()
161  {
162  return FALSE;
163 
164  }//end canClone()
165 
166 
176  function _getName($short_name=FALSE)
177  {
178  return $GLOBALS['SQ_SYSTEM']->am->getTypeInfo($this->type(), 'name');
179 
180  }//end _getName()
181 
182 
191  function _getComponentName($type)
192  {
193  $name = $GLOBALS['SQ_SYSTEM']->am->getTypeInfo($type, 'name');
194  return $name;
195 
196  }//end _getComponentName()
197 
198 
207  function _getComponentDescription($type)
208  {
209  return $GLOBALS['SQ_SYSTEM']->am->getTypeInfo($type, 'description');
210 
211  }//end _getComponentDescription()
212 
213 
214 //-- BRIDGE FUNCTIONS --//
215 
216 
230  function getAsset($shadowid, $type_code='', $mute_errors=FALSE)
231  {
232 
233  $asset = NULL;
234 
235  // we can only handle triggers
236  if (empty($type_code)) $type_code = 'trigger';
237 
238  $id_parts = explode(':', $shadowid);
239  $id_part = $id_parts[count($id_parts)-1];
240 
241  // create either a trigger or a folder
242  if(is_numeric($id_part)){
243  if (!empty($id_part) && $type_code == 'trigger') {
244  $id = $id_part;
245 
246  // attempt to get trigger asset
247  $GLOBALS['SQ_SYSTEM']->am->includeAsset('trigger');
248  $asset = new Trigger($this->id, $id);
249  //set up status and display name
250  $asset->short_name = ($asset->vars['active']['value'] == '1') ? $asset->vars['name']['value'] : $asset->vars['name']['value'].' (disabled)';
251  $asset->name = ($asset->vars['active']['value'] == '1') ? $asset->vars['name']['value'] : $asset->vars['name']['value'].' (disabled)';
252  $asset->status = ($asset->vars['active']['value'] == '1') ? SQ_STATUS_LIVE : SQ_STATUS_UNDER_CONSTRUCTION;
253 
254  // if no id, this trigger is bodge
255  if (empty($asset->id)) $asset = NULL;
256  }
257  }
258  else {
259  // create a shadow folder
260  $GLOBALS['SQ_SYSTEM']->am->includeAsset('trigger_folder');
261  $asset = new Trigger_Folder($shadowid);
262  $asset->status = SQ_STATUS_UNDER_CONSTRUCTION;
263  $asset->short_name = $id_part;
264 
265  }
266  return $asset;
267 
268  }//end getAsset()
269 
270 
282  function _createCategoryTriggerTree($triggers)
283  {
284  $results = Array();
285  foreach ($triggers as $trigger) {
286 
287  $my_result = &$results;
288  //category name must have right format, otherwise, it will be linked under trigger manager
289  if(empty($trigger['category']) || !preg_match('/^[a-zA-Z][a-zA-Z0-9_\- ]*(\/[a-zA-Z][a-zA-Z0-9_\- ]*)*$/',$trigger['category'])){
290  $categories = NULL;
291  }
292  else {
293  $categories = explode('/',$trigger['category']);
294  foreach ($categories as $i => $cate) {
295  $my_result = &$my_result[$cate];
296  }
297  }
298 
299  $my_result [] = $trigger;
300  }
301 
302  return $results;
303  }// end _createCategoryTriggerTree ()
304 
305 
317  function _findCategoryTriggerTree($assetid,$trigger_tree)
318  {
319  $id_parts = explode(':', $assetid);
320  array_shift($id_parts);
321 
322  // For every recursion, go deeper.
323  $new_assetid = implode(':', $id_parts);
324  if(count($id_parts) == 0 || !array_key_exists($id_parts[0],$trigger_tree)){
325  return NULL;
326  }
327  else if(count($id_parts)==1) {
328  return $trigger_tree[$id_parts[0]];
329  }
330  return $this->_findCategoryTriggerTree($new_assetid,$trigger_tree[$id_parts[0]]);
331  }// end _findCategoryTriggerTree()
332 
333 
334 
346  function _getTopTriggerAssetMapLink($parentid, $trigger_tree, &$links)
347  {
348  // temp array for links before sorting
349  $links_unsorted = Array();
350  // temp array for sorting
351  $sort_index = Array();
352 
353  if (!isset($trigger_tree) || !is_array($trigger_tree)) return NULL;
354  foreach ($trigger_tree as $cate => $trigger) {
355  $link = Array();
356 
357  //if it's a trigger, create a trigger link
358  if(is_numeric($cate)){
359 
360  $link['url'] = '';
361  $link['path'] = '';
362  $link['num_kids'] = 0;
363  $link['accessible'] = 1;
364 
365  $link['majorid'] = $this->id;
366  // link all triggers under trigger manager, it will help asset map to refresh properly
367  $link['minorid'] = implode(':', Array($this->id, $trigger['id']));
368  $link['assetid'] = implode(':', Array($this->id, $trigger['id']));
369  $link['minor_type_code'] = 'trigger';
370  $link['type_code'] = 'trigger';
371  $link['linkid'] = implode(':', Array($this->id, $trigger['id']));
372 
373  // make name and short name the same
374  $link['name'] = $trigger['name'];
375  $link['short_name'] = $trigger['active'] ? $trigger['name'] : $trigger['name'].' (disabled)';
376  $link['status'] = $trigger['active'] ? SQ_STATUS_LIVE : SQ_STATUS_UNDER_CONSTRUCTION;
377  $link['sort_order'] = NULL;
378  $link['link_type'] = SQ_LINK_TYPE_1;
379  $link['locked'] = 0;
380  $link['is_dependant'] = 0;
381 
382  $sort_index ['trigger'][] = $link['name'];
383  $links_unsorted['trigger'][] = $link;
384  }
385  // if it's category, create a folder link
386  else if(isset ($cate) && !empty($cate)) {
387 
388  $link['url'] = '';
389  $link['path'] = '';
390  $link['num_kids'] = -1;
391  $link['accessible'] = 1;
392 
393  $link['majorid'] = $parentid;
394  $link['minorid'] = implode(':', Array($parentid, $cate));
395  $link['assetid'] = implode(':', Array($parentid, $cate));
396  $link['minor_type_code'] = 'trigger_folder';
397  $link['type_code'] = 'trigger_folder';
398 
399  $link['linkid'] = implode(':', Array($parentid, $cate));
400 
401  // make name and short name the same
402  $link['name'] = $cate;
403  $link['short_name'] = $cate;
404  $link['status'] = SQ_STATUS_UNDER_CONSTRUCTION;
405  $link['sort_order'] = NULL;
406  $link['link_type'] = SQ_LINK_TYPE_1;
407  $link['locked'] = 0;
408  $link['is_dependant'] = 0;
409 
410  $sort_index ['folder'][] = $link['name'];
411  $links_unsorted ['folder'][] = $link;
412 
413  }//end else if
414  }// end foreach
415 
416  /*
417  * Sort links, folders come first, and then order by alphabet
418  */
419 
420  // make sure folder comes first
421  if(!empty($sort_index)){
422  ksort($sort_index);
423  }
424 
425 
426 
427  // make sure everything is with alphabetic order
428  foreach(array_keys($sort_index) as $type){
429  asort($sort_index[$type]);
430  foreach ($sort_index[$type] as $index => $name) {
431  // now, insert it into links array
432  $links[] = $links_unsorted[$type][$index];
433  }
434  }
435 
436  }// end _getTopTriggerAssetMapLink()
437 
438 
439 
440 
447  function getAssetMapLinks()
448  {
449  // use existing trigger manager functions to get our triggers
450  $linked_triggers = $this->getTriggerList();
451 
452  $links = Array();
453  if(!isset($linked_triggers)) return NULL;
454 
455  //create trigger tree
456  $this->_trigger_tree = $this->_createCategoryTriggerTree($linked_triggers);
457 
458  //get top level links from trigger tree
459  $this->_getTopTriggerAssetMapLink($this->id, $this->_trigger_tree, $links);
460 
461  return $links;
462 
463  }//end getAssetMapLinks()
464 
465 
474  function getAssetMapAssetInfo($assetid)
475  {
476  $asset_info = Array();
477 
478  $id_parts = explode(':', $assetid);
479  $id_part = $id_parts[count($id_parts)-1];
480  if (empty($id_part)) return $asset_info;
481 
482  // if it's trigger, return info of this trigger
483  if(is_numeric($id_part)){
484  $trigger = $this->_loadTrigger($id_part);
485  if (empty($trigger)) return $asset_info;
486 
487  $asset_info['assetid'] = $assetid;
488  $asset_info['name'] = $trigger['active'] ? $trigger['name'] : $trigger['name'].' (disabled)';
489  $asset_info['short_name'] = $trigger['active'] ? $trigger['name'] : $trigger['name'].' (disabled)';
490  $asset_info['version'] = $this->version;
491  $asset_info['status'] = $trigger['active'] ? SQ_STATUS_LIVE : SQ_STATUS_UNDER_CONSTRUCTION;
492  $asset_info['type_code'] = 'trigger';
493  $asset_info['num_kids'] = 0;
494  $asset_info['accessible'] = 1;
495  $asset_info['url'] = '';
496  $asset_info['web_path'] = '';
497  }
498  // return info of a folder
499  else {
500  $asset_info['assetid'] = $id_part;
501  $asset_info['name'] = $id_part;
502  $asset_info['short_name'] = $id_part;
503 
504  $asset_info['status'] = SQ_STATUS_UNDER_CONSTRUCTION;
505  $asset_info['type_code'] = 'folder';
506  //folders should always have at least one child
507  $asset_info['num_kids'] = -1;
508  $asset_info['accessible'] = 1;
509  $asset_info['url'] = '';
510  $asset_info['web_path'] = '';
511  }
512  return $asset_info;
513 
514  }//end getAssetMapAssetInfo()
515 
516 
540  function getLinks($assetid, $link_types, $type_code='', $strict_type_code=TRUE, $side_of_link='major', $sort_by=NULL, $dependant=NULL, $exclusive=NULL)
541  {
542  $id_parts = explode(':', $assetid);
543  //get last part of id
544  $id_part = $id_parts[count($id_parts)-1];
545  // only folders can have children, not for triggers
546  if (empty($id_part) || is_numeric($id_part)) return Array();
547 
548  $links = Array();
549 
550  // create trigger tree
551  $linked_triggers = $this->getTriggerList();
552  $this->_trigger_tree = $this->_createCategoryTriggerTree($linked_triggers);
553 
554  //get links in current level in trigger tree
555  $this->_getTopTriggerAssetMapLink($assetid, $this->_findCategoryTriggerTree($assetid,$this->_trigger_tree), $links);
556 
557  return $links;
558 
559  }//end getLinks()
560 
561 
562 
563 
579  function getParents($assetid, $type_code='', $strict_type_code=TRUE)
580  {
581  return Array();
582 
583  }//end getParents()
584 
585 
606  function getChildren($assetid, $type_code='', $strict_type_code=TRUE, $dependant=NULL, $sort_by=NULL)
607  {
608  return Array();
609 
610  }//end getChildren()
611 
612 
623  function getLineageFromURL($assetid, $protocol, $url)
624  {
625  return Array();
626 
627  }//end getLineageFromURL()
628 
629 
650  function getPermission($assetid, $permission, $granted=NULL, $and_greater=TRUE, $expand_groups=FALSE, $all_info=FALSE)
651  {
652 
653  return Array();
654 
655  }//end getPermission()
656 
657 
669  function setPermission($assetid, $userid, $permission, $granted)
670  {
671  return FALSE;
672 
673  }//end setPermission()
674 
675 
686  function deletePermission($assetid, $userid, $permission)
687  {
688  return FALSE;
689 
690  }//end deletePermission()
691 
692 
707  function getAssetInfo($assetids, $type_code=Array(), $strict_type_code=TRUE, $field='')
708  {
709  $info_array = Array();
710 
711  foreach ($assetids as $id) {
712 
713  $GLOBALS['SQ_SYSTEM']->am->setPermission($id, $GLOBALS['SQ_SYSTEM']->am->getSystemAssetid('public_user'), SQ_PERMISSION_READ, 0, TRUE, TRUE);
714  $id_parts = explode(':', $id);
715 
716  $id_part = $id_parts[count($id_parts)-1];
717  if (is_null($id_part)) continue;
718  // if asset is a trigger
719  if(is_numeric($id_part)){
720 
721  // use existing load trigger function to get trigger info
722  $trigger_info = $this->_loadTrigger($id_part);
723  if (empty($trigger_info)) continue;
724 
725  $info_array[$id]['assetid'] = $id;
726  $info_array[$id]['name'] = $trigger_info['name'];
727  $info_array[$id]['short_name'] = $trigger_info['active'] ? $trigger_info['name'] : $trigger_info['name'].' (disabled)';
728  $info_array[$id]['version'] = $this->version;
729  $info_array[$id]['status'] = $this->status;
730  $info_array[$id]['type_code'] = 'trigger';
731  $info_array[$id]['num_kids'] = 0;
732  $info_array[$id]['accessible'] = 1;
733  $info_array[$id]['url'] = '';
734  $info_array[$id]['web_path'] = '';
735 
736  if (!empty($field)) {
737  $info_array[$id] = array_get_index($info_array[$id], $field);
738  }
739  }
740  // or a folder
741  else {
742  $info_array[$id]['assetid'] = $id;
743  $info_array[$id]['name'] = $id_part;
744  $info_array[$id]['short_name'] = $id_part;
745  $info_array[$id]['version'] = $this->version;
746  $info_array[$id]['status'] = $this->status;
747  $info_array[$id]['type_code'] = 'trigger_folder';
748  $info_array[$id]['num_kids'] = -1;
749  $info_array[$id]['force_secure'] =$this->force_secure;
750  $info_array[$id]['accessible'] = 1;
751  $info_array[$id]['url'] = '';
752  $info_array[$id]['web_path'] = '';
753 
754  }//end else
755  }//end foreach
756  return $info_array;
757 
758  }//end getAssetInfo()
759 
760 
772  function assetExists($assetids)
773  {
774  return FALSE;
775 
776  }//end assetExists()
777 
778 
800  function getLink($assetid, $link_type=NULL, $type_code='', $strict_type_code=TRUE, $value=NULL, $side_of_link='major', $exclusive=NULL)
801  {
802  return Array();
803 
804  }//end getLink()
805 
806 
818  function getLinkById($linkid, $assetid=0, $side_of_link='major')
819  {
820 
821  $link_parts = explode(':', $linkid);
822  $trigger_id = $link_parts[count($link_parts)-1];
823  if (is_null($trigger_id)) return NULL;
824 
825  //if it's a trigger, load trigger detail, return link info
826  if(is_numeric($trigger_id)){
827  $trigger_info = $this->_loadTrigger($trigger_id);
828 
829  // construct template
830  $link = Array(
831  'majorid' => $this->id,
832  'minorid' => $trigger_id,
833  'link_type' => SQ_LINK_TYPE_1,
834  'major_type_code' => 'trigger_manager',
835  'minor_type_code' => 'trigger',
836  'value' => '',
837  'linkid' => $this->id.':'.$trigger_id,
838  'sort_order' => $trigger_info['id'],
839  'is_dependant' => 0,
840  'is_exclusive' => 0,
841  'locked' => 0,
842  );
843 
844  }
845  else {
846  // get parent id
847  foreach($link_parts as $node){
848  $parent_parts [] = $node;
849  }
850  array_pop($parent_parts);
851  $parent_id = implode(':',$parent_parts);
852 
853 
854  $link = Array(
855  'majorid' => $parent_id,
856  'minorid' => $trigger_id,
857  'link_type' => count($parent_parts) == 1 ? 'trigger_manager' : 'trigger_folder',
858  'minor_type_code' => 'trigger_folder',
859  'value' => '',
860  'linkid' => $trigger_id,
861  'sort_order' => NULL,
862  'is_dependant' => 0,
863  'is_exclusive' => 0,
864  'locked' => 0,
865  );
866 
867 
868  }
869 
870  return $link;
871 
872  }//end getLinkById()
873 
874 
896  function countLinks($assetid, $side_of_link='major', $link_types=0, $type_code='', $strict_type_code=TRUE, $ignore_linkid=0)
897  {
898  return 0;
899 
900  }//end countLinks()
901 
902 
923  function getLinkByAsset($assetid, $other_assetid, $link_types=NULL, $value=NULL, $side_of_link='major', $force_array=FALSE, $dependant=NULL, $exclusive=NULL)
924  {
925  return Array();
926 
927  }//end getLinkByAsset()
928 
929 
939  function getAllChildLinks($assetid, $link_type=0)
940  {
941  return Array();
942 
943  }//end getAllChildLinks()
944 
945 
960  function updateLink($linkid, $link_type=NULL, $value=NULL, $sort_order=NULL)
961  {
962  return FALSE;
963 
964  }//end updateLink()
965 
966 
976  function deleteAssetLink($linkid, $moving=FALSE)
977  {
978  // not concerned about the link id, just the trigger id
979  // as triggers only have one link (with the manager)
980  $link_parts = explode(':', $linkid);
981 
982  $trigger_id = $link_parts[count($link_parts)-1];
983  if (is_null($trigger_id)) return FALSE;
984 
985  // use trigger manager function to delete the trigger
986  if (is_numeric($trigger_id)) return $this->_deleteTrigger($trigger_id);
987 
988 
989 
990  // delete folder, that is deleting all triggers under this folder, not permitted
991  trigger_localised_error('SYS0069', E_USER_WARNING, $trigger_id);
992 
993  return FALSE;
994 
995  }//end deleteAssetLink()
996 
997 
998 
999 
1000 
1011  function canCreateLink(Asset $minor, $link_type, $exclusive)
1012  {
1013  return FALSE;
1014 
1015  }//end canCreateLink()
1016 
1017 
1034  function createAssetLink(&$major, &$minor, $link_type, $value='', $sort_order=NULL, $dependant='0', $exclusive='0', $moving=FALSE)
1035  {
1036  return 0;
1037 
1038  }//end createAssetLink()
1039 
1040 
1041 //-- TRIGGER EXECUTION --//
1042 
1043 
1054  function broadcastEvent($event_name, &$broadcaster, $parameters=NULL)
1055  {
1056 
1057  // triggers do not work in ROLLBACK mode
1058  if (SQ_ROLLBACK_VIEW) return TRUE;
1059  if (!$this->attr('enabled')) return TRUE;
1060  // We've been told not to do this right now
1061  if ($broadcaster->shouldFastTrack($event_name)) return FALSE;
1062 
1063  $am =& $GLOBALS['SQ_SYSTEM']->am;
1064 
1065  // include the event asset
1066  if (!$this->_loadComponent($event_name)) {
1067  trigger_localised_error('CORE0219', E_USER_WARNING, $event_name);
1068  return FALSE;
1069  }
1070 
1071  // check whether any triggers listen for this event
1072  if (!$this->_isEventListenedFor($event_name)) {
1073  return TRUE;
1074  }
1075 
1076  // calculate state, prepare state hash
1077  $state = $this->_calculateState($event_name, $broadcaster, $parameters);
1078  $state_hash = $this->_calculateStateHash($state);
1079 
1080  $broadcast_status = TRUE;
1081 
1082  // lookup triggers
1083  $candidate_trigger_ids = $this->_lookupCandidateTriggers($state_hash);
1084 
1085  // Enhancement #5488: Trigger Batching Specify Trigger Ids
1086  $specified_trigger_ids = !empty($parameters['specified_trigger_ids']) ? $parameters['specified_trigger_ids'] : $candidate_trigger_ids;
1087  $candidate_trigger_ids = array_intersect($candidate_trigger_ids, $specified_trigger_ids);
1088 
1089  // execute each trigger
1090  foreach ($candidate_trigger_ids as $trigger_id) {
1091 
1092  $status_message = NULL;
1093  $msg_type = NULL;
1094  // re-create a trigger
1095 
1096 
1097  $trigger = $this->_loadTrigger($trigger_id);
1098  // Timer!
1099  $GLOBALS['SQ_SYSTEM']->pm->startTimer($this, $trigger['name']);
1100  $status = $this->_executeTrigger($trigger['data'], $state);
1101  // End timer
1102  $GLOBALS['SQ_SYSTEM']->pm->stopTimer($this, $trigger['name']);
1103 
1104 
1105  // get settings
1106  $trigger_settings =& $trigger['data']['settings'];
1107  $is_blocking = array_get_index($trigger['data']['settings'], 'blocking', FALSE);
1108 
1109  // prepare message data
1110  $msg_reps = Array(
1111  'trigger_name' => $trigger['name'],
1112  'triggerid' => $trigger['id'],
1113  'event' => $event_name,
1114  'asset_name' => $broadcaster->name,
1115  'assetid' => $broadcaster->id,
1116  );
1117 
1118  switch ($status) {
1119  case SQ_TRIG_RESULT_FAILURE:
1120  if ($is_blocking) {
1121  // if the trigger is set to block, then drop out of the
1122  // loop to prevent the other triggers from running
1123  // only allowing blocking triggers to affect the broadcast status
1124  $broadcast_status = FALSE;
1125  break 2;
1126  } else {
1127  trigger_localised_error('CORE0127', E_USER_WARNING, $trigger['id'], $trigger['name'], $event_name, $broadcaster->id, $broadcaster->name);
1128  }
1129 
1130  break;
1131 
1132  case SQ_TRIG_RESULT_SUCCESS:
1133  $msg_type = 'trigger.success';
1134  break;
1135 
1136  case SQ_TRIG_RESULT_INVALID:
1137  $msg_type = 'trigger.invalid';
1138  break;
1139 
1140  default:
1141  }
1142 
1143  if (!empty($msg_type)) {
1144  // log message using messaging service
1145  $ms = $GLOBALS['SQ_SYSTEM']->getMessagingService();
1146 
1147  $message = $ms->newMessage(Array(), $msg_type, $msg_reps);
1148  $message->send();
1149  }
1150 
1151  }//end foreach
1152 
1153  // clear out the record of running triggers
1154  $this->_tmp['running_triggers'] = Array();
1155 
1156  return $broadcast_status;
1157 
1158  }//end broadcastEvent()
1159 
1160 
1171  function _calculateState($event_type, &$event_broadcaster, $parameters=NULL)
1172  {
1173  // calculate the environment state
1174  $am =& $GLOBALS['SQ_SYSTEM']->am;
1175 
1176  // for use by the trigger
1177  $state['event']['name'] = $event_type;
1178  $state['event']['data'] = $parameters;
1179 
1180  $state['asset'] =& $event_broadcaster;
1181  $state['assetid'] = $event_broadcaster->id;
1182  $state['asset_type'] = $event_broadcaster->type();
1183 
1185  $parent_links = $am->getLinks($state['assetid'], SQ_SC_LINK_ALL, NULL, TRUE, 'minor');
1186  $new_parent_links = Array();
1187  $parent_assets = Array();
1188  $parent_link_map = Array();
1189  foreach ($parent_links as $link) {
1190  $new_parent_links[$link['linkid']] = $link;
1191  $parent_assets[$link['majorid']] = $link['major_type_code'];
1192  $parent_link_map[$link['majorid']][] = $link['linkid'];
1193  }
1194  $state['immediate_parent_link_map'] = $parent_link_map;
1195  $state['immediate_parent_links'] = $new_parent_links;
1196  $state['immediate_parents'] = $parent_assets;
1197 
1198 
1200  $child_links = $am->getLinks($state['assetid'], SQ_SC_LINK_ALL, NULL, TRUE, 'major');
1201  $new_child_links = Array();
1202  $child_assets = Array();
1203  $child_link_map = Array();
1204  foreach ($child_links as $link) {
1205  $new_child_links[$link['linkid']] = $link;
1206  $child_assets[$link['minorid']] = $link['minor_type_code'];
1207  $child_link_map[$link['minorid']][] = $link['linkid'];
1208  }
1209  $state['immediate_child_link_map'] = $child_link_map;
1210  $state['immediate_child_links'] = $new_child_links;
1211  $state['immediate_children'] = $child_assets;
1212 
1213  // uses type 1,2 and 3 only
1214  $tmp_treeid = $am->getAssetTreeids($state['assetid']);
1215 
1216  if ($tmp_treeid) {
1217  $state['treeid'] = $tmp_treeid;
1218  } else {
1219  $state['treeid'] = Array();
1220  }
1221 
1222  return $state;
1223 
1224  }//end _calculateState()
1225 
1226 
1235  function _calculateStateHash(&$state)
1236  {
1237  $state_hash['event'] = $state['event']['name'];
1238  $state_hash['assetid'] = $state['assetid'];
1239  $state_hash['asset_type'] = $state['asset_type'];
1240  $state_hash['treeid'] = $state['treeid'];
1241  $state_hash['parents'] = $state['immediate_parents'];
1242  $state_hash['children'] = $state['immediate_children'];
1243 
1244  return $state_hash;
1245 
1246  }//end _calculateStateHash()
1247 
1248 
1257  function _lookupCandidateTriggers($state_hash=NULL)
1258  {// TODO TOF TEST THIS
1259  // if no state_hash, then fail
1260  if (empty($state_hash)) return Array();
1261  $db = MatrixDAL::getDb();
1262  $bind_vars = Array();
1263 
1264  // compose the link_tree SQL block
1265  $treeid_sql_array[] = 'treeid = :null_treeid';
1266  $bind_vars['null_treeid'] = (string)'-1';
1267  if (empty($state_hash['treeid'])) {
1268  $state_hash['treeid'] = Array();
1269  }
1270 
1271  $treeid_count = 0;
1272  $propagate_flag_count = 0;
1273  $no_propagate_flag_count = 0;
1274  $current_tree_length_count = 0;
1275  foreach ($state_hash['treeid'] as $current_treeid) {
1276  if (empty($current_treeid)) continue;
1277 
1278  $treeid_sql_array[] = '
1279  (
1280  (treeid != :current_treeid'.'_'.++$treeid_count.' AND SUBSTR(:current_treeid'.'_'.++$treeid_count.', 1, LENGTH(treeid)) = treeid)
1281  AND (
1282  tree_propagate = :propagate_flag'.'_'.++$propagate_flag_count.'
1283  OR (tree_propagate = :no_propagate_flag'.'_'.++$no_propagate_flag_count.' AND LENGTH(treeid) = :current_tree_length'.'_'.++$current_tree_length_count.')
1284  )
1285  )';
1286 
1287  for ($i=$treeid_count-1; $i<=$treeid_count; $i++) {
1288  $bind_vars['current_treeid_'.$i] = $current_treeid;
1289  }
1290  $bind_vars['propagate_flag_'.$propagate_flag_count] = SQ_TRIG_TREE_PROPAGATE;
1291  $bind_vars['no_propagate_flag_'.$no_propagate_flag_count] = SQ_TRIG_TREE_NO_PROPAGATE;
1292  $bind_vars['current_tree_length_'.$current_tree_length_count] = strlen($current_treeid) - SQ_CONF_ASSET_TREE_SIZE;
1293  }
1294 
1295  $treeid_sql = implode($treeid_sql_array, ' OR ');
1296 
1298  $parent_id_array = Array();
1299  $parent_type_array = Array();
1300 
1301  $parents = $state_hash['parents'];
1302  foreach ($parents as $id => $type) {
1303  $parent_id_array[] = $id;
1304  $parent_type_array[$type] = '\''.$type.'\'';
1305  }
1306 
1307  // parent IDs
1308  $parent_id_sql = 'parentid = :null_parentid';
1309  $bind_vars['null_parentid'] = (string)'-1';
1310  if (!empty($parent_id_array)) {
1311  for (reset($parent_id_array); NULL !== ($k = key($parent_id_array)); next($parent_id_array)) {
1312  $parent_id_array[$k] = '\''.(string) $parent_id_array[$k].'\'';
1313  }
1314 
1315  foreach (array_chunk($parent_id_array, 200) as $chunk) {
1316  $parent_id_sql .= ' OR parentid IN ('.implode(',', $chunk).')';
1317  }
1318  }
1319 
1320  // parent types
1321  $parent_type_sql = 'parent_type = :null_parent_type';
1322  $bind_vars['null_parent_type'] = (string)'-1';
1323  if (!empty($parent_type_array)) {
1324  $parent_type_sql .= ' OR parent_type IN ('.implode(',', $parent_type_array).')';
1325  }
1326 
1327 
1329  $child_id_sql = 'childid = :null_childid';
1330  $child_type_sql = 'child_type = :null_child_type';
1331  $bind_vars['null_childid'] = (string)'-1';
1332  $bind_vars['null_child_type'] = (string)'-1';
1333 
1334  if (!empty($state_hash['treeid'])) {
1335  // children will always be the same, regardless which treeid is used, so just choose the first one off the top
1336  $treeid = reset($state_hash['treeid']);
1337 
1338  // TODO: This gets included twice, so needs to be modified to
1339  // change the bind variables in the second query.
1340  // get all the immediate children
1341  $child_fromwhere_sql = '
1342  FROM
1343  (sq_ast_lnk_tree t INNER JOIN sq_ast_lnk l ON t.linkid = l.linkid)
1344  INNER JOIN sq_ast a ON l.minorid = a.assetid
1345  WHERE
1346  t.treeid LIKE :childsql_treeid_wildcard
1347  AND t.treeid > :childsql_treeid
1348  AND LENGTH(t.treeid) <= :childsql_treeid_length
1349  ';
1350  $bind_vars['childsql_treeid_wildcard'] = $treeid.'%';
1351  $bind_vars['childsql_treeid'] = $treeid;
1352  $bind_vars['childsql_treeid_length'] = strlen($treeid) + SQ_CONF_ASSET_TREE_SIZE;
1353 
1354  $child_id_sql .= '
1355  OR childid IN
1356  (
1357  SELECT DISTINCT l.minorid
1358  '.$child_fromwhere_sql.'
1359  )
1360  ';
1361 
1362  $child_type_sql .= '
1363  OR child_type IN
1364  (
1365  SELECT DISTINCT a.type_code
1366  '.$child_fromwhere_sql.'
1367  )
1368  ';
1369  }//end if
1370 
1371  // event
1372  $event_sql = 'event = :null_event_id';
1373  $bind_vars['null_event_id'] = (string)'-1';
1374  if (!is_null($state_hash['event'])) {
1375  $event_sql .= ' OR event = :event_id';
1376  $bind_vars['event_id'] = $state_hash['event'];
1377  }
1378 
1379  // broadcaster's asset ID
1380  $assetid_sql = 'assetid = :null_bc_assetid';
1381  $bind_vars['null_bc_assetid'] = (string)'-1';
1382  if (!is_null($state_hash['assetid'])) {
1383  $assetid_sql .= ' OR assetid = :bc_assetid';
1384  $bind_vars['bc_assetid'] = $state_hash['assetid'];
1385  }
1386 
1387  // broadcaster's asset Type
1388  $asset_type_sql = 'asset_type = :null_bc_asset_type';
1389  $bind_vars['null_bc_asset_type'] = (string)'-1';
1390  if (!is_null($state_hash['asset_type'])) {
1391  $asset_type_sql .= ' OR asset_type = :bc_asset_type';
1392  $bind_vars['bc_asset_type'] = $state_hash['asset_type'];
1393  }
1394 
1395  // compose query
1396  $sql = 'SELECT DISTINCT triggerid
1397  FROM sq_trig_hash
1398  WHERE ('.$event_sql.')
1399  AND ('.$assetid_sql.')
1400  AND ('.$asset_type_sql.')
1401  AND ('.$treeid_sql.')
1402  AND ('.$parent_id_sql.')
1403  AND ('.$parent_type_sql.')
1404  AND ('.$child_id_sql.')
1405  AND ('.$child_type_sql.')';
1406 
1407  $result = NULL;
1408  try {
1409  $query = MatrixDAL::preparePdoQuery($sql);
1410  foreach ($bind_vars as $bind_var => $bind_value) {
1411  MatrixDAL::bindValueToPdo($query, $bind_var, $bind_value);
1412  }
1413  $result = MatrixDAL::executePdoAssoc($query, 0);
1414  } catch (Exception $e) {
1415  throw new Exception('unable to get the candidate triggers: '.$e->getMessage());
1416  }
1417 
1418  return $result;
1419 
1420  }//end _lookupCandidateTriggers()
1421 
1422 
1434  function _executeTrigger($trigger_data, &$state_data)
1435  {
1436  $status = SQ_TRIG_RESULT_SUCCESS;
1437 
1438 
1439  $conditions = $trigger_data['conditions'];
1440  $actions = $trigger_data['actions'];
1441 
1442  if (isset($trigger_data['settings'])) {
1443  $is_blocking = array_get_index($trigger_data['settings'], 'blocking', FALSE);
1444  } else {
1445  $is_blocking = FALSE;
1446  }
1447 
1448  $am =& $GLOBALS['SQ_SYSTEM']->am;
1449 
1450  // check each condition
1451  foreach ($conditions as $condition) {
1452  // include condition code
1453  if (!$this->_loadComponent($condition['type'])) {
1454  return SQ_TRIG_RESULT_INVALID;
1455  }
1456 
1457  // check condition
1458  // dynamic substitution of static condition class
1459  $condition_success = call_user_func_array(Array($condition['type'], 'evaluate'), Array($condition['data'], &$state_data));
1460 
1461  // if condition fails return with Success
1462  // (trigger does not need execution, false alarm but trigger did not fail)
1463  if ($condition_success == $condition['inverse_condition']) {
1464  return SQ_TRIG_RESULT_FALSE;
1465  }
1466  }
1467 
1468 
1469  // all conditions pass. now perform the actions.
1470  // NOTE: this is not thread-safe
1471  // for thread safety all the conditions and actions need to be a part of one transaction
1472  // we rely on the fact that this function will be wrappend in a transaction
1473 
1474  // this relies on the state remaining constant throughout trigger execution
1475  // this assumption will be the first to blame for unpredictable behaviour
1476 
1477  // include action code in one go, in case one of the middle actions are missing
1478  foreach ($actions as $action) {
1479  if (!$this->_loadComponent($action['type'])) {
1480  return SQ_TRIG_RESULT_INVALID;
1481  }
1482  }
1483 
1484  // init locks - this will be used to store locks to release after executing all the actions
1485  $locks_held = Array();
1486 
1487  // perform actions
1488  foreach ($actions as $action) {
1489  $not_required = array_get_index($action, 'not_required', FALSE);
1490  $ignore_permissions = array_get_index($action, 'ignore_permissions', FALSE);
1491 
1492  if ($ignore_permissions || !$GLOBALS['SQ_SYSTEM']->runLevelEnables(SQ_SECURITY_LOCKING)) {
1493 
1494  // execute action
1495  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
1496  $action_success = call_user_func_array(Array($action['type'], 'execute'), Array($action['data'], &$state_data));
1497  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
1498 
1499  } else {
1500  $lock_success = TRUE;
1501  // acquire locks
1502  // dynamic substitution of static action class
1503  $action_locks = call_user_func_array(Array($action['type'], 'getLocks'), Array($action['data'], &$state_data));
1504 
1505  foreach ($action_locks as $lock_assetid => $lock_types) {
1506  foreach ($lock_types as $lock_type) {
1507  $lock_success = $am->acquireLock($lock_assetid, $lock_type);
1508  // add this lock to the list of locks to release if we actually acquired, not if we only updated
1509  if ($lock_success==1) {
1510  $locks_held[$lock_assetid][$lock_type] = TRUE;
1511  } else if ($lock_success==0) {
1512  // bail if we can't get all of the locks
1513  break 2;
1514  }
1515  }
1516  }
1517 
1518  if (!$lock_success) {
1519  if ($not_required) {
1520  // next action, please
1521  continue;
1522  } else {
1523  $status = SQ_TRIG_RESULT_FAILURE;
1524  break;
1525  }
1526  }
1527 
1528  // execute action
1529  $action_success = call_user_func_array(Array($action['type'], 'execute'), Array($action['data'], &$state_data));
1530  }//end else
1531 
1532  if ($action_success === FALSE) {
1533  if ($not_required) {
1534  if (!$is_blocking) {
1535  trigger_localised_error('CORE0134', E_USER_NOTICE, $action['type']);
1536  }
1537  continue;
1538  } else {
1539  $status = SQ_TRIG_RESULT_FAILURE;
1540  if (!$is_blocking) {
1541  trigger_localised_error('CORE0135', E_USER_NOTICE, $action['type']);
1542  }
1543  break;
1544  }
1545  }
1546 
1547  $status = SQ_TRIG_RESULT_SUCCESS;
1548 
1549  }//end foreach
1550 
1551  // release the locks
1552  foreach ($locks_held as $lock_assetid => $lock_types) {
1553  foreach ($lock_types as $lock_type => $lock_true) {
1554  // can't do anything if locks aren't released, so don't take result
1555  $am->releaseLock($lock_assetid, $lock_type);
1556  }
1557  }
1558 
1559  return $status;
1560 
1561  }//end _executeTrigger()
1562 
1563 
1564 //-- TRIGGER CREATION --//
1565 
1566 
1573  function _getHashObject()
1574  {
1575  $hash = new Hash();
1576  return $hash;
1577 
1578  }//end _getHashObject()
1579 
1580 
1587  function _getEmptyTrigger()
1588  {
1589  $trigger['name'] = '';
1590  $trigger['description'] = '';
1591  $trigger['category'] = '';
1592  $trigger['data'] = NULL;
1593  $trigger['active'] = SQ_TRIG_STATUS_INACTIVE;
1594  $trigger['state_hash'] = $this->_getHashObject();
1595 
1596  return $trigger;
1597 
1598  }//end _getEmptyTrigger()
1599 
1600 
1611  function _saveTrigger($trigger)
1612  {//TODO TOF: write unit tests
1613  // triggers do not work in ROLLBACK mode
1614  if (SQ_ROLLBACK_VIEW) return TRUE;
1615  if (empty($trigger)) return FALSE;
1616 
1617  try {
1618  // start transaction
1619  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
1620  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
1621  $db = MatrixDAL::getDb();
1622  $status = TRUE;
1623  $new = FALSE;
1624 
1625  // lack of id means it is a new trigger
1626  // this bit gets all ids to come from the sequence, manual entries will make db inconsistent
1627  if (!isset($trigger['id'])) {
1628  $new = TRUE;
1629  $tr_id = MatrixDAL::executeOne('core', 'seqNextVal', Array('seqName' => 'sq_trig_id_seq'));
1630  $trigger['id'] = $tr_id;
1631  }
1632 
1633  if ($new) {
1634 
1635  $sql = 'INSERT INTO
1636  sq_trig
1637  (
1638  id,
1639  data,
1640  active,
1641  description,
1642  category,
1643  name
1644  )
1645  VALUES
1646  (
1647  :id,
1648  :data,
1649  :active,
1650  :description,
1651  :category,
1652  :name
1653  )';
1654 
1655  $prepared = MatrixDAL::preparePdoQuery($sql);
1656  MatrixDAL::bindValueToPdo($prepared, 'id', $trigger['id']);
1657  MatrixDAL::bindValueToPdo($prepared, 'data', serialize($trigger['data']));
1658  MatrixDAL::bindValueToPdo($prepared, 'active', array_get_index($trigger,'active', SQ_TRIG_STATUS_DEFAULT));
1659  MatrixDAL::bindValueToPdo($prepared, 'description', $trigger['description']);
1660  MatrixDAL::bindValueToPdo($prepared, 'category', $trigger['category']);
1661  MatrixDAL::bindValueToPdo($prepared, 'name', $trigger['name']);
1662  $result = MatrixDAL::execPdoQuery($prepared);
1663 
1664  } else {
1665 
1666  $sql = 'UPDATE
1667  sq_trig
1668  SET
1669  data = :data,
1670  active = :active,
1671  description = :description,
1672  category = :category,
1673  name = :name
1674  WHERE
1675  id = :id';
1676 
1677  $prepared = MatrixDAL::preparePdoQuery($sql);
1678  MatrixDAL::bindValueToPdo($prepared, 'id', $trigger['id']);
1679  MatrixDAL::bindValueToPdo($prepared, 'data', serialize($trigger['data']));
1680  MatrixDAL::bindValueToPdo($prepared, 'active', array_get_index($trigger,'active', SQ_TRIG_STATUS_DEFAULT));
1681  MatrixDAL::bindValueToPdo($prepared, 'description', $trigger['description']);
1682  MatrixDAL::bindValueToPdo($prepared, 'category', $trigger['category']);
1683  MatrixDAL::bindValueToPdo($prepared, 'name', $trigger['name']);
1684  $result = MatrixDAL::execPdoQuery($prepared);
1685  }
1686 
1687  $this->_deleteTriggerHash($trigger['id']);
1688 
1689  // prepare and save the hash
1690  // the edit interface can set the optional 'save_hash' index to false, which will cause
1691  // the hash not to be saved for this trigger, for whatever reason
1692  if ($status && array_get_index($trigger, 'save_hash', TRUE) && $trigger['active']) {
1693 
1694  $hash = $this->_getHashObject();
1695  $hash->setTriggerId($trigger['id']);
1696 
1697  // update the hash
1698  if (isset($trigger['data'])) {
1699  if (isset($trigger['data']['events'])) {
1700  foreach ($trigger['data']['events'] as $event) {
1701  $hash->setEvent($event);
1702  }
1703  }
1704  if (isset($trigger['data']['conditions'])) {
1705  foreach ($trigger['data']['conditions'] as $condition) {
1706  if ($GLOBALS['SQ_SYSTEM']->am->installed(array_get_index($condition, 'type', ''))) {
1707  if ($this->_loadComponent($condition['type'])) {
1708  if (isset($condition['data'])) {
1709  if (!$condition['inverse_condition']) {
1710  $settings =& $condition['data'];
1711  } else {
1712  $settings = Array();
1713  }
1714  call_user_func_array(Array($condition['type'], 'setHash'), Array(&$settings, &$hash));
1715  }
1716  }
1717  }
1718  }
1719  }
1720  }
1721 
1722  $status = $status && $this->_saveTriggerHash($hash);
1723  }
1724 
1725  // now try to save the trigger itself
1726  if ($status) {
1727  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
1728  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1729  return $trigger['id'];
1730  } else {
1731  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
1732  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1733  return FALSE;
1734  }
1735 
1736  } catch (Exception $e)
1737  {
1738  throw new Exception('Unable to save the trigger due to database error: '.$e->getMessage());
1739  }
1740 
1741  }//end _saveTrigger()
1742 
1743 
1752  function _saveTriggerHash($hash=NULL)
1753  {// TODO TOF TEST
1754  if (empty($hash)) return FALSE;
1755 
1756  $hash_combinations = $hash->getHashCombinations();
1757  if (empty($hash_combinations)) return FALSE;
1758 
1759  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
1760  $db = MatrixDAL::getDb();
1761  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
1762 
1763  foreach ($hash_combinations as $combination) {
1764 
1765  $field_array = Array();
1766  $value_array = Array();
1767 
1768  foreach ($combination as $hash_id => $hash_data) {
1769  $field_array[] = $hash_id;
1770  $value_array[] = '\''.($hash_data).'\'';
1771  }
1772 
1773  $sql = 'INSERT INTO
1774  sq_trig_hash
1775  (
1776  '.implode($field_array, ', ').'
1777  )
1778  VALUES
1779  (
1780  '.implode($value_array, ', ').'
1781  )';
1782 
1783  $result = NULL;
1784  try {
1785  $query = MatrixDAL::preparePdoQuery($sql);
1786  $result = MatrixDAL::execPdoQuery($query);
1787  } catch (Exception $e) {
1788  throw new Exception('could not create the trigger hash due to database error: '.$e->getMessage());
1789  }
1790 
1791  // check result
1792  if ($result === FALSE) {
1793  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
1794  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1795  return FALSE;
1796  }
1797 
1798  }//end foreach
1799 
1800  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
1801  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1802 
1803  return TRUE;
1804 
1805  }//end _saveTriggerHash()
1806 
1807 
1816  function _prepareRawStateHash($state_hash)
1817  {
1818  // if the state_hash is empty or not an array, make it an array so we
1819  // can set defaults later
1820  if (empty($state_hash) || !is_array($state_hash)) {
1821  $state_hash = Array();
1822  }
1823 
1824  // define the defaults
1825  $state_hash_defaults = Array(
1826  'event' => '',
1827  'assetid' => '',
1828  'asset_type' => '',
1829  'treeid' => '',
1830  'tree_propagate' => SQ_TRIG_TREE_NO_PROPAGATE,
1831  'triggerid' => '',
1832  );
1833 
1834  // make sure every field has data, and that no fields are null
1835  foreach ($state_hash_defaults as $defaults_id => $defaults_data) {
1836  if (empty($state_hash[$defaults_id])) {
1837  $state_hash[$defaults_id] = $defaults_data;
1838  }
1839  }
1840 
1841  return $state_hash;
1842 
1843  }//end _prepareRawStateHash()
1844 
1845 
1846 //-- TRIGGER DELETION --//
1847 
1848 
1857  function _deleteTrigger($trigger_id)
1858  {
1859  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
1860  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
1861 
1862  $result = NULL;
1863  try {
1864  $bind_vars = Array (
1865  'trigger_id' => $trigger_id,
1866  );
1867  $result = MatrixDAL::executeQuery('core', 'deleteTriggerId', $bind_vars);
1868  } catch (Exception $e) {
1869  throw new Exception('Unable delete the Trigger'.$trigger_id.': due to database error: '.$e->getMessage());
1870  }
1871 
1872  $success = $this->_deleteTriggerHash($trigger_id);
1873  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
1874  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1875 
1876  return $success;
1877 
1878  }//end _deleteTrigger()
1879 
1880 
1889  function _deleteTriggerHash($trigger_id)
1890  {// TODO TOF TEST THIS FUNCTION
1891  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
1892  try {
1893  $db = MatrixDAL::getDb();
1894  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
1895 
1896  $sql = 'DELETE FROM
1897  sq_trig_hash
1898  WHERE
1899  triggerid = :triggerid';
1900 
1901  $query = MatrixDAL::preparePdoQuery($sql);
1902  MatrixDAL::bindValueToPdo($query, 'triggerid', $trigger_id);
1903  $result = MatrixDAL::execPdoQuery($query);
1904 
1905  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
1906  } catch(Exception $e) {
1907  throw new Exception("Failed to delete the hash (#$trigger_id): ".$e->getMessage());
1908  }
1909  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1910 
1911  return TRUE;
1912 
1913  }//end _deleteTriggerHash()
1914 
1915 
1916 //-- GETTERS/SETTERS, LOAD FUNCTIONS --//
1917 
1918 
1928  function &getTriggerList()
1929  {// TODO TOF TEST THIS FUNCTION
1930 
1931  $trigger_db = MatrixDAL::executeAll('core', 'getTriggerList');
1932 
1933  if (empty($trigger_db)) {
1934  foreach ($trigger_db as $trigger_data) {
1935  $trigger_data['data'] = (isset($trigger_data['data']) && !empty($trigger_data['data'])) ? unserialize($trigger_data['data']) : '';
1936  }
1937  }
1938 
1939  return $trigger_db;
1940 
1941  }//end getTriggerList()
1942 
1943 
1984  function getTrigger($triggerid)
1985  {
1986  if (is_numeric($triggerid)) {
1987  return $this->_loadTrigger($triggerid);
1988  } else {
1989  return NULL;
1990  }
1991 
1992  }//end getTrigger()
1993 
1994 
2006  function createTrigger(&$trigger)
2007  {
2008  // check the structure of trigger
2009  // $s contains the status - if any part of $trigger fails the checking,
2010  // $s will stop further checks from happening
2011  $s = TRUE;
2012  $am =& $GLOBALS['SQ_SYSTEM']->am;
2013 
2014  // basic elements
2015  $s = $s && isset($trigger['name']);
2016  $s = $s && isset($trigger['description']);
2017  $s = $s && isset($trigger['active']);
2018  $s = $s && isset($trigger['data']);
2019 
2020  // events
2021  $s = $s && isset($trigger['data']['events']);
2022 
2023  // conditions
2024  $s = $s && isset($trigger['data']['conditions']);
2025  if ($s && !empty($trigger['data']['conditions'])) {
2026  foreach ($trigger['data']['conditions'] as $condition) {
2027  $s = $s && isset($condition['type']) && $am->installed($condition['type']);
2028  $s = $s && isset($condition['data']);
2029  $s = $s && isset($condition['inverse_condition']);
2030  }
2031  }
2032 
2033  // actions
2034  $s = $s && isset($trigger['data']['actions']);
2035  if ($s && !empty($trigger['data']['actions'])) {
2036  foreach ($trigger['data']['actions'] as $action) {
2037  $s = $s && isset($action['type']) && $am->installed($action['type']);
2038  $s = $s && isset($action['data']);
2039  $s = $s && isset($action['not_required']);
2040  $s = $s && isset($action['ignore_permissions']);
2041  }
2042  }
2043 
2044  if ($s) {
2045  return $this->_saveTrigger($trigger);
2046  } else {
2047  trigger_localised_error('CORE0232', E_USER_WARNING);
2048  return FALSE;
2049  }
2050 
2051  }//end createTrigger()
2052 
2053 
2062  function _loadTrigger($trigger_id=NULL)
2063  {// TODO TOF: test this function
2064  if (!isset($trigger_id)) return NULL;
2065  $trigger_db = NULL;
2066  try {
2067  $db = MatrixDAL::getDb();
2068 
2069  $select = 'SELECT
2070  id,
2071  name,
2072  data,
2073  description,
2074  active,
2075  category
2076  FROM
2077  '.SQ_TABLE_RUNNING_PREFIX.'trig';
2078 
2079  $where = 'id = :id';
2080  $where = $GLOBALS['SQ_SYSTEM']->constructRollbackWhereClause($where);
2081  $query = MatrixDAL::preparePdoQuery($select.' '.$where);
2082  MatrixDAL::bindValueToPdo($query, 'id', $trigger_id);
2083  $trigger_db = MatrixDAL::executePdoAll($query);
2084  if (!empty($trigger_db)) {
2085  $trigger_db = $trigger_db[0];
2086  }
2087  } catch (Exception $e) {
2088  throw new Exception('Unable get the information from the trigger table due to database error: '.$e->getMessage());
2089  }
2090 
2091  if (empty($trigger_db)) {
2092  // trigger_localised_error('CORE0133', E_USER_WARNING, $trigger_id);
2093  return NULL;
2094  }
2095 
2096  $trigger_db['data'] = (isset($trigger_db['data']) && !empty($trigger_db['data'])) ? unserialize($trigger_db['data']) : '';
2097 
2098  // set defaults if not present
2099  if (!isset($trigger_db['data']['events'])) {
2100  $trigger_db['data']['events'] = Array();
2101  }
2102  if (!isset($trigger_db['data']['conditions'])) {
2103  $trigger_db['data']['conditions'] = Array();
2104  }
2105  if (!isset($trigger_db['data']['actions'])) {
2106  $trigger_db['data']['actions'] = Array();
2107  }
2108  if (!isset($trigger_db['data']['settings'])) {
2109  $trigger_db['data']['settings'] = Array();
2110  }
2111 
2112  return $trigger_db;
2113 
2114  }//end _loadTrigger()
2115 
2116 
2124  {
2125  $db = MatrixDAL::getDb();
2126 
2127  try {
2128  $select = 'SELECT
2129  id,
2130  name,
2131  description,
2132  category,
2133  active
2134  FROM
2135  '.SQ_TABLE_RUNNING_PREFIX.'trig';
2136 
2137  $where = $GLOBALS['SQ_SYSTEM']->constructRollbackWhereClause();
2138  $order = 'ORDER BY active DESC , name ASC';
2139 
2140  $query = MatrixDAL::preparePdoQuery($select.' '.$where.' '.$order);
2141  $result = MatrixDAL::executePdoAll($query);
2142  } catch (Exception $e) {
2143  throw new Exception('Unable to get trigger info list due to the following database error:'.$e->getMessage());
2144  }//end try catch
2145 
2146  return $result;
2147 
2148  }//end _getTriggerInfoList()
2149 
2150 
2159  function _getComponentList($type=NULL)
2160  {
2161  if (empty($type)) return Array();
2162 
2163  $am =& $GLOBALS['SQ_SYSTEM']->am;
2164 
2165  $component_types = $am->getTypeDescendants($type);
2166  $component_info = $am->getTypeInfo($component_types, 'name');
2167 
2168  return $component_info;
2169 
2170  }//end _getComponentList()
2171 
2172 
2179  function _getEventList()
2180  {
2181  $event_list = $this->_getComponentList('trigger_event');
2182  asort($event_list);
2183 
2184  return $event_list;
2185 
2186  }//end _getEventList()
2187 
2188 
2196  {
2197  return $this->_getComponentList('trigger_condition');
2198 
2199  }//end _getConditionList()
2200 
2201 
2208  function _getActionList()
2209  {
2210  return $this->_getComponentList('trigger_action');
2211 
2212  }//end _getActionList()
2213 
2214 
2229  function _loadComponent($type_code)
2230  {
2231  $am =& $GLOBALS['SQ_SYSTEM']->am;
2232 
2233  if ($am->installed($type_code)) {
2234  $am->includeAsset($type_code);
2235  return TRUE;
2236  } else {
2237  return FALSE;
2238  }
2239 
2240  }//end _loadComponent()
2241 
2242 
2254  function _getComponentInterface($type_code, $data, $prefix, $write_access=FALSE, Trigger $trigger=NULL, $action_id)
2255  {
2256  if (!$this->_loadComponent($type_code)) return '';
2257 
2258  $interface = call_user_func_array(Array($type_code, 'getInterface'), Array($data, $prefix, $write_access, &$trigger, $action_id));
2259 
2260  return $interface;
2261 
2262  }//end _getComponentInterface()
2263 
2264 
2273  function _isMultipleConditionAllowed($type_code)
2274  {
2275  $this->_loadComponent($type_code);
2276  $status = call_user_func(Array($type_code, 'allowMultiple'));
2277  return $status;
2278 
2279  }//end _isMultipleConditionAllowed()
2280 
2281 
2290  function _isEventListenedFor($event)
2291  {// TODO TOF TEST THIS
2292  // Check the trigger hash table to see whether any active triggers are listening for this event
2293  $trigger_count = NULL;
2294  try {
2295  $bind_vars = Array (
2296  'event' => $event,
2297  );
2298  $trigger_count = MatrixDAL::executeOne('core', 'getNbOfTriggerHash', $bind_vars);
2299  } catch (Exception $e) {
2300  throw new Exception('Unable get the number of trigger hashes: due to database error: '.$e->getMessage());
2301  }
2302 
2303 
2304  return !empty($trigger_count);
2305 
2306  }//end _isEventListenedFor()
2307 
2308 
2309 }//end class
2310 
2311 
2312 ?>