Tuesday, August 03, 2010

SugarCrm : ModuleBuilder configure fields as links

This posting is written based on the SugarCrm 5.5.2 codebase.

In SugarCRM you only get a link pointing to the detail view of a record on the Name column/field in a list view or a sub panel. To add support for link to detail view on a arbitrary  fields/columns we need to make some changes to the Module Builder code.

Below you can find the Module Builder screen where you can define how the list view of a given module should look.


Now when we click on the Edit icon of “Test” field then it will show a following screen. (The link checkbox is a result of the changes we are going to make to the code)


When user check the Link checkbox and deploy the module then detail view hyperlink will be applied on Test field in ListView.



Likewise we can apply the hyperlink on the fields in the sub panel also. For that we need to modify the Available Subpanel layouts in Module Builder as shown in the screen shot below.

Please note that the code changes we are about to do are not upgrade safe. This means that you cannot just upgrade your SugarCrm installation when the next official release is available. If you do upgrade you will loose the ability to specify if a field in a list view or sub panel should have a link. The generated *defs.php files however should be upgrade safe meaning that if you have changed some fields to be a link this should still work on the new version.

There are several files that needs to be modified :

1.    /modules/ModuleBuilder/javascript/studiotabgroups.js


winput.value = "width=" + document.getElementById(items[i].id+'width').innerHTML;
  form.appendChild(winput);
  //}
  //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
  var linput = document.createElement('input');
  linput.type='hidden';
  linput.name= input.value + 'link';
  linput.value = "link=" + document.getElementById(items[i].id+'link').value;
  form.appendChild(linput);     
  //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
 }
}


2.    /modules/ModuleBuilder/parsers/views/ListLayoutMetaDataParser.php
Following lines are added to add the 'link' => true in a listviewdefs.php file to have a hyperlink on the field.
else {
     $newViewdefs [ $fieldname ] [ 'width' ] = "10%";
    }

    //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
    $link = substr ( $_REQUEST [ $fieldname . 'link' ], 5, 5 ) ;
    if ((isset ( $link )) && ($link !=''))
    {
     $newViewdefs [ $fieldname ] [ 'link' ] = $link=='true'?true:false ;
    } else {
     $newViewdefs [ $fieldname ] [ 'link' ] = false ;
    }
    
    if($newViewdefs [ $fieldname ] [ 'link' ] == false ){
     unset($newViewdefs [ $fieldname ] [ 'link' ]);
    }
    
    //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
    $newViewdefs [ $fieldname ] [ 'default' ] = ($i == 0) ;
   }
  }


3.    /modules/ModuleBuilder/parsers/views/SubpanelMetaDataParser.php

In this file the _populateFromRequest function is rewritten to handle the Request after submit. In this function following lines are written to add a 'widget_class' => 'SubPanelDetailViewLink' in a layout file of the Subpanel.
}
  return $listDef;
 }
 //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
 /**
  * This will populate field defs from request
  * 
  */
 protected function _populateFromRequest ()
 {
  $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->populateFromRequest()" ) ;
  $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->populateFromRequest() - fielddefs = ".print_r($this->_fielddefs, true));
  
  // Transfer across any reserved fields, that is, any where studio !== true, which are not editable but must be preserved
  $newViewdefs = array ( ) ;
  foreach ( $this->_viewdefs as $key => $def )
  {
   if (isset ( $def [ 'studio' ] ) && $def [ 'studio' ] !== true)
    $newViewdefs [ $key ] = $def ;
  }
  
  $rejectTypes = array ( 'html' , 'enum' , 'text' ) ;
  // only take items from group_0 for searchviews (basic_search or advanced_search) and subpanels (which both are missing the Available column) - take group_0, _1 and _2 for all other list views
  $lastGroup = (isset ( $this->columns [ 'LBL_AVAILABLE' ] )) ? 2 : 1 ;
  for ( $i = 0 ; isset ( $_POST [ 'group_' . $i ] ) && $i < $lastGroup ; $i ++ )
  {
   foreach ( $_POST [ 'group_' . $i ] as $fieldname )
   {
    $fieldname = strtolower ( $fieldname ) ;
    
    if (isset ( $this->_viewdefs [ $fieldname ] ))
    {
     $newViewdefs [ $fieldname ] = $this->_viewdefs [ $fieldname ] ;
    } else if (isset($this->originalViewDef) && isset ( $this->originalViewDef [ $fieldname ] ))
    {
     $newViewdefs [ $fieldname ] = $this->originalViewDef [ $fieldname ] ;
    }
    else 
    {
     $newViewdefs [ $fieldname ] = array ( 'width' => 10 , $this->labelIdentifier => $this->_fielddefs [ $fieldname ] [ 'vname' ] ) ;
    }
    // sorting fields of certain types will cause a database engine problems
    if (isset ( $this->_fielddefs [ $fieldname ] ) && (isset($this->_fielddefs[$fieldname]['type']) 
     && in_array ( $this->_fielddefs [ $fieldname ] [ 'type' ], $rejectTypes )))
    {
     $newViewdefs [ $fieldname ] [ 'sortable' ] = false ;
    }
    // Bug 23728 - Make adding a currency type field default to setting the 'currency_format' to true
    if (isset ( $this->_fielddefs [ $fieldname ] [ 'type' ]) && $this->_fielddefs [ $fieldname ] [ 'type' ] == 'currency') 
    {
     $newViewdefs [ $fieldname ] [ 'currency_format' ] = true;
    }
    
    if (isset ( $_REQUEST [ strtolower ( $fieldname ) . 'width' ] ))
    {
     $width = substr ( $_REQUEST [ $fieldname . 'width' ], 6, 3 ) ;
     if (strpos ( $width, "%" ) != false)
     {
      $width = substr ( $width, 0, 2 ) ;
     }
     if ($width < 101 && $width > 0)
     {
      $newViewdefs [ $fieldname ] [ 'width' ] = $width."%" ;
     }
    } else if (isset ( $this->_viewdefs [ $fieldname ] [ 'width' ] ))
    {
     $newViewdefs [ $fieldname ] [ 'width' ] = $this->_viewdefs [ $fieldname ] [ 'width' ] ;
    }
    
      
    //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
    /**
    * Following code is written to apply the SubPanelDetailViewLink widget to those fileds for which thee
    * Link checkbox is checked.
    */
    $link = substr ( $_REQUEST [ $fieldname . 'link' ], 5, 5 ) ;
    if ((isset ( $link )) && ($link !=''))
    {
     if($link=='true'){
      $newViewdefs [ $fieldname ] [ 'widget_class' ] = 'SubPanelDetailViewLink' ;
     } else {
      $newViewdefs [$fieldname]['widget_class'] = false ;
     }
    } else {
     $newViewdefs [ $fieldname ] [ 'widget_class' ] = false ;
    }
    
    if( (isset($this->_viewdefs[$fieldname]['widget_class'])) && 
     ($this->_viewdefs[$fieldname]['widget_class'] != '' && 
     $this->_viewdefs[$fieldname]['widget_class'] != 'SubPanelDetailViewLink') ){       
     $newViewdefs [ $fieldname ] ['widget_class'] = $this->_viewdefs[$fieldname]['widget_class'];       
    }
    
    if($newViewdefs[$fieldname]['widget_class'] == false){
     unset($newViewdefs[$fieldname]['widget_class']);
    }
    //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
    
    $newViewdefs [ $fieldname ] [ 'default' ] = ($i == 0) ;
   }
  }
  $this->_viewdefs = $newViewdefs ;

 }

}
?>

4.    /modules/ModuleBuilder/tpls/editProperty.tpl

Following lines are added to store the value of the Link field.
var property = field.name.substring('editProperty_'.length);
   var id = field.id.substring('editProperty_'.length);
   document.getElementById(id).innerHTML = field.value;
   //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
   if(id.substr( -4) == 'link'){
    document.getElementById(id).value = field.value;
   }
   //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
  }
 }
}


Link checkbox is added in following lines (please note that the syntax highlighting does not work properly for the Smarty template - that is a project for another blog posting I guess).
{if isset($property.hidden)}
   {$property.value}
  {else}
   {if $property.name eq "Link"}
    <input onchange='document.getElementById("editProperty_{$id}{$property.id}").value = this.checked;' value='{$property.value}' type='checkbox' {if ($property.value eq "true")}checked{/if}>
   {else}
   <input onchange='document.getElementById("editProperty_{$id}{$property.id}").value = this.value' value='{$property.value}'>
   {/if}
  {/if}
 </td> 
</tr>


5.    /modules/ModuleBuilder/tpls/listView.tpl
Following lines are added to pass the value of the Link field when user click on the Edit icon and to create a hidden field which will store a Link field’s value
(please note that the syntax highlighting does not work properly for the Smarty template - that is a project for another blog posting I guess)
<td></td>
   <td align="right">
    <img src="{sugar_getimagepath file='edit_inline.gif'}" style="cursor: pointer;"
    {* Following lines are added to pass the value of the Link field when user click on the Edit icon *}
    onclick="var value_label = document.getElementById('subslot{$modCounter}label').innerHTML; var value_width = document.getElementById('subslot{$modCounter}width').innerHTML; var value_link = document.getElementById('subslot{$modCounter}link').value; ModuleBuilder.getContent('module=ModuleBuilder&action=editProperty&view_module={$view_module}{if isset($subpanel)}&subpanel={$subpanel}{/if}{if $MB}&MB={$MB}&view_package={$view_package}{/if}&id_label=subslot{$modCounter}label&name_label=label_{if isset($value.label)}{$value.label}{elseif !empty($value.vname)}{$value.vname}{else}{$key}{/if}&title_label={$MOD.LBL_LABEL_TITLE}&value_label=' + value_label + '&id_width=subslot{$modCounter}width&name_width={$MOD.LBL_WIDTH}&value_width=' + value_width+'&amp;name_link=Link&amp;id_link=subslot{$modCounter}link&amp;value_link=' + value_link );">
    {* Following lines are added to pass the value of the Link field when user click on the Edit icon *}
   </td>
   </tr>
   <tr class='fieldValue'>
    {if empty($hideKeys)}<td>[{$key}]</td>{/if}
    <td align="right" colspan="2"><span id='subslot{$modCounter}width'>{$value.width}</span><span>%</span></td>
    {* Following lines are added to create a hidden field which will store a Link field’s value *}
    <input type='hidden' id='subslot{$modCounter}link' value='{if !empty($subpanel) }{if ($value.widget_class eq "SubPanelDetailViewLink")}true{else}false{/if}{else}{if ($value.link)}true{else}false{/if}{/if}' />
    {* Following lines are added to create a hidden field which will store a Link field’s value *}
  </tr>
 </table>
</li>

6.    /modules/ModuleBuilder/views/view.listview.php

$groups [ $groupKey ] [ $fieldKey ] [ 'width' ] = substr ( $field [ 'width' ], 0, strlen ( $field [ 'width' ] ) - 1 ) ;
   }
  }
  //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
  $groups [ $groupKey ] [ $fieldKey ] [ 'link' ] = $field [ 'link' ];
  //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
 }
}


And finally we have the changes in a SVN diff/patch format


Index: modules/ModuleBuilder/javascript/studiotabgroups.js
===================================================================
--- modules/ModuleBuilder/javascript/studiotabgroups.js (revision 11)
+++ modules/ModuleBuilder/javascript/studiotabgroups.js (working copy)
@@ -108,6 +108,13 @@
      winput.value = "width=" + document.getElementById(items[i].id+'width').innerHTML;
      form.appendChild(winput);
 //     }
+     //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
+     var linput = document.createElement('input');
+     linput.type='hidden';
+     linput.name= input.value + 'link';
+     linput.value = "link=" + document.getElementById(items[i].id+'link').value;
+     form.appendChild(linput);     
+     //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
     }
     }
      }
Index: modules/ModuleBuilder/parsers/views/ListLayoutMetaDataParser.php
===================================================================
--- modules/ModuleBuilder/parsers/views/ListLayoutMetaDataParser.php (revision 10)
+++ modules/ModuleBuilder/parsers/views/ListLayoutMetaDataParser.php (working copy)
@@ -300,7 +300,20 @@
                 else {
                  $newViewdefs [ $fieldname ] [ 'width' ] = "10%";
                 }
-
+                //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
+                $link = substr ( $_REQUEST [ $fieldname . 'link' ], 5, 5 ) ;
+             if ((isset ( $link )) && ($link !=''))
+                {
+                    $newViewdefs [ $fieldname ] [ 'link' ] = $link=='true'?true:false ;
+                } else {
+                 $newViewdefs [ $fieldname ] [ 'link' ] = false ;
+                }
+                
+                if($newViewdefs [ $fieldname ] [ 'link' ] == false ){
+                 unset($newViewdefs [ $fieldname ] [ 'link' ]);
+                }
+                
+                //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
                 $newViewdefs [ $fieldname ] [ 'default' ] = ($i == 0) ;
             }
         }
Index: modules/ModuleBuilder/parsers/views/SubpanelMetaDataParser.php
===================================================================
--- modules/ModuleBuilder/parsers/views/SubpanelMetaDataParser.php (revision 7)
+++ modules/ModuleBuilder/parsers/views/SubpanelMetaDataParser.php (working copy)
@@ -156,6 +156,107 @@
         }
         return $listDef;
     }
+ //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
+    /**
+     * This will populate field defs from request
+     * 
+     */
+ protected function _populateFromRequest ()
+    {
+        $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->populateFromRequest()" ) ;
+        $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->populateFromRequest() - fielddefs = ".print_r($this->_fielddefs, true));
+        
+        // Transfer across any reserved fields, that is, any where studio !== true, which are not editable but must be preserved
+        $newViewdefs = array ( ) ;
+        foreach ( $this->_viewdefs as $key => $def )
+        {
+            if (isset ( $def [ 'studio' ] ) && $def [ 'studio' ] !== true)
+                $newViewdefs [ $key ] = $def ;
+        }
+        
+        $rejectTypes = array ( 'html' , 'enum' , 'text' ) ;
+        // only take items from group_0 for searchviews (basic_search or advanced_search) and subpanels (which both are missing the Available column) - take group_0, _1 and _2 for all other list views
+        $lastGroup = (isset ( $this->columns [ 'LBL_AVAILABLE' ] )) ? 2 : 1 ;
+        for ( $i = 0 ; isset ( $_POST [ 'group_' . $i ] ) && $i < $lastGroup ; $i ++ )
+        {
+            foreach ( $_POST [ 'group_' . $i ] as $fieldname )
+            {
+                $fieldname = strtolower ( $fieldname ) ;
+                
+                if (isset ( $this->_viewdefs [ $fieldname ] ))
+                {
+                    $newViewdefs [ $fieldname ] = $this->_viewdefs [ $fieldname ] ;
+                } else if (isset($this->originalViewDef) && isset ( $this->originalViewDef [ $fieldname ] ))
+                {
+                 $newViewdefs [ $fieldname ] = $this->originalViewDef [ $fieldname ] ;
+                }
+                else 
+                {
+                    $newViewdefs [ $fieldname ] = array ( 'width' => 10 , $this->labelIdentifier => $this->_fielddefs [ $fieldname ] [ 'vname' ] ) ;
+                }
+                // sorting fields of certain types will cause a database engine problems
+                if (isset ( $this->_fielddefs [ $fieldname ] ) && (isset($this->_fielddefs[$fieldname]['type']) 
+                 && in_array ( $this->_fielddefs [ $fieldname ] [ 'type' ], $rejectTypes )))
+                {
+                    $newViewdefs [ $fieldname ] [ 'sortable' ] = false ;
+                }
+                // Bug 23728 - Make adding a currency type field default to setting the 'currency_format' to true
+                if (isset ( $this->_fielddefs [ $fieldname ] [ 'type' ]) && $this->_fielddefs [ $fieldname ] [ 'type' ] == 'currency') 
+                {
+                    $newViewdefs [ $fieldname ] [ 'currency_format' ] = true;
+                }
+                
+                if (isset ( $_REQUEST [ strtolower ( $fieldname ) . 'width' ] ))
+                {
+                    $width = substr ( $_REQUEST [ $fieldname . 'width' ], 6, 3 ) ;
+                    if (strpos ( $width, "%" ) != false)
+                    {
+                        $width = substr ( $width, 0, 2 ) ;
+                    }
+                    if ($width < 101 && $width > 0)
+                    {
+                        $newViewdefs [ $fieldname ] [ 'width' ] = $width."%" ;
+                    }
+                } else if (isset ( $this->_viewdefs [ $fieldname ] [ 'width' ] ))
+                {
+                    $newViewdefs [ $fieldname ] [ 'width' ] = $this->_viewdefs [ $fieldname ] [ 'width' ] ;
+                }
+    
+               
+                //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
+    /**
+    * Following code is written to apply the SubPanelDetailViewLink widget to those fileds for which thee
+    * Link checkbox is checked.
+    */
+                $link = substr ( $_REQUEST [ $fieldname . 'link' ], 5, 5 ) ;
+             if ((isset ( $link )) && ($link !=''))
+                {
+                    if($link=='true'){
+                  $newViewdefs [ $fieldname ] [ 'widget_class' ] = 'SubPanelDetailViewLink' ;
+                    } else {
+                     $newViewdefs [$fieldname]['widget_class'] = false ;
+                    }
+                } else {
+                 $newViewdefs [ $fieldname ] [ 'widget_class' ] = false ;
+                }
+                
+                if( (isset($this->_viewdefs[$fieldname]['widget_class'])) && 
+                 ($this->_viewdefs[$fieldname]['widget_class'] != '' && 
+                 $this->_viewdefs[$fieldname]['widget_class'] != 'SubPanelDetailViewLink') ){                   
+                 $newViewdefs [ $fieldname ] ['widget_class'] = $this->_viewdefs[$fieldname]['widget_class'];                   
+                }
+                
+                if($newViewdefs[$fieldname]['widget_class'] == false){
+                 unset($newViewdefs[$fieldname]['widget_class']);
+    }
+                //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
+                
+                $newViewdefs [ $fieldname ] [ 'default' ] = ($i == 0) ;
+            }
+        }
+        $this->_viewdefs = $newViewdefs ;
 
+    }
+
 }
 ?>
Index: modules/ModuleBuilder/tpls/editProperty.tpl
===================================================================
--- modules/ModuleBuilder/tpls/editProperty.tpl (revision 8)
+++ modules/ModuleBuilder/tpls/editProperty.tpl (working copy)
@@ -60,6 +60,11 @@
      var property = field.name.substring('editProperty_'.length);
      var id = field.id.substring('editProperty_'.length);
      document.getElementById(id).innerHTML = field.value;
+     //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
+     if(id.substr( -4) == 'link'){
+      document.getElementById(id).value = field.value;
+     }
+     //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
     }
    }
   }
@@ -90,7 +95,11 @@
    {if isset($property.hidden)}
     {$property.value}
    {else}
+    {if $property.name eq "Link"}
+     <input onchange='document.getElementById("editProperty_{$id}{$property.id}").value = this.checked;' value='{$property.value}' type='checkbox' {if ($property.value eq "true")}checked{/if}>
+    {else}
     <input onchange='document.getElementById("editProperty_{$id}{$property.id}").value = this.value' value='{$property.value}'>
+    {/if}
    {/if}
   </td> 
  </tr>
Index: modules/ModuleBuilder/tpls/listView.tpl
===================================================================
--- modules/ModuleBuilder/tpls/listView.tpl (revision 7)
+++ modules/ModuleBuilder/tpls/listView.tpl (working copy)
@@ -93,12 +93,13 @@
             <td></td>
             <td align="right">
                 <img src="{sugar_getimagepath file='edit_inline.gif'}" style="cursor: pointer;"
-                onclick="var value_label = document.getElementById('subslot{$modCounter}label').innerHTML; var value_width = document.getElementById('subslot{$modCounter}width').innerHTML; ModuleBuilder.getContent('module=ModuleBuilder&action=editProperty&view_module={$view_module}{if isset($subpanel)}&subpanel={$subpanel}{/if}{if $MB}&MB={$MB}&view_package={$view_package}{/if}&id_label=subslot{$modCounter}label&name_label=label_{if isset($value.label)}{$value.label}{elseif !empty($value.vname)}{$value.vname}{else}{$key}{/if}&title_label={$MOD.LBL_LABEL_TITLE}&value_label=' + value_label + '&id_width=subslot{$modCounter}width&name_width={$MOD.LBL_WIDTH}&value_width=' + value_width );">
+                onclick="var value_label = document.getElementById('subslot{$modCounter}label').innerHTML; var value_width = document.getElementById('subslot{$modCounter}width').innerHTML; var value_link = document.getElementById('subslot{$modCounter}link').value; ModuleBuilder.getContent('module=ModuleBuilder&action=editProperty&view_module={$view_module}{if isset($subpanel)}&subpanel={$subpanel}{/if}{if $MB}&MB={$MB}&view_package={$view_package}{/if}&id_label=subslot{$modCounter}label&name_label=label_{if isset($value.label)}{$value.label}{elseif !empty($value.vname)}{$value.vname}{else}{$key}{/if}&title_label={$MOD.LBL_LABEL_TITLE}&value_label=' + value_label + '&id_width=subslot{$modCounter}width&name_width={$MOD.LBL_WIDTH}&value_width=' + value_width+'&amp;name_link=Link&amp;id_link=subslot{$modCounter}link&amp;value_link=' + value_link );">
             </td>
             </tr>
             <tr class='fieldValue'>
                 {if empty($hideKeys)}<td>[{$key}]</td>{/if}
                 <td align="right" colspan="2"><span id='subslot{$modCounter}width'>{$value.width}</span><span>%</span></td>
+    <input type='hidden' id='subslot{$modCounter}link' value='{if !empty($subpanel) }{if ($value.widget_class eq "SubPanelDetailViewLink")}true{else}false{/if}{else}{if ($value.link)}true{else}false{/if}{/if}' />
         </tr>
     </table>
 </li>
Index: modules/ModuleBuilder/views/view.listview.php
===================================================================
--- modules/ModuleBuilder/views/view.listview.php (revision 9)
+++ modules/ModuleBuilder/views/view.listview.php (working copy)
@@ -206,6 +206,9 @@
                         $groups [ $groupKey ] [ $fieldKey ] [ 'width' ] = substr ( $field [ 'width' ], 0, strlen ( $field [ 'width' ] ) - 1 ) ;
                     }
                 }
+    //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
+                $groups [ $groupKey ] [ $fieldKey ] [ 'link' ] = $field [ 'link' ];
+                //Hack Module Builder - Subpanel and ListView field support for hyperlink to detailsview 
             }
         }
Post a Comment