SugarCRM Logic Hook Order of Execution

When you have more than one logic hook, several local logic hooks, and several global logic hooks, you wonder in what order will all these logic hooks fire.

This is something I haven't come across in the SugarCRM documentation, so its worth an article here.  The order of firing turns out to be important, because you can run into conflicts between the logic hooks creating loops and bugs that may be difficult to find.  Let's take it a step at a time and start with local logic hooks.

Here's an example of having multiple local logic hooks, in this case located in "custom/modules/Accounts/logic_hooks.php."

logic_hooks.php

logic_hooks.php

In this logic_hooks.php file we find two before_save, two after_retrieve, and one process_record logic hook.  Which of these three events fires first is controlled by the event itself.  The before_save event fires right before you save a record to the database, the after_retrieve event fires after a record is returned from the database, and the process_record event fires when a record is being processes as part of a list view or subpanel.

In this case, five different pieces of custom code are programed to fire when one of those events is triggered by user interaction.  The interaction of the user with the system controls when a particular event fires.  The developer has control over which event to use with his code, and so the developer controls when each type of event fires, but what about the two before_save or two after_retrieve hooks, which fires first?

The developer controls this with the number in the array.  In the before_save hooks, in the array values, you'll see a number as the first entry in the array, this controls the order of firing for the two before_save hooks.  In this case, first (1) the "Updates Email Addrs" will fire and then (2) the "Logic Hook - Accounts Before Save." hook.

If you have global logic hooks, located in "custom/modules/logic_hooks.php," the logic_hooks.php file also may have multiple logic hooks.  These are set up the same way the local logic hooks are set up as described above.  So what's the problem?

Well, the question arises, if I have two local before_save logic hooks and two global before_save logic hooks, which one fires first?  Do you alternate between the number one's, and then go to the number two's?  This is important if the custom code in one hook may alter, or change, the result produced by running the custom code in another logic hook.

To answer these questions, I created a series of test logic hooks.  I created three local before_save logic hooks, numbered 1, 2, and 3; and three global before_save logic hooks, numbered 1, 2, and 3.  I took all the other logic hooks out of the file so there would be no conflict with different types of events.  I kept the numbers the same in the local and global file, so there would be no confusion about the numbers themselves controlling the overall execution, for example, 4, 5, and 6.

If the numbers alone controlled the sequence of events, then the developer would have to keep track of the number they were on for each event throughout the system, not good.  The only thing I did with the code itself was output a log entry saying what the hook was, whether it was local or global, and what number was firing.  See my article on "The SugarCRM Error Log," if you want to know how to set the error log output up.

Here are log entry results:

06/20/13 16:00:33 [3488][57faf14d-9e12-11e2-b875-d4bed953f95d][FATAL] This is the before_save local_hook #1 for Contact.

06/20/13 16:00:33 [3488][57faf14d-9e12-11e2-b875-d4bed953f95d][FATAL] This is the before_save local_hook #2 for Contact.

06/20/13 16:00:33 [3488][57faf14d-9e12-11e2-b875-d4bed953f95d][FATAL] This is the before_save local_hook #3 for Contact.

06/20/13 16:00:33 [3488][57faf14d-9e12-11e2-b875-d4bed953f95d][FATAL] This is the global before_save hook #1.

06/20/13 16:00:33 [3488][57faf14d-9e12-11e2-b875-d4bed953f95d][FATAL] This is the global before_save hook #2.

06/20/13 16:00:33 [3488][57faf14d-9e12-11e2-b875-d4bed953f95d][FATAL] This is the global before_save hook #3.

So take away number one is that all the local logic hooks fire first, and they fire in number order, and then the global logic hooks fire in number order.

As long as I had this experiment set up, I decided to mess around with it a little more. I wanted to double check that the hook numbers themselves controlled the execution order within the local, or global logic hook, and not the hooks placement in the logic_hooks file.  I switched the numbers, between the 1, and 2, in both the local and global hooks to see if the position listed in the logic hook file mattered, or indeed the numbers controlled the sequence.  I altered the log message to show what "should" happen, if the numbers were what controlled the sequence.

And I did one more thing.  I changed the last two of the before_save hooks to after_save hooks to show how the events themselves determine the sequence.

Here's the result:

06/20/13 15:52:44 [3488][57faf14d-9e12-11e2-b875-d4bed953f95d][FATAL] This is the before_save local_hook #2 in Contact module.  Should fire 1st.

06/20/13 15:52:44 [3488][57faf14d-9e12-11e2-b875-d4bed953f95d][FATAL] This is the before_save local_hook #1 in Contact module,  Should fire 2nd.

06/20/13 15:52:44 [3488][57faf14d-9e12-11e2-b875-d4bed953f95d][FATAL] This is the global before_save hook #3. Should fire 1st.

06/20/13 15:52:44 [3488][57faf14d-9e12-11e2-b875-d4bed953f95d][FATAL] This is the global before_save hook #2. Should fire 2nd.

06/20/13 15:52:44 [3488][57faf14d-9e12-11e2-b875-d4bed953f95d][FATAL] This is the local after_save hook #3 in Contact module.

06/20/13 15:52:44 [3488][57faf14d-9e12-11e2-b875-d4bed953f95d][FATAL] This is the global after_save hook #1. Fires last because after_save global.

These results show that the numbers do indeed control the sequence of a particular event, and not their place in the logic hook file, and  for a particular event the local fires then the global hook.

 

Posted in SugarCRM | Leave a comment

SugarCRM Logic Hook Events

This is a list of the events you can use with SugarCRM logic hooks broken into groups with tips on how to use them.  I reviewed how to write logic hooks in my last post.  Use this page as a quick reference page for future logic hooks you may write.

Application Hooks

Application hooks are global hooks used to execute logic when working with the global application.  Application hooks can not use the $bean object.  They are mostly used for logging, or loading libraries.  Application hooks should not be used with any front-end display logic.

after_entry_point  --  Executes at the start of every request, at the top of the page.
after_ui_footer      --  Executes after the footer has been invoked.
after_ui_frame      --  Executes after the frame has been invoked and before the footer has been invoked.
server_round_trip  --  Executes at the end of every page.

Module Hooks

Module hooks are specifically used when working with modules.  I find these events are used the most.  A majority of my logic hooks are either a before_save, or after_save hook.  The $bean ORM object can be used with all these hooks but you have to be careful of infinite looping.   If you have to access any data in the database in your code, then these are the hooks you want to use.

before_delete                --  Fires before a record is deleted
before_relationship_add      --  Fires before a relationship is added between two records
before_relationship_delete  --  Fires before a relationship is deleted between two records
before_restore              --  Fires before a record is undeleted
before_save                   --  Fires before a record is saved
You have to be careful using a $bean->save when you use this hook, you could end up with an error, because of an infinite loop.  Having said that, between the before_save and after_save event, I do my $bean->save in the before_save event, and never do it in an after_save event.  I found you can use a $bean->save in the before_save hook without putting it in a loop, if your careful, but be aware of this when using the hook to save data back to the database.

after_delete                    --  Fires after a record is deleted
after_relationship_add       --  Fires after a relationship is added between two records
after_relationship_delete    --  Fires after a relationship is deleted between two records
after_restore                --  Fires after a record is undeleted
after_retrieve               --  Fires after a record has been retrieved from the database.
This hook does not fire when you create a new record.

after_save                   --  Fires after a record is saved.
You can NOT use a $bean->save when you use this hook, or you'll end up with an error, because of an infinite loop.   Why?  Well after you save to the database in the after_save hook,  the after_save hook fires again, saving to the database, and round, and round we go.  This is a gotcha you have to work around when writing an after_save hook, my solution is to use a before_save hook when saving to the database.

handle_exception           --  Fires when an exception is thrown
process_record               --  Fires when the record being processed is part of a list view,  or a subpanel list

User Hooks

These logic hooks are used when working with the Users module, and when you want an event fired around log in and log out.

after_load_user  -- Fires when current user is set for the current request.
Used for events that are dependent on the current user, such as those used with the  ACL modules.

login_failed        --  Fires on a failed log in attempt
after_login          --  Fires after a user logs into the system
before_logout     --  Fires before a user logs out of the system
after_logout        --  Fires after a user logs out of the system

Job Queue Hooks

These logic hooks are used with the Job Queue module.

 job_failure        --  Executes when a jobs final failure occurs.
 job_failure_retry  --  Executes any time a job fails before its final failure.
Use job_failure if you only want action on a final failure.

SNIP Hooks

These logic hooks are used with emails and should only be used with global logic hooks.

after_email_import  --  Fires after a SNIP email has been imported
before_email_import --  Fires before a SNIP email has been imported

I want to point out that the folks at Sugar are gradually over time adding new events from user requests.  This is one area where SugarCRM does a good job of documentation.  Most of this post is from their documentation, I just wanted to put it in an easier to read format, and add a few tips.  The actual documentation is here:

http://support.sugarcrm.com/02_Documentation/04_Sugar_Developer/Sugar_Developer_Guide_6.6/03_Module_Framework/Logic_Hooks

Posted in SugarCRM | Leave a comment

SugarCRM Logic Hooks

Logic hooks are developer's bread and butter for custom coding in SugarCRM.  Logic hooks allow you to alter the functionality and behavior of Sugar to meet  customer requirements.  Unfortunately, there is too much to cover to completely cover logic hooks in one article, so we'll do this with a couple of articles.  With this article I want to show you how to create a logic hook.

Logic hooks are normally created for a particular module.  You want to alter a particular modules behavior.  It is also possible to create a global logic hook that will fire across all modules.  To create a logic hook in Sugar you need two files.  The first is always called logic_hooks.php and the second can be called anything you'd like with some restrictions.

These files are located in custom/modules/[the module your working with], for example, for a logic hook for the Accounts module, you would place the two files in the "custom/modules/Accounts" folder.  If you want to have a global logic hook that fires across the application when an event happens, then you would place the two files in the "custom/modules" folder.

Let's start with the logic_hooks.php file.  The purpose of the logic_hook file is to fire your custom code when a particular event happens in Sugar.  An event is a specific dynamic change in Sugar, caused by a user or system interaction, for example, a record is saved, or a page is loaded.  There is a long list of events that can be used to trigger a logic hook, which then triggers your custom code, which I'll cover in my next article.  Let's see what a typical logic_hooks.php file looks like:

logic_hooks.php

logic_hooks.php

This particular logic_hooks file contains two "before_save" hooks, two "after_retrieve" hooks, and a "process_record" hook.   More on these in my next article.

You're creating an array of information for the particular event used to fire your custom code.  The array for your event is initialized with this line:  $hook_array = Array();.  Each event has its own array.   In the picture, a "before_save" event is started with: $hook_array['before_save'] =

The order of execution for a "before_save" event is, when a record is saved in the front-end of the application by a user clicking on a record's save button, right before that record is saved to the database, the logic hook fires, and runs your custom code, after your custom code runs, the record will be saved to the database.  Just so you know, there's also an "after_save" event which fires right after the record has been saved to the database.

Let's concentrate on one hook, the "before_save" hook for an explanation of the array components.  Here it is again:

logic_hook_one650

Reading from the left, this is a "before_save" hook, in the brackets you enter the event you want to use to fire your custom code.  Please note the blank square brackets [] after the event brackets, they need to be there.  Inside the array, you first see the number 1, this is used to determine the order when many "before_save" hooks need to fire.  In the first picture, you see a "before_save" 1 and 2.

Next we have a comment, 'update Email Addr', this is put in the the array for a short description of the hook.  When you have a bunch of logic hooks in the file as in the first picture, it makes it easy to pick out the logic hook your looking for in the logic_hooks file.

The next three entries in the array is the heart of the hook.  The next section, 'custom/modules/Accounts/updateEmailAddr.php', tells Sugar the exact path and file name where your custom code resides, that you want to fire with this hook.  This is the name of the second file you need for the hook.

The next entry is the name of the class of your custom code.  It has a restriction.  Your class has to have the same name as your file name.  The "updateEmailAddr.php" file should have the class called "UpdateEmailAddr."    The class name in the logic_hooks file  directs Sugar to your class.

The last entry, 'updateStoredEmailAddr', is the name of the method inside your custom class that will be fired when the hook fires.

Let's take a look at the second file you'll need, the "updateEmailAddr.php" file:

updateEmailAddr.php

updateEmailAddr.php

Alright, here's the custom code for the hook.  This particular hook fires before the record is saved to the database, checks to see if there's been a change to a type of user's email address field to be saved, and if there has been a change, it will look up what it should be and replace whatever was entered in the field with the primary email assigned to the account.  In this case, that email address is not allowed to be changed without certain procedures being followed, so this, in essence, is a validation hook.

Let's talk about the code.  You create a class to contain your code, called, you guessed it "updateEmailAddr," that you put in the logic hooks file.  And the method that runs when the hook fires is: "updateStoredEmailAddr" just like you described in the logic hooks file.

The file should always start out with:  if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');  This prevents unauthorized access the file, and is seen at the start in most of the Sugar files.

The method "updateStoredEmailAddr(&$bean, $event, $arguments)" usually contains the parameters "&$bean", "$event", and "$arguments".  This passes the database ORM Sugar $bean into the method by reference, so you can use it in your method.  The $event is "before_save," or the event that fires the hook.  The $arguments can be an array of parameters that are specific to the event you'd like to send into the method.  Most of the time the last two parameters are not used in the method.

Inside the method, "updateStoredEmailAddr," you'll notice like other methods you can include other code from other files.  In this case, the included file  contains the method, getPrimaryEmail().

The return at the end of the class returns back to Sugar which then proceeds to save the record to the database.  Next time, I'll go over the various events you can use to fire your custom code.

Posted in SugarCRM | Leave a comment

The SugarCRM Error Log

Sugar has a robust error tracking system.  It is one of the best tools that a developer can use to debug their code.  If you're writing custom code in Sugar it behooves you to understand how to use the Sugar error log to your benefit.  Sugar records a wealth of information to the error log, some think too much, but you can control that.  Let's take a closer look.

The Sugar error log is a file located in the top Sugar directory level called sugarcrm.log.    This is a text file you can bring up and look at in any text editor, like Notepad++.  The long hyphenated number in each log record is your user_id in Sugar in case more than one user is using the system, and thus will also be creating log entries.  Here's what the sugarcrm.log entries look like:

If you go to the back-end admin panel in "Admin->System Settings" and scan down the page, you'll find a subpanel at the bottom called "Logger Settings."  Here you can change the actual name of the the file and extension, the maximum size of the file, whether you want a date attached to the file name, and the log level, which I'll talk about below.   I would recommend you keep the default settings and leave it set up as is, which means your Sugar errors will go to the sugarcrm.log, and when that exceeds the default file size of 10MB, it will change that file name to sugarcrm_1.log, then sugarcrm_2.log, etc., and start a new sugarcrm.log.  This is handy, since all you need to be concerned with is one file, the most current log, the sugarcrm.log file.

Let's go a little beyond the admin panel.  The code controlling the Sugar log is located in include/SugarLogger.  The code that runs the logger and determines what is output is in SugarLogger.php.  I don't recommend you mess with this file unless you really know what your doing, but you can configure the format of what is outputted here.  For example, if you were the only one developing your system, you may want to take the user_id out of a log record.   There is a better way to control the amount of information outputted.  Let's take a look at the include/SugarLogger/LoggerManager.php file.

LoggerManager.php

LoggerManager.php

The $_level variable in the file is the Log Level set in Admin->System Settings->Log Level.   The array of log level mappings in LoggerManager.php correspond to the Log Level dropdown menu in the admin panel.  Log Level is important.  Sugar is very verbose in the amount of entries it makes to the sugarcrm.log.  You want to limit the entries in the log, because it can get very long, and you'll have trouble finding the entries you want to stand out in the mass of entries that are outputted.

To control the amount of entries in the log file, you can set the Log Level to "Fatal," for example, then only errors that are marked "Fatal" and lower in the array's number hierarchy are visible.  In the case of a "fatal" log level only errors marked fatal, security, special, dale, and off would show in the sugarcrm.log.  By changing the Log Level, you can control what is logged to the log.

Here's what the Log Level events mean in Sugar:

Debug : Logs events that help in debugging the application.
Info : Logs informational messages and database queries.
Warn : Logs potentially harmful events.
Error : Logs error events in the application.
Fatal : Logs severe error events that leads the application to abort. This is the default level.
Security : Logs events that may compromise the security of the application.

The reason I showed you the LoggerManager.php file is to show you that you can add your own special labels to the log levels dropdown by adding them to this array.  In the picture, I added the "special" and "dale" levels to use in my debugging which are not in the default package.  The advantage of this is you don't have to go back and change the log level when you want to cut down you log output to see your debugging output more easily.   Bear in mind, if you put your own log levels in the array in the LoggerManager.php file, that this is a core file, and your entries may be removed when you upgrade Sugar.

 

How do you create log entries in the sugarcrm.log file?

Most of the log entries are already embedded in the Sugar core code, which outputs error messages depending on the Log Level, as we just discussed.  However, the log can be made much more functional by creating your own log entries anywhere in the Sugar code, and I do mean anywhere, by adding a line to the code like this:

STRINGS

$GLOBALS['log']->special("Output this string to tell me where I am at in my code");

This will output the string, "Output this string to tell me where I am at in my code," right after [SPECIAL] in the sugarcrm.log record for this event. (See the first picture.)  In this example, you can substitute any of the Log Levels for special when you put this in your code.  That's it.

Let's look at what else you can output to the sugarcrm.log file.

VARIABLES
Are outputted, like so:

$GLOBALS['log']->dale("The accountid is: $accountid");

ARRAYS
Are outputted, like so:

$GLOBALS['log']->debug("The _POST from the detail view is: " . print_r($_POST, true));

 

In addition to variable and arrays, I've found it useful to output the PHP method with the log entry, like so:

$GLOBALS['log']->fatal("[teamAssignBefore] bean->modulename:  $bean->module_name");

Another useful trick when writing new code is to number the output from a series of log entries, so you can tell your progress through the code and whether you entered a conditional or not.  The numbers make it easy to follow your progress through the code, like so.

$GLOBALS['log']->dale("10 The accountid is: $accountid");
$GLOBALS['log']->dale("20 The account name is: $accountname");
$GLOBALS['log']->dale("30 The account city is: $accountcity");

If you have a conditional you can change the number endings, like so:

$GLOBALS['log']->dale("10 The accountid is: $accountid");
$GLOBALS['log']->dale("20 The account name is: $accountname");
$GLOBALS['log']->fatal("25 There is no account name in the record: $accountname");
$GLOBALS['log']->fatal("35 There is no account city in the record: $accountcity");
$GLOBALS['log']->dale("30 The account city is: $accountcity");

There is one other error file you should be aware of is the php_error.log.  This log will output any PHP errors in your code.  Most of the time this will not have to be looked at unless, of course, you started getting those glaring orange PHP error boxes in your output.

When I'm coding in Sugar, I have my NetbeansIDE open on one monitor, where I write my code, and on my other monitor, I have the sugarcrm.log file open.  When I run my code, I immediately see my variables and arrays in the log, and thus immediately know what's happening in my code, and whether I'm getting the data I expect.  Happy coding!

Posted in SugarCRM | Leave a comment

SugarCRM – Studio

studio250

Studio's Module Icons

I left off last time talking about Studio.  Studio is a developer's graphical tool to customize Sugar.  The folks at SugarCRM decided that they had different skill levels of developers working with Sugar that needed to customize the Sugar interface so they designed a graphical tool to do just that.

Sugar comes with a bunch of developers tools, each with a singular functionality.  There are tools to rename modules, load modules, create workflows, create dropdown menus, customize the visibility of a module or subpanel, and of course, Studio.  Each of these tools are located in the back-end Admin panel and most are self explanatory and easy to understand.

There is another development tool, Module Builder, for developers who want to develop their own module that we'll review sometime in the future.  Unfortunately, Studio and Module Builder were built by two different developers, and they do not work well together, but more on that in the future.  Let's concentrate on Studio today, a tool to modify Sugar from its out-of-the-box look to something useful for a particular application.

Studio is a visual tool the modifies Sugar modules.  Changes in Studio change the module code by creating files in the custom/modules/[modulename]  directory.  The custom directory allows users to change the Sugar code without modifying the core Sugar files.  That way if SugarCRM came out with an update to Sugar, it will not overwrite your modifications when you upgrade.

Since Studio modifies modules, when you first open Studio you'll see graphical icons of all the modules currently installed.  To modify a module, you click on the module you want to modify.  You're then presented with a new window with icons representing different ways to modify a module: Labels, Fields, Relationships, Layouts, Subpanels, and Mobile Layouts.

To simplify this discussion, I'm going to use the "Accounts" module, as the example module thorough out this article.  You can substitute any other module name for "Accounts" any time I mention "accounts" in this article.

Changing Field Labels

Changing Field Labels

The first tool in Studio, Labels, is for modifying the field names that appear in the various front-end views.  If you want to call a field, "Full Name", instead of "Last Name," then this is where you would change the name.  Sugar conveniently starts all its names with variables starting with "LBL_"  When you change a label name, it changes the corresponding name in the array in the language file for that module.  Language files are in a bunch of places in Sugar: custom/modules/accounts/language, custom/include/language, custom/Extension/modules/Accounts/Ext/language, custom/Extension/application/Ext/Language, custom/application/Ext/Language, modules/Accounts/language, and include/language.  Most of them, but not all, for English have the file name en_us.lang.php.  One of the problems for developers is trying to determine which language file has the field definition you need to change.  Because of the mix of language files, using Studio solves a lot of the problems of which language file to change.

Sugar Data Types

Sugar Data Types

The next module tool in Studio is Fields.  Fields allow you to create new fields and customize existing fields in the module.  If I wanted to add a way to track a special serial number for a record in the database, I would click on "Add Field." This would open the Add Field interface and the first thing you tell Sugar is what type of data you want to store. You can see you have a wealth of data choices in the picture.  Each data type brings up a different form of menu choices about that type of data.  You can tell Sugar what field name you want to use in the database, the label you want that field to have in forms, the default value, and whether it's a required filed the user has to enter before they can save the record.  For example, if you select Dropdown as the data type, you can edit the choices in the dropdown list.  In a new feature, you can link multiple dropdown lists, so if you select a certain dropdown value in a parent dropdown list, only select choices will come up in the child dropdown list.  This can be done graphically in Studio.  When you save the information about the new field, Studio creates a new field in the accounts_cstm table in the database where it stores the data.

Next, we have Relationships.  Studio allows you to create relationships between accounts and other modules. These can be one-to-one, one-to-many, and many-to-many relationships.  If I want to link accounts with contacts, I could set this up by clicking "Add Relationship."  Setting relationships up in Studio does several things.  When you create a new relationship, Sugar will create a new join table in the database.  In this example, that table would be called, accounts_contacts.  Sugar will create a subpanel in the account detail view in the front-end of the application, which will would show the contacts for that account.  Finally, Sugar creates a Sugar Bean for that relationship.  If you remember from my previous article, the Sugar Bean is the equivalent of a PHP ORM for easier coding in PHP.

Studio Layouts

Studio Layouts

Layouts are about what the user sees when they access the front-end of the application.  Just because I created a new field in "Fields" doesn't mean I can see that field in the front of the application.  The "Fields" tool creates the database field to store the data and has nothing to do with being able to see that data in the views.  Layouts configures what fields are visible for each view, and where those fields are placed in the view.  If you click on layouts in Studio, you'll see icons for each view type: EditView, DetailView, ListView, QuickCreate, SugarDashlet, PopupView, and Search.  If you click on any of these icons, the layout for that view or subview is configurable via drag and drop with your mouse.  You click on the module field in the left column and drag to an open row in the main area.  You determine what fields are visible in the front-end by placing them in the main layout area.  You can add new rows or new panels, which comes in below the first panel in the view.  Each view has a left and right column.  You can have a field use both the left and right column, or one side by clicking on the edit pencil icon.  You can rearrange the order of the fields by dragging and dropping them in the place you want them.  When you save your layout, it is stored in an array in a view file in the custom/modules/Accounts/metadata directory.

Subpanels are created when you create a relationship.  They show up below the main view.  They are configured the same way you configure the layouts.  If you have subpanels they are stored in the custom/modules/Accounts/metadata/subpanels directory.

Last we have Mobile Layouts.  These are layouts for your mobile devices and they are configured just like your other layouts.

As you can see, Studio allows you to configure each module in your application.  Modules are normally called from the home page as a menu choice, which then goes to the module/Accounts/account.php file which extend to the core Sugar code, sets up the module, and brings up the module list view and menus to go to the other views.  Studio is helpful to developers in configuring and getting your application just the way you want it.

This ends my articles on the overall view of Sugar from the 50,000 foot level.  I'm going to start digging into the code, and the many quirks of Sugar in future articles.  Before I do that, I want to talk about the Sugar error log, which I'll write about in my next article.

Posted in SugarCRM | Leave a comment