Adding a custom search filter to the ListView

I was given a task that involved adding a filter to the ListView of an Events module so that (by default) only future events are displayed….but with some way of listing them all.

Two obvious solutions came to mind:

    • filter on date and have a check-box ticked on basic search that says “Display future events”
    • filter on event status (planning or confirmed)

I preferred the first option because the second involves two possible status codes..

Step 1:

The first thing I had to was add a custom non-db field to the Events module:

custom/Extension/modules/Events/Ext/Vardefs/future_only.php

$dictionary['Events']['fields']['open_only_future_events'] = array(
'name' => 'open_only_future_events',
'type' => 'bool',
'source'=> 'non-db',
'studio' => 'visible',
'query_type' => 'default',
'vname'=> 'LBL_EVENT_FUTURE_ONLY',
);

Then I added the field to the basic search layout in the Events module (this can also be done via studio):

array (
'layout' =>
array (
'basic_search' =>
array (
'name' =>
array (
'name' => 'name',
'label' => 'LBL_NAME',
'default' => true,
'width' => '10%',
),
'open_only_future_events' =>
array (
'type' => 'bool',
'studio' => 'visible',
'label' => 'LBL_EVENT_FUTURE_ONLY',
'width' => '10%',
'name' => 'open_only_future_events',
),
'current_user_only' =>
array (
'name' => 'current_user_only',
'label' => 'LBL_CURRENT_USER_FILTER',
'type' => 'bool',
'default' => true,
'width' => '10%',
),
),

Normally when you add a check-box field to the basic search it displays it as a drop-down rather than a check-box. This was an annoying issue as I wanted a check-box to be displayed. You will notice that the My Items check-box is displayed correctly in the basic search layout so there must be a hard coded check somewhere that looks for current_user_only or Open Items and makes them stay a check-box.

After a bit of searching I found the code in include/SugarFields/Fields/Bool/SugarFieldBool.php line 55. It appears that all I had to do was pre-pend my field name with open_only and it would remain a check-box rather than a drop-down field.

Don’t forget to at the label to the language file in: custom/Extension/modules/Events/Ext/Language/en_us.Events.php

$mod_strings ["LBL_EVENT_FUTURE_ONLY"] = "Display future events";

Step 2:

Now that the check-box is displaying correctly in the basic search layout at the top of the Events ListView I had to actually make the search filter work by only displaying upcoming future events in the ListView when it was checked. After a little research and  a lot of pouring over the code this turned out to be simpler than I thought.

All I had to do was add a simple subquery to the SearchFields.php file like so:

custom/modules/Events/metadata/SearchFields.php

$searchFields['Events'] = array (
'name' =>
array (
'query_type' => 'default',
),
'current_user_only' =>
array (
'query_type' => 'default',
'db_field' =>
array (
0 => 'assigned_user_id',
),
'my_items' => true,
'vname' => 'LBL_CURRENT_USER_FILTER',
'type' => 'bool',
),
'open_only_future_events' =>
array (
'query_type' => 'format',
'operator' => 'subquery',
'subquery' => 'SELECT events.id FROM events LEFT JOIN events_cstm ON events.id = events_cstm.id_c WHERE event_date_time_c > NOW() OR NOT {0}',
'db_field' =>
array (
0 => 'id',
),
'type' => 'bool',
),
'assigned_user_id' =>
array (
'query_type' => 'default',
),
'range_date_entered' =>
array (
'query_type' => 'default',
'enable_range_search' => true,
'is_date_field' => true,
),
'start_range_date_entered' =>
array (
'query_type' => 'default',
'enable_range_search' => true,
'is_date_field' => true,
),
'end_range_date_entered' =>
array (
'query_type' => 'default',
'enable_range_search' => true,
'is_date_field' => true,
),
'range_date_modified' =>
array (
'query_type' => 'default',
'enable_range_search' => true,
'is_date_field' => true,
),
'start_range_date_modified' =>
array (
'query_type' => 'default',
'enable_range_search' => true,
'is_date_field' => true,
),
'end_range_date_modified' =>
array (
'query_type' => 'default',
'enable_range_search' => true,
'is_date_field' => true,
),
);

The main part of the above code to look at is :

'open_only_future_events' =>
array (
'query_type' => 'format',
'operator' => 'subquery',
'subquery' => 'SELECT events.id FROM events LEFT JOIN events_cstm ON events.id = events_cstm.id_c WHERE event_date_time_c > NOW() OR NOT {0}',
'db_field' =>
array (
0 => 'id',
),
'type' => 'bool',
),

By setting the query_type to format and adding OR NOT {0} to the end of the query you are telling the filter to use the open_only_future_events check-box value in the {} and to ignore it if its {0} (not checked).

That’s pretty much all that was involved in getting this working however it took quite a bit of effort to figure this out hence the post.

Posted in SugarCRM development and customisation, SugarCRM development development hints and tips, SugarCRM development tutorials

Adding Many to Many Relationships to the Search Box in SuiteCRM and SugarCRM CE 6.5.x

Out the box SuiteCRM and SugarCRM CE do not allow searching from related modules with a many to many relationship. But this can be overcome by adding a custom relate field that represents your relationship then using a custom sub-query in SearchFields.php.

Step one add the relate field:

Create a file for the module you want to add the non-db related field in the custom extension vardefs:

custom/Extension/modules/Contacts/Ext/Vardefs/anyname.php


$dictionary["Contact"]["fields"]["par_med_cond"] = array (
'name' => 'par_med_cond',
'type' => 'relate',
'source' => 'non-db',
'module' => 'FRP_FundraisingProgram',
'vname' => 'LBL_CONTACTS_PAR_MED_COND',
'studio' => 'visible',
'id_name' => 'par_med_cond_id',
);

$dictionary["Contact"]["fields"]["par_med_cond_id"] = array (
'name' => 'par_med_cond_id',
'type' => 'id',
'source' => 'non-db',
'module' => 'FRP_FundraisingProgram',
'vname' => 'LBL_CONTACTS_PAR_MED_COND',
'studio' => 'visible',
);

$dictionary["Contact"]["fields"]["frp_fundraisingprogram_contacts_1frp_fundraisingprogram_ida"] = array (
'name' => 'frp_fundraisingprogram_contacts_1frp_fundraisingprogram_ida',
'type' => 'link',
'source' => 'non-db',
'reportable' => false,
'module' => 'FRP_FundraisingProgram',
'side' => 'right',
'vname' => 'LBL_CONTACTS_PAR_MED_COND_ID_TITLE',
);

Step 2 Step two add your field to the searchdefs

custom/modules/Contacts/metadata/searchdefs.php

$searchdefs ['Contacts'] =
array (
'layout' =>
array (
'basic_search' =>
array (
'search_name' =>
array (
'name' => 'search_name',
'label' => 'LBL_NAME',
'type' => 'name',
'default' => true,
'width' => '10%',
),
'par_med_cond' =>
array (
'type' => 'relate',
'studio' => 'visible',
'default' => true,
'width' => '10%',
'label' => 'LBL_CONTACTS_PAR_MED_COND',
'id' => 'PAR_MED_COND_ID',
'link' => true,
'name' => 'par_med_cond',
),

Step 3 Step two add your sub-query to the SearchFields.php

custom/modules/Contacts/metadata/SearchFields.php

See the par_med_cond definition

$searchFields['Contacts'] = array (
'first_name' =>
array (
'query_type' => 'default',
),
'last_name' =>
array (
'query_type' => 'default',
),
'search_name' =>
array (
'query_type' => 'default',
'db_field' =>
array (
0 => 'first_name',
1 => 'last_name',
),
'force_unifiedsearch' => true,
),
'account_name' =>
array (
'query_type' => 'default',
'db_field' =>
array (
0 => 'accounts.name',
),
),
'lead_source' =>
array (
'query_type' => 'default',
'operator' => '=',
'options' => 'lead_source_dom',
'template_var' => 'LEAD_SOURCE_OPTIONS',
),
'do_not_call' =>
array (
'query_type' => 'default',
'input_type' => 'checkbox',
'operator' => '=',
),
'phone' =>
array (
'query_type' => 'default',
'db_field' =>
array (
0 => 'phone_mobile',
1 => 'phone_work',
2 => 'phone_other',
3 => 'phone_fax',
4 => 'assistant_phone',
),
),
'par_med_cond' =>
array (
'query_type' => 'default',
'operator' => 'subquery',
'subquery' => 'SELECT spsmc.frp_fundraisingprogram_contacts_1contacts_idb FROM frp_fundraisingprogram_contacts_1_c spsmc LEFT JOIN frp_fundraisingprogram smc ON spsmc.frp_fundraisingprogram_contacts_1frp_fundraisingprogram_ida = smc.id WHERE smc.deleted =0 AND spsmc.deleted =0 AND smc.name LIKE',
'db_field' =>
array (
0 => 'id',
),
),
'email' =>
array (
'query_type' => 'default',
'operator' => 'subquery',
'subquery' => 'SELECT eabr.bean_id FROM email_addr_bean_rel eabr JOIN email_addresses ea ON (ea.id = eabr.email_address_id) WHERE eabr.deleted=0 AND ea.email_address LIKE',
'db_field' =>
array (
0 => 'id',
),
),

The custom relate field representing our relationship should now be available to add to the search layouts via studio and and return the correct search results. However this will not work if you need to add the field to the popupview search.

Searching on relationships is one of the big shortcomings in Sugar. Once you do get it to work for the main search results, it is likely to break if you use the pagination or Mass Update on those search results.

This is similar to what we are seeing with the popupview search. The back end doesn’t use a unified method of searching. It’s a different stack of calls depending on where you are. Very frustrating. What this means is that you can’t always get something on the list view/searches to work across to other areas like the popup view.

I struggled with this for a while then is was suggested to me to customise the view.popup.php for the module and jam in the condition if being searched on in $popupMeta['whereStatement'] within display().

Adding the many to many search to the Popup View

custom/modules/Contacts/views/view.popup.php

Inside the view.popup.php for Contacts there was an updatePopupMeta() function. Inside this function I added the following:

if(!empty($_REQUEST['mode']) && $_REQUEST['mode']=='MultiSelect_fp')
{

$popupMeta['whereStatement'] .= (!empty($popupMeta['whereStatement']) ? ' AND' : '')." ((contacts.id IN (select frp_fundraisingprogram_contacts_1contacts_idb from (SELECT spsmc.frp_fundraisingprogram_contacts_1contacts_idb FROM frp_fundraisingprogram_contacts_1_c spsmc LEFT JOIN frp_fundraisingprogram smc ON spsmc.frp_fundraisingprogram_contacts_1frp_fundraisingprogram_ida = smc.id WHERE smc.deleted =0 AND spsmc.deleted =0 AND smc.name LIKE '".$_REQUEST['metadata']."') par_med_cond_derived))) AND contacts.deleted=0 ";
}

This adds the required sub-query onto the where statement in the popup filter if mode equals ‘MultiSelect_fp’. I also had to customise the open popup JavaScript to pass in the required search parameter for the popups initial filtering: $_REQUEST['metadata']

Posted in SugarCRM development and customisation, SugarCRM development development hints and tips

Allow System Admin users to set ‘Textfields’ as being mass update fields in SugarCRM and SuiteCRM

Mass update on text field.

In my last post I showed you how to make specific fileds for specific modules mass up-datable but what if you want to make a field type mass up-datable across all modules? In the following tutorial I have used text fields as an example.

Before we start I want to make it clear that this is a Non-Upgrade Safe modification.

By default SugarCRM only allows fields of the following types to be mass up-datable :

  • Bool
  • Enum
  • Multienum
  • radioenum
  • datetime
  • date

So if you want add non mass up-datable fields or custom fields to the mass update form then you have to make some non-upgrade safe changes to the following file:

include/MassUpdate.php

Edit the getMassUpdateForm() function and add the code after this comment

/** Modification: add text fields like this */

:

if(isset($field['type']))
				{
					switch($field["type"])
					{
						case "relate":
    						    // bug 14691: avoid laying out an empty cell in the <table>
    							$handleRelationship = $this->handleRelationship($displayname, $field);
    							if ($handleRelationship != '')
    							{
    								$even = !$even;
    								$newhtml .= $handleRelationship;
    							}
							break;
						case "parent":$even = !$even; $newhtml .=$this->addParent($displayname, $field); break;
						case "int":
							if(!empty($field['massupdate']) && empty($field['auto_increment']))
							{
								$even = !$even; $newhtml .=$this->addInputType($displayname, $field);
							}
							 break;
						case "contact_id":$even = !$even; $newhtml .=$this->addContactID($displayname, $field["name"]); break;
						case "assigned_user_name":$even = !$even; $newhtml .= $this->addAssignedUserID($displayname,  $field["name"]); break;
						case "account_id":$even = !$even; $newhtml .= $this->addAccountID($displayname,  $field["name"]); break;
						case "account_name":$even = !$even; $newhtml .= $this->addAccountID($displayname,  $field["id_name"]); break;
						case "bool": $even = !$even; $newhtml .= $this->addBool($displayname,  $field["name"]); break;
						case "enum":
						case "multienum":
							if(!empty($field['isMultiSelect']))
							{
								$even = !$even; $newhtml .= $this->addStatusMulti($displayname,  $field["name"], translate($field["options"])); break;
							}else if(!empty($field['options'])) {
								$even = !$even; $newhtml .= $this->addStatus($displayname,  $field["name"], translate($field["options"])); break;
							}else if(!empty($field['function'])){
								$functionValue = $this->getFunctionValue($this->sugarbean, $field);
								$even = !$even; $newhtml .= $this->addStatus($displayname,  $field["name"], $functionValue); break;
							}
							break;
						case "radioenum":
						$even = !$even; $newhtml .= $this->addRadioenum($displayname,  $field["name"] , translate($field["options"])); break;
						case "datetimecombo":
						$even = !$even; $newhtml .= $this->addDatetime($displayname,  $field["name"]); break;
						case "datetime":
						case "date":$even = !$even; $newhtml .= $this->addDate($displayname,  $field["name"]); break;
                        /** Modification: add text fields like this */
                        case "varchar":

                            if($field['massupdate'] == 1){

                                $even = !$even; $newhtml .= $this->addTextBox($displayname,  $field["name"]);

                            } break;

                        default:
                            $newhtml .= $this->addDefault($displayname,  $field, $even); break;
                            break;
					}
				}

Note how we add the type ‘varchar’ to the switch statement. Now we add the custom function addTextBox to the bottom of the class:

public function addTextBox($displayname, $field){
        $displayname = addslashes($displayname);
        $html = <<<EOQ
	<td scope="row" width="20%">$displayname</td>
	<td class='dataField' width="30%"><input type="text" name='$field' size="12" id='{$field}' maxlength='10' value=""></td>

EOQ;
        return $html;
    }

Now if we update the vardefs for a specific text field and set ‘massupdate’ => true the field will appear in the mass update panel of the module but that’s not enough if we want system admin users to be able to add text fields to the mass update panel of a module via studio.

In order to achive this all we need to do is edit: modules/DynamicFields/templates/Fields/Forms/varchar.tpl

and add the following to the file:

<tr>
    <td class='mbLBL'>{sugar_translate module="DynamicFields" label="COLUMN_TITLE_MASS_UPDATE"}:</td>
    <td>
        {if $hideLevel < 5}
            <input type="checkbox" id="massupdate" name="massupdate" value="1" {if !empty($vardef.massupdate)}checked{/if}/>
        {else}
            <input type="checkbox" id="massupdate" name="massupdate" value="1" disabled {if !empty($vardef.massupdate)}checked{/if}/>
        {/if}
    </td>
</tr>

{include file="modules/DynamicFields/templates/Fields/Forms/coreBottom.tpl"}

Now all text fields will have the Mass Update check box available in studio.

Mass Update available on text fields

Posted in SugarCRM development and customisation, SugarCRM development development hints and tips, SugarCRM development tutorials

Add any field to Mass Update in SugarCRM CE | SuiteCRM

Before we start I want to make it clear that this is a Non-Upgrade Safe modification.

Credit to Jason Eggers for originally describing how to do this in this forum post.

By default SugarCRM only allows custom fields of the following types to be mass up-datable :

  • Bool
  • Enum
  • Multienum
  • radioenum
  • datetime
  • date

So if you want add non mass up-datable fields or custom fields to the mass update form for a specific module then you have to make some non-upgrade safe changes to the following file:

include/MassUpdate.php

Edit the getMassUpdateForm() function and add the code after this comment

/** Modification: add custom fields like this */

:

switch($field["type"]){
 case "relate": $even = !$even; $newhtml .= $this->handleRelationship($displayname, $field); break;
 case "parent":$even = !$even; $newhtml .=$this->addParent($displayname, $field); break;
 case "contact_id":$even = !$even; $newhtml .=$this->addContactID($displayname, $field["name"]); break;
 case "assigned_user_name":$even = !$even; $newhtml .= $this->addAssignedUserID($displayname, $field["name"]); break;
 case "account_id":$even = !$even; $newhtml .= $this->addAccountID($displayname, $field["name"]); break;
 case "account_name":$even = !$even; $newhtml .= $this->addAccountID($displayname, $field["id_name"]); break;
 case "bool": $even = !$even; $newhtml .= $this->addBool($displayname, $field["name"]); break;
 case "enum":
 if(!empty($field['isMultiSelect'])){
 $even = !$even; $newhtml .= $this->addStatusMulti($displayname, $field["name"], translate($field["options"])); break;
 }else{
 $even = !$even; $newhtml .= $this->addStatus($displayname, $field["name"], translate($field["options"])); break;
 }
 case "date":$even = !$even; $newhtml .= $this->addDate($displayname, $field["name"]); break;

 }

 /** Modification: add custom fields like this */
 if($field["type"] == "url" && $field["name"] == "field_name") {
 $even = !$even;
 $newhtml .= $this->addField($displayname, $field["name"]);
 } 

Above I am adding a field of type url but this could be any field type.

Now add the following function to the bottom of the MassUpdate class:

 //Add fields to the Mass Update screen
 function addField($displayname, $varname){

 global $app_strings, $app_list_strings, $theme;

 $html = <<<EOQ
 <td width="15%" scope="row" >$displayname</td>
 <td><input name='$varname' id='{$varname}' /></td>
EOQ;

 return $html;
 }

The next thing we need to do is set the massupdate parameter to true in the vardefs for the field.

If its a custom field that you added through studio then simply go into your sugar database and then the fields_meta_data table and find the row entry for your custom field.
Change the massupdate field from 0 to 1.

If its not a custom field added through studio then create a custom php file in this location:

/custom/Extention/modules/<BeanName>/Ext/Vardefs/any_name.php

and then add the following code for your specific field.

$dictionary['<BeanName>']['fields']['<field_name>']['massupdate'] = 1;

Finally run a repair/rebuild and your field should be available on mass update panel.

Posted in SugarCRM development and customisation, SugarCRM development development hints and tips

How to add custom fields to a relationship table and display them in a Subpanel – SugarCRM | SuiteCRM

There may come a time in the development of a custom SuiteCRM or SugarCRM module when it would be handy to store additional data in the relationship table between two modules. This is not possible in studio or module builder and is not that straight forward even for an experienced coder unless you have a deep understanding of sugar’s underlying architecture.

So Ill save you the headache I had when first trying to implement this type of modification.

Step 1

The first thing you need to do is define your new fields in the metadata for the relationship. I will be adding the field in the relationship between a custom module FP_events and Contacts. The relationship fp_events_contacts is a many to many and the subpanel ill be displaying the field in is the contacts subpanel on the FP_events module.

This file can be found at:

custom/metadata/fp_events_contactsMetaData.php

In the code below notice that I added a field called ‘date_cancelled’ with type ‘date’ to the fields array.


$dictionary["fp_events_contacts"] = array (
 'true_relationship_type' => 'many-to-many',
 'relationships' =>
 array (
 'fp_events_contacts' =>
 array (
 'lhs_module' => 'FP_events',
 'lhs_table' => 'fp_events',
 'lhs_key' => 'id',
 'rhs_module' => 'Contacts',
 'rhs_table' => 'contacts',
 'rhs_key' => 'id',
 'relationship_type' => 'many-to-many',
 'join_table' => 'fp_events_contacts_c',
 'join_key_lhs' => 'fp_events_contactsfp_events_ida',
 'join_key_rhs' => 'fp_events_contactscontacts_idb',
 ),
 ),
 'table' => 'fp_events_contacts_c',
 'fields' =>
 array (
 0 =>
 array (
 'name' => 'id',
 'type' => 'varchar',
 'len' => 36,
 ),
 1 =>
 array (
 'name' => 'date_modified',
 'type' => 'datetime',
 ),
 2 =>
 array (
 'name' => 'deleted',
 'type' => 'bool',
 'len' => '1',
 'default' => '0',
 'required' => true,
 ),
 3 =>
 array (
 'name' => 'fp_events_contactsfp_events_ida',
 'type' => 'varchar',
 'len' => 36,
 ),
 4 =>
 array (
 'name' => 'fp_events_contactscontacts_idb',
 'type' => 'varchar',
 'len' => 36,
 ),
 5 =>
 array (
 'name' => 'invite_status',
 'type' => 'varchar',
 'len'=>'25',
 'default'=>'Not Invited',
 ),
 6 =>
 array (
 'name' => 'accept_status',
 'type' => 'varchar',
 'len'=>'25',
 'default'=>'No Response',
 ),
 7 =>
 array (
 'name' => 'email_responded',
 'type' => 'int',
 'len' => '2',
 'default' => '0',
 ),
 8 =>
          array (
              'name' => 'date_cancelled',
              'type' => 'date',
          ),
 ),
 'indices' =>
 array (
 0 =>
 array (
 'name' => 'fp_events_contactsspk',
 'type' => 'primary',
 'fields' =>
 array (
 0 => 'id',
 ),
 ),
 1 =>
 array (
 'name' => 'fp_events_contacts_alt',
 'type' => 'alternate_key',
 'fields' =>
 array (
 0 => 'fp_events_contactsfp_events_ida',
 1 => 'fp_events_contactscontacts_idb',
 ),
 ),
 ),
);

Once you have added your desired fields to the fields arrays do a quick repair and rebuild from the admin panel of Suite or Sugar and then execute the suggested sql queries this will add the fields to the database table for the relationship. (I double checked that the fields were added by going into phpmyadmin and looking at the fp_events_contacts_c table.)

Step 2

Your fields are now defined and in the actual database table but your only half way their if you want your fields to actually be displayed in the subpanel. The next thing you want to do is define your new fields in the vardefs for the relationship. This is done by adding a file in the custom/Extensions folder like so:

custom/Extension/modules/Contacts/Ext/Vardefs/CAN_BE_ANY_NAME.php

In this file add the following three definitions for each field your adding. Pay careful attention that all the field names and ids match up between definitions as minor typos here will prevent the fields from showing in the subpanel and can be major pain to spot:

$dictionary['Contact']['fields']['e_date_cancelled'] =
 array (
 'name' => 'e_date_cancelled',
 'rname' => 'id',
 'relationship_fields'=>array('id' => 'cancelled_id', 'date_cancelled' => 'event_cancelled'),
 'vname' => 'LBL_CONT_ACCEPT_CANCELLED',
 'type' => 'relate',
 'link' => 'fp_events_contacts',
 'link_type' => 'relationship_info',
 'join_link_name' => 'fp_events_contacts',
 'source' => 'non-db',
 'importable' => 'false',
 'duplicate_merge'=> 'disabled',
 'studio' => false,
 );

$dictionary['Contact']['fields']['event_cancelled'] =
 array(
 'massupdate' => false,
 'name' => 'event_cancelled',
 'type' => 'date',
 'studio' => 'false',
 'source' => 'non-db',
 'vname' => 'LBL_LIST_ACCEPT_CANCELLED',
 'importable' => 'false',
 );
$dictionary['Contact']['fields']['cancelled_id'] =
 array(
 'name' => 'cancelled_id',
 'type' => 'varchar',
 'source' => 'non-db',
 'vname' => 'LBL_LIST_ACCEPT_CANCELLED',
 'studio' => array('listview' => false),
 );

Step 3

The final thing you need to do is define the fields in the actual layout defs of the subpanel. In this case that file is located:

custom/modules/Contacts/metadata/subpanels/FP_events_subpanel_fp_events_contacts.php

In the code below notice that I add my field event_cancelled (as defined in the step 2 vardefs) to list_fields array and further down in the array I also add ‘e_date_cancelled’ and ‘cancelled_id’ and mark their usage as query_only.

$subpanel_layout['list_fields'] = array (
 'name' =>
 array (
 'name' => 'name',
 'vname' => 'LBL_LIST_NAME',
 'sort_by' => 'last_name',
 'sort_order' => 'asc',
 'widget_class' => 'SubPanelDetailViewLink',
 'module' => 'Contacts',
 'width' => '23%',
 'default' => true,
 ),
 'account_name' =>
 array (
 'name' => 'account_name',
 'module' => 'Accounts',
 'target_record_key' => 'account_id',
 'target_module' => 'Accounts',
 'widget_class' => 'SubPanelDetailViewLink',
 'vname' => 'LBL_LIST_ACCOUNT_NAME',
 'width' => '22%',
 'sortable' => false,
 'default' => true,
 ),
 'phone_work' =>
 array (
 'name' => 'phone_work',
 'vname' => 'LBL_LIST_PHONE',
 'width' => '15%',
 'default' => true,
 ),
 'email1' =>
 array (
 'name' => 'email1',
 'vname' => 'LBL_LIST_EMAIL',
 'widget_class' => 'SubPanelEmailLink',
 'width' => '20%',
 'sortable' => false,
 'default' => true,
 ),
 'event_status_name' =>
 array (
 'vname' => 'LBL_STATUS',
 'width' => '10%',
 'sortable' => false,
 'default' => true,
 ),
 'event_accept_status' =>
 array (
 'width' => '10%',
 'sortable' => false,
 'default' => true,
 'vname' => 'LBL_ACCEPT_STATUS',
 ),
 'event_cancelled' =>
 array (
 'width' => '10%',
 'sortable' => false,
 'default' => true,
 'vname' => 'LBL_ACCEPT_CANCELLED',
 ),
 'edit_button' =>
 array (
 'vname' => 'LBL_EDIT_BUTTON',
 'widget_class' => 'SubPanelEditButton',
 'module' => 'Contacts',
 'width' => '5%',
 'default' => true,
 ),
 'remove_button' =>
 array (
 'vname' => 'LBL_REMOVE',
 'widget_class' => 'SubPanelRemoveButton',
 'module' => 'Contacts',
 'width' => '5%',
 'default' => true,
 ),
 'e_accept_status_fields' =>
 array (
 'usage' => 'query_only',
 ),
 'event_status_id' =>
 array (
 'usage' => 'query_only',
 ),
 'e_invite_status_fields' =>
 array (
 'usage' => 'query_only',
 ),
 'event_invite_id' =>
 array (
 'usage' => 'query_only',
 ),
 'e_date_cancelled' =>
 array (
 'usage' => 'query_only',
 ),
 'cancelled_id' =>
 array (
 'usage' => 'query_only',
 ),
 'first_name' =>
 array (
 'name' => 'first_name',
 'usage' => 'query_only',
 ),
 'last_name' =>
 array (
 'name' => 'last_name',
 'usage' => 'query_only',
 ),
 'salutation' =>
 array (
 'name' => 'salutation',
 'usage' => 'query_only',
 ),
 'account_id' =>
 array (
 'usage' => 'query_only',
 ),
);

Also remember to add the label in this case LBL_ACCEPT_CANCELLED to the custom language strings.

I added it to:

custom/Extension/application/Ext/Language/en_us.Advanced OpenEvents.php

$app_strings['LBL_ACCEPT_CANCELLED'] = 'Date Cancelled';

But it may work if added to the mod strings.

Now do another quick repair and rebuild from the admin panel and your custom relationship fields should now be showing up on the subpanel. You will now be able to add data into these fields in your module controller either via queries or through sugar’s bean framework.

Note you might have to manually go into the database and add some dummy data into those fields to confirm they are showing (Assuming you have not yet added any data to your new fields).

Posted in SugarCRM development and customisation, SugarCRM development tutorials

Make an Edit View field Read Only based on User Role (SugarCRM CE)

I recently had a SugarCRM project which had a requirement to make a Select field read only depending on the current users role. The requirment was that if the currenlty logged in user was in the Admin or Admin Manager role then they would be able to see the full select box in the edit view of the module. Otherwise they would only see the current value of the field and therefore not be able to change the field at all.

In order to achive this I first had to create a custom viewEdit file for my module like so:

custom/modules/[your_module]/views/view.edit.php (if this folder does not exist just create it and the file)

      class [your_module]ViewEdit extends ViewEdit
{
    function display(){
        require_once 'modules/ACLRoles/ACLRole.php';

        //Get the current user's role
        $objACLRole = new ACLRole();
        $roles = $objACLRole->getUserRoles($GLOBALS['current_user']->id);
        //check if they are in the Admin or Admin Manager's role
        if(in_array('Admin',$roles) || in_array('Admin Manager',$roles)){
            $this->ev->ss->assign('ReadOnly', '');
        }
        else{ //If not pass in a variable with the value readonly
            $this->ev->ss->assign('ReadOnly', 'readonly');
        }
        //Call the parent display function
        parent::display();
    }
}

The above code gets the currently logged in users roles and checks if the user is in the Admin or Admin Manager role if not it then assigns a value of ‘readonly’ to a variable called ReadOnly and passes it to the smarty template.

We then have to create a customCode field in the editviewdefs that checks if the ReadOnly variable is set to readonly and if so only displays the value otherwise it displays the full select box:

custom/modules/[your_module]/metadata/editviewdefs.php

(If this folder and file does not exist just go into studio and make a small change to the edit view layout for your chosen module and save it. This will create the custom metadata files for the module)

Scroll through the editview defs and find your field in the array then add the ‘customCode’ attribute below the lable like below.


 'panels' => 
    array (
      'default' => 
      array (
        0 => 
        array (
          0 => 'name',
          1 => 
          array (
            'name' => 'status',
            'studio' => 'visible',
            'label' => 'LBL_STATUS',
            'customCode' => '{if $ReadOnly == "readonly"}<span>{$fields.status.value}</span>{else}{html_options name="status" id="status" options=$fields.status.options selected=$fields.status.value}{/if}',
          ),
        ),
        1 => 

The code in brackets like this {if $readOnly == “readonly”} is smarty template code.

Posted in Uncategorised