Robert Caverly

Control execution order for Sugar Feed logic hooks

Discussion created by Robert Caverly on Apr 26, 2016

SugarFeed messages (the ones that appear in the Home page Activity Stream dashlet) are really just a "before save" logic hook installed (or uninstalled) by the Admin->Activity Stream page. There are two problems with this as it is implemented in SugarCRM 6.5.

  • The hook(s) for SugarFeed are inserted with an execution order of '1'. This is hardwired in to the code that installs/removes the hook entry in the logic_hooks.php file
  • The uninstall code is defined in such a way that if you override the order (by manually editing logic_hooks.php or via the logic hooks array in a manifest file) the hook can never be removed or disabled by the Admin function to disable alerts for a specific module.

The below logic hook implementation provides a way to overcome these limitations.

 1   <?php
2   if (!defined('sugarEntry') || !sugarEntry)
3       die('Not A Valid Entry Point');
5   require_once('modules/SugarFeed/feedLogicBase.php');
7   class my_moduleFeed extends FeedLogicBase
8   {
9       var $module = 'my_module'; 
11      var $hook_label = 'My Module Activity Alerts'; 
12      var $hook_order = 9; 
14      function __construct() { 
15          if (empty($this->hook_label)) { 
16              $this->hook_label = $this->module.' push feed'; 
17          } 
18      } 
20      function installHook($file, $className){ 
21          check_logic_hook_file($this->module, "before_save", array($this->hook_order, $this->hook_label,  $file, $className, "pushFeed")); 
22      } 
24      function removeHook($file, $className){ 
25          require_once('include/utils/logic_utils.php'); 
26          $hook_array = get_hook_array($this->module); 
28          if(isset($hook_array['before_save'])){ 
29              foreach($hook_array['before_save'] as $action){ 
30                  // remove the logic hook if the hook label OR the class and method match 
31                  if ( $action[1] == $this->hook_label  
32                  || ($action[3] == $className && $action[4] == 'pushFeed') ) { 
33                      remove_logic_hook($this->module, "before_save", $action); 
34                  } 
35              } 
36          } 
37      }             
39      public function pushFeed($bean, $event, $arguments) 
40      { 
41          // implement module specific alert logic here 
42          ...  
43      }
45  }

The above code is a sample SugarFeed logic hook file that would be placed in the directory /custom/modules/lam_solvency/SugarFeeds. These files always inherit from the FeedLogicBase class, which only implements two methods: installHook() and removeHook(). As indicated before these methods are hardcoded for most parameters. By overriding these methods in the sub class lam_solvencyFeed we can customize how these hooks are installed and uninstalled.

Customizing the Hook Install

The main thing we want to be able to control upon installation is the order in which the SugarFeed hook will fire. Consider the case in which you have one or more before save logic hooks which may not only validate data, but also update bean values based on some business logic.  Since the default behavior is for SugarFeed hooks to be install with an order value of 1 (the first element of the action array) there is no guarantee that your business logic will have been executed BEFORE alerts were generated. This could result in inappropriate values displaying or incorrect alerts being generated.

The first change is to store the desired order value in a class variable [line 12]. This value is then substituted in the installHook() function at line 21. This value is hard coded to 1 in the default implementation.

The second change is to alter the default "label" from "[module name] push feed" to the custom string on line 11. Again this is used on line 21 in the call to check_logic_hook_file().

For now we are leaving the method name unchanged from the default - though this could probably be updated as well. The $file and $className values are passed in and are actually computed in the SugarFeed class where the call to installHook() is made.

Customizing the Hook Removal

The changes to the hook removal are a little more complex! By default the removeHook() function will only remove a logic hook action array from the logic_hooks.php file if ALL of the elements in the action array match (except the file name - not sure why it was decided this element could be different).  The key to changing this is that we first get the array of logic hooks for this module [line 26], and then iterate over this array looking for a 'match' based on our own rules. The checks at lines 31-32 will call the remove_logic_hook() if either the hook label matches or the class name and method name match. It does not check the order value at all, which allows us to deploy the Sugar feed file and properly update it's entry in the logic hook file without doing anything other than going to the Admin->Activity Stream page and turning the corresponding modules activity stream off and then on again, by unchecking the appropriate item, saving and then rechecking it and saving again. The key to this working is that when we call the remove function we must pass the currently defined action array so that it is a complete match and the remove function will work as expected.

Final Thoughts

The new constructor is provide to insure that the hook label has a valid value (mimics the value in the default implementation) in case it is not explicitly initialized. We could/should also insure that the hook order is initialized as well.  Additional customization of the Sugar feed hook installation could also be done such as having multiple feed methods and installing/removing multiple hooks if needed. Though I can't think of why you might want to do so - you could even include conditional logic as to which hook gets installed, etc.

Refer to the following files to more fully understand how this customization works:

  • include/utils.php
  • modules/SugarFeed/feedLogicBase.php
  • modules/SugarFeed/SugarFeed.php - activateModuleFeed(), disableModuleFeed()