User Interface
Overview
Sugar's user interface is dependent on the client (i.e. base, mobile, or portal) being used to access the system. Clients are the various platforms that use Sugar's APIs to render the user interface. Each platform type will have a specific path for its components. While the Developer Guide mainly covers the base client type, the following sections will outline the various metadata locations.
Clients
Clients are the various platforms that access and useÂ Sidecar to render content. Depending on the platform you are using, the layout, view, and metadata will be driven based on its client type. The following sections describe the client types.
base
The base client is the Sugar application that you use to access your data from a web browser. The framework's specific views, layouts, and fields are rendered usingÂ Sidecar. Â Files specific to this client type can be found in the following directories:
./clients/base/
./custom/clients/base/
./modules/<module>/clients/base/
./custom/modules/<module>/clients/base/
mobile
The mobile client is the SugarCRM mobile applicationÂ that you use to access data from your mobile device. The framework-specific views, layouts, and fields for this application areÂ found in the following directories:
./clients/mobile/
./custom/clients/mobile/
./modules/<module>/clients/mobile/
./custom/modules/<module>/clients/mobile/
portal
The portal client is the customer self-service portal application that comes with SugarÂ Enterprise and Sugar Ultimate.Â The framework-specific views, layouts, and fields for this application areÂ found in the following directories:
./clients/portal/
./custom/clients/portal/
./modules/<module>/clients/portal/
./custom/modules/<module>/clients/portal/


TopicsSidecarSidecar is a platform that moves processing to the client side to render pages as single-page web apps. Sidecar contains a complete Model-View-Controller (MVC) framework based on the Backbone.js library.EventsThe Backbone events module is a lightweight pub-sub pattern that gets mixed into each Backbone class (Model, View, Collection, Router, etc.). This means that you can listen to or dispatch custom named events from any Backbone object.RoutesRoutes determine where users are directed based on patterns in the URL.HandlebarsThe Handlebars library, located in ./sidecar/lib/handlebars/, is a JavaScript library that lets Sugar create semantic templates. Handlebars help render content for layouts, views, and fields for Sidecar. Using Handlebars, you can make modifications to the display of content such as adding HTML or CSS.LayoutsLayouts are component plugins that define the overall layout and positioning of the page. Layouts replace the previous concept of MVC views and are used system-wide to generate rows, columns, bootstrap fluid layouts, and pop-ups by wrapping and placing multiple views or nested layouts on a page.ViewsViews are component plugins that render data from a context. View components may contain field components and are typically made up of a controller JavaScript file (.js) and at least one Handlebars template (.hbs).FieldsFields are component plugins that render and format field values. They are made up of a controller JavaScript file (.js) and at least one Handlebars template (.hbt). For more information regarding the data handling of a field, please refer the data framework fields documentation. For information on creating custom field types, please refer the Creating Custom Field Types cookbook example.SubpanelsFor Sidecar, Sugar's subpanel layouts have been modified to work as simplified metadata. This page is an overview of the metadata framework for subpanels.DashletsDashlets are special view-component plugins that render data from a context and make use of the Dashlet plugin. They are typically made up


that render data from a context and make use of the Dashlet plugin. They are typically made up of a controller JavaScript file (.js) and at least one Handlebars template (.hbs).DrawersThe drawer layout widget, located in ./clients/base/layouts/drawer/, is used to display a window of additional content to the user. This window can then be closed to display the content the user was previously viewing.AlertsThe alert view widget, located in ./clients/base/views/alert/, displays helpful information such as loading messages, notices, and confirmation messages to the user.LanguageThe language library, located in ./sidecar/src/core/language.js, is used to manage the user's display language as well as fetch labels and lists. For more information on customizing languages, please visit the language framework documentation.MainLayoutSugar's Main navigation is split into two components: Top-Header and Sidebar. The Top-Header is primarily used for search, notifications, and user actions and the Sidebar is the primary tool used to navigate the front end of the Sugar application.Administration LinksAdministration links are the shortcut URLs found on the Administration page in the Sugar application. Developers can create additional administration links using the extension framework.Legacy MVCThe legacy MVC Architecture.


Last modified: 2023-02-03 21:04:03


Layouts
Overview
Layouts are component plugins that define the overall layout and positioning of the page. Layouts replace the previous concept of MVC views and are used system-wide to generate rows, columns, bootstrap fluid layouts, and pop-ups by wrapping and placing multiple views or nested layouts on a page.
Layout components are typically made up of a controller JavaScript file (.js) and a PHP file (.php), however, layout types vary and are not dependent on having both files.
Hierarchy Diagram
The layout components are loaded in the following manner:
Note: The Sugar application client type is "base". For more information on the various client types, please refer toÂ the User Interface page.
Sidecar Layout Routing
Sidecar uses routing to determine where to direct the user. To route the user to a specific page in Sugar, refer to the following default URL formats:
Behavior
URL Format
Route the user to the list layout for a module
http://{site url}/#<module>/
Route the user to the record layout for a specific record
http://{site url}/#<module>/f82d09cb-48cd-a1fb-beae-521cf39247b5
Route the user to a custom layout for the module
http://{site url}/#<module>/layout/<layout>
Layout Example
The list layout, located in ./clients/base/layouts/list/, handles the layout for the list view. The sections below outline the various files that render this view.
JavaScript
The file list.js, shown below, contains the JavaScript used to place the layout content.
./clients/base/layouts/list/list.js
/**
* Layout that places components using bootstrap fluid layout divs
* @class View.Layouts.ListLayout
* @extends View.FluidLayout
*/
({
    /**
     * Places a view's element on the page.
     * @param {View.View} comp


* @param {View.View} comp
     * @protected
     * @method
     */
    _placeComponent: function(comp, def) {
        var size = def.size || 12;
        // Helper to create boiler plate layout containers
        function createLayoutContainers(self) {
            // Only creates the containers once
            if (!self.$el.children()[0]) {
                comp.$el.addClass('list');
            }
        }
        createLayoutContainers(this);
        // All components of this layout will be placed within the
        // innermost container div.
        this.$el.append(comp.el);
    }
})
Layout Definition
The layout definition is contained as an array in list.php. This layout definition contains four views:
massupdate
massaddtolist
recordlist
list-bottom
./clients/base/layouts/list/list.php
<?php
$viewdefs['base']['layout']['list'] = array(
    'components' =>
    array(
        array(
            'view' => 'massupdate',
        ),
        array(
            'view' => 'massaddtolist',
        ),
        array(
            'view' => 'recordlist',
            'primary' => true,
        ),
        array(
            'view' => 'list-bottom',
        ),
    ),
    'type' => 'simple',
    'name' => 'list',
    'span' => 12,
);
Application
For information on working with layouts, please refer to the Creating Layouts and Overriding Layouts pages for practical examples.


TopicsCreating LayoutsThis example explains how to create a custom layout to define the various components that load on a page.Overriding LayoutsThis page explains how to override a stock layout component. For this example, we will extend the stock record view and create a custom view named "my-record" that will be used in our record layout's override. This example involves two steps:
  	Last modified: 2023-02-03 21:04:03


Overriding Layouts
Overview
This page explains how to override a stock layout component. For this example, we will extend the stock record view and create a custom view named "my-record" that will be used in our record layout's override. This example involves twoÂ steps:
Override the Layout
Extend the View
These steps are explained in the following sections.
Overriding the Layout
First,Â copy ./clients/base/layouts/record/record.php to ./custom/clients/base/layouts/record/record.php. Once copied, modify the following line from:
'view' => 'record',
To:
'view' => 'my-record',
That line will change the record layout from using the base record.js view, ./clients/base/views/record/record.js, to instead use a custom view that we will create in ./custom/clients/base/views/my-record/my-record.js. At this point, theÂ custom layout override should be very similar to the example below:
./custom/clients/base/layouts/record/record.php
<?php
$viewdefs['base']['layout']['record'] = array(
    'components' => array(
        array(
            'layout' => array(
                'type' => 'default',
                'name' => 'sidebar',
                'components' => array(
                    array(
                        'layout' => array(
                            'type' => 'base',
                            'name' => 'main-pane',
                            'css_class' => 'main-pane span8',
                            'components' => array(
                                array(
                                    'view' => 'my-record',
                                    'primary' => true,
                                ),
                                array(
                                    'layout' => 'extra-info',
                                ),
                                array(
                                    'layout' => array(
                                        'type' => 'filterpanel',


'layout' => array(
                                        'type' => 'filterpanel',
                                        'last_state' => array(
                                            'id' => 'record-filterpanel',
                                            'defaults' => array(
                                                'toggle-view' => 'subpanels',
                                            ),
                                        ),
                                        'refresh_button' => true,
                                        'availableToggles' => array(
                                            array(
                                                'name' => 'subpanels',
                                                'icon' => 'fa-table',
                                                'label' => 'LBL_DATA_VIEW',
                                            ),
                                            array(
                                                'name' => 'list',
                                                'icon' => 'fa-table',
                                                'label' => 'LBL_LISTVIEW',
                                            ),
                                            array(
                                                'name' => 'activitystream',
                                                'icon' => 'fa-clock-o',
                                                'label' => 'LBL_ACTIVITY_STREAM',
                                            ),
                                        ),
                                        'components' => array(
                                            array(
                                                'layout' => 'filter',
                                                'xmeta' => array(
                                                    'layoutType' => '',
                                                ),
                                                'loadModule' => 'Filters',
                                            ),
                                            array(
                                                'view' => 'filter-rows',
                                            ),
                                            array(
                                                'view' => 'filter-actions',
                                            ),
                                            array(
                                                'layout' => 'activitystream',
                                                'context' =>
                                                array(
                                                    'module' => 'Activities',
                                                ),
                                            ),
                                            array(
                                                'layout' => 'subpanels',
                                            ),
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                    array(
                        'layout' => array(
                            'type' => 'base',
                            'name' => 'dashboard-pane',


'type' => 'base',
                            'name' => 'dashboard-pane',
                            'css_class' => 'dashboard-pane',
                            'components' => array(
                                array(
                                    'layout' => array(
                                        'type' => 'dashboard',
                                        'last_state' => array(
                                            'id' => 'last-visit',
                                        )
                                    ),
                                    'context' => array(
                                        'forceNew' => true,
                                        'module' => 'Home',
                                    ),
                                    'loadModule' => 'Dashboards',
                                ),
                            ),
                        ),
                    ),
                    array(
                        'layout' => array(
                            'type' => 'base',
                            'name' => 'preview-pane',
                            'css_class' => 'preview-pane',
                            'components' => array(
                                array(
                                    'layout' => 'preview',
                                ),
                            ),
                        ),
                    ),
                ),
            ),
        ),
    ),
);
Extending the View
For this example, we will extend the stock record view and create a custom view named my-record that will be used in our record layouts override.
./custom/clients/base/views/my-record/my-record.js
({
    extendsFrom: 'RecordView',
    initialize: function (options) {
        this._super("initialize", [options]);
        //log this point
        console.log("**** Override called");
    }
})
Once the files are in place, navigate to Admin > Repair > Quick Repair and Rebuild.
  	Last modified: 2023-02-03 21:04:03


Creating Layouts
Overview
This exampleÂ explains howÂ to create a custom layout to define the various components that load on aÂ page.Â
Creating the Layout
This example creates a component named "my-layout", whichÂ will render a custom view named "my-view".
./custom/clients/base/layouts/my-layout/my-layout.php
<?php
    $viewdefs['base']['layout']['my-layout'] = array(
        'type' => 'simple',
        'components' => array(
            array(
                'view' => 'my-view',
            ),
        ),
    );
Creating a View
The view component will render the actual content we want to see on the page. The view below will display a clickable cube icon that will spin when clicked by the user.
./custom/clients/base/views/my-view/my-view.js
({
    className: 'my-view tcenter',
    cubeOptions: {
        spin: false
    },
    events: {
        'click .sugar-cube': 'spinCube'
    },
    spinCube: function() {
        this.cubeOptions.spin = !this.cubeOptions.spin;
        this.render();
    }
})
./custom/clients/base/views/my-view/my-view.hbs

My View

{{{subFieldTemplate 'sugar-cube' 'detail' cubeOptions}}}

Click to spin the cube!


Click to spin the cube!

Once the files are in place, navigate to Admin > Repair and perform a Quick Repair and Rebuild.
Navigating to the Layout
To see this newÂ layout and view, navigate to http://{site url}/#<module>/layout/my-layout.
  	Last modified: 2023-02-03 21:04:03


Language
Overview
The language library, located in ./sidecar/src/core/language.js, is used to manage the user's display language as well as fetch labels and lists. For more information on customizing languages, please visit the language framework documentation.
Methods
app.lang.get(key, module, context)
The app.lang.get(key, module, context) method fetches a string for a given key. The method searches the module strings first and then falls back to the app strings. If the label is a template, it will be compiled and executed with the given context.
Parameters
Name
Required
Description
key
yes
The key of the string to retrieve
module
no
The Sugar module that the label belongs to
context
no
Template context
Example
app.lang.get('LBL_NAME', 'Accounts');
app.lang.getAppString(key)
The app.lang.getAppString(key) method retrieves an application string for a given key.
Parameters
Name
Required
Description
key
yes
The key of the string to retrieve
Example
app.lang.getAppString('LBL_MODULE');
app.lang.getAppListStrings(key)
The app.lang.getAppListStrings(key) method retrieves an application list string or object.
Parameters
Name
Required
Description
key
yes
The key of the string to retrieve
Example
app.lang.getAppListStrings('sales_stage_dom');
app.lang.getModuleSingular(moduleKey)
The app.lang.getModuleSingular(moduleKey) method retrieves an application list string or object.
Parameters
Name
Required
Description
moduleKey
yes
The module key of the singular module label to retrieve
Example
app.lang.getModuleSingular("Accounts");
app.lang.getLanguage()
The app.lang.getLanguage() method retrieves the current user's language key.
Example
app.lang.getLanguage();
app.lang.updateLanguage(languageKey)


Example
app.lang.getLanguage();
app.lang.updateLanguage(languageKey)
The app.lang.updateLanguage(languageKey) method updates the current user's language key.
Parameters
Name
Required
Description
languageKey
yes
Language key of the language to set for the user
Example
app.lang.updateLanguage('en_us');
Testing in Console
To test out the language library, you can trigger actionsÂ in your browsers developer tools by using the global Apps variable as shown below:
App.lang.getAppListStrings('sales_stage_dom');
  	Last modified: 2023-02-03 21:04:03


Subpanels
Overview
For Sidecar, Sugar's subpanel layouts have been modified to work as simplified metadata.Â This page is anÂ overview of the metadata framework for subpanels.Â
The reason for this change is that previous versions of Sugar generated the metadata from various sources such as the SubPanelLayout and MetaDataManager classes. This eliminates the need for generating and processing the layouts and allows the metadata to be easily loaded to Sidecar.
Note:Â Modules running in backward compatibility mode do not use the Sidecar subpanel layouts as they use the legacy MVC framework.
Hierarchy Diagram
When loading the Sidecar subpanel layouts, the system processes the layout in the following manner:
Note: The Sugar application's client type is "base". For more information on the various client types, please refer toÂ the User Interface page.
Subpanels and Subpanel Layouts
Sugar contains both a subpanels (plural) layout and a subpanel (singular) layout. The subpanels layout contains the collection of subpanels,Â whereasÂ the subpanel layout renders the actual subpanel widget.
An example of a stock module's subpanels layout is:
./modules/Bugs/clients/base/layouts/subpanels/subpanels.php
<?php
$viewdefs['Bugs']['base']['layout']['subpanels'] = array (
  'components' => array (
      array (
          'layout' => 'subpanel',
          'label' => 'LBL_DOCUMENTS_SUBPANEL_TITLE',
          'context' => array (
              'link' => 'documents',
          ),
      ),
      array (
          'layout' => 'subpanel',
          'label' => 'LBL_CONTACTS_SUBPANEL_TITLE',
          'context' => array (
              'link' => 'contacts',
          ),
      ),
      array (


'link' => 'contacts',
          ),
      ),
      array (
          'layout' => 'subpanel',
          'label' => 'LBL_ACCOUNTS_SUBPANEL_TITLE',
          'context' => array (
              'link' => 'accounts',
          ),
      ),
      array (
          'layout' => 'subpanel',
          'label' => 'LBL_CASES_SUBPANEL_TITLE',
          'context' => array (
              'link' => 'cases',
          ),
      ),
  ),
  'type' => 'subpanels',
  'span' => 12,
);
You can see that the layout incorporates the use of the subpanel layout for each module. As most of the subpanel data is similar, this approach allows us to use less duplicate code. The subpanel layout, shown below, shows the three views that make up the subpanel widgets users see.
./clients/base/layouts/subpanel/subpanel.php
<?php
$viewdefs['base']['layout']['subpanel']  = array (
    'components' => array (
        array (
            'view' => 'panel-top',
        )
        array (
            'view' => 'subpanel-list',
        ),
        array (
            'view' => 'list-bottom',
        ),
    ),
    'span' => 12,
    'last_state' => array(
        'id' => 'subpanel'
    ),
);
Adding Subpanel Layouts


),
);
Adding Subpanel Layouts
When a new relationship is deployed from Studio, the relationship creation process will generate the layouts using the extension framework. You should note that for stock relationships and custom deployed relationships, layouts are generated for both Sidecar and Legacy MVC Subpanel formats. This is done to ensure that any related modules, whether in Sidecar or Backward Compatibility mode, display a related subpanel as expected.
Sidecar Layouts
Custom Sidecar layouts, located in ./custom/Extension/modules/<module>/Ext/clients/<client>/layouts/subpanels/, are compiled into ./custom/modules/<module>/Ext/clients/<client>/layouts/subpanels/subpanels.ext.php using the extension framework. When a relationship is saved, layout files are created for both the "base" and "mobile" client types.
For example, deploying a 1:M relationship from Bugs to Leads will generate the following Sidecar files:
./custom/Extension/modules/Bugs/Ext/clients/base/layouts/subpanels/bugs_leads_1_Bugs.php
<?php
$viewdefs['Bugs']['base']['layout']['subpanels']['components'][] = array (
  'layout' => 'subpanel',
  'label' => 'LBL_BUGS_LEADS_1_FROM_LEADS_TITLE',
  'context' =>
  array (
    'link' => 'bugs_leads_1',
  ),
);
./custom/Extension/modules/Bugs/Ext/clients/mobile/layouts/subpanels/bugs_leads_1_Bugs.php
<?php
$viewdefs['Bugs']['mobile']['layout']['subpanels']['components'][] = array (
  'layout' => 'subpanel',
  'label' => 'LBL_BUGS_LEADS_1_FROM_LEADS_TITLE',
  'context' =>
  array (
    'link' => 'bugs_leads_1',
  ),


array (
    'link' => 'bugs_leads_1',
  ),
);
Note: The additional legacy MVC layouts generated by a relationships deployment are described below.
Legacy MVC Subpanel Layouts
Custom Legacy MVC Subpanel layouts, located in ./custom/Extension/modules/<module>/Ext/Layoutdefs/, are compiled into ./custom/modules/<module>/Ext/Layoutdefs/layoutdefs.ext.php using the extension framework. You should also note that when a relationship is saved, wireless layouts, located in ./custom/Extension/modules/<module>/Ext/WirelessLayoutdefs/, are created and compiled into ./custom/modules/<module>/Ext/Layoutdefs/layoutdefs.ext.php.
An example of this is when deploying a 1-M relationship from Bugs to Leads, the following layoutdef files are generated:
./custom/Extension/modules/Bugs/Ext/Layoutdefs/bugs_leads_1_Bugs.php
<?php
$layout_defs["Bugs"]["subpanel_setup"]['bugs_leads_1'] = array (
  'order' => 100,
  'module' => 'Leads',
  'subpanel_name' => 'default',
  'sort_order' => 'asc',
  'sort_by' => 'id',
  'title_key' => 'LBL_BUGS_LEADS_1_FROM_LEADS_TITLE',
  'get_subpanel_data' => 'bugs_leads_1',
  'top_buttons' =>
  array (
    0 =>
    array (
      'widget_class' => 'SubPanelTopButtonQuickCreate',
    ),
    1 =>
    array (
      'widget_class' => 'SubPanelTopSelectButton',
      'mode' => 'MultiSelect',
    ),
  ),
);


'mode' => 'MultiSelect',
    ),
  ),
);
./custom/Extension/modules/Bugs/Ext/WirelessLayoutdefs/bugs_leads_1_Bugs.php
<?php
$layout_defs["Bugs"]["subpanel_setup"]['bugs_leads_1'] = array (
  'order' => 100,
  'module' => 'Leads',
  'subpanel_name' => 'default',
  'title_key' => 'LBL_BUGS_LEADS_1_FROM_LEADS_TITLE',
  'get_subpanel_data' => 'bugs_leads_1',
);
Fields Metadata
Sidecar's subpanel field layouts are initially defined by the subpanel list-view metadata.
Hierarchy Diagram
The subpanel list metadata is loaded in the following manner:
Note: The Sugar application's client type is "base". For more information on the various client types, please refer toÂ the User Interface page.
Subpanel List Views
By default, all modules come with a default set of subpanel fields for when they are rendered as a subpanel. An example of this is can be found in the Bugs module:
./modules/Bugs/clients/base/views/subpanel-list/subpanel-list.php
<?php
$subpanel_layout['list_fields'] = array (
  'full_name' =>
  array (
    'type' => 'fullname',
    'link' => true,
    'studio' =>
    array (
      'listview' => false,
    ),
    'vname' => 'LBL_NAME',
    'width' => '10%',
    'default' => true,
  ),
  'date_entered' =>
  array (
    'type' => 'datetime',
    'studio' =>
    array (


'type' => 'datetime',
    'studio' =>
    array (
      'portaleditview' => false,
    ),
    'readonly' => true,
    'vname' => 'LBL_DATE_ENTERED',
    'width' => '10%',
    'default' => true,
  ),
  'refered_by' =>
  array (
    'vname' => 'LBL_LIST_REFERED_BY',
    'width' => '10%',
    'default' => true,
  ),
  'lead_source' =>
  array (
    'vname' => 'LBL_LIST_LEAD_SOURCE',
    'width' => '10%',
    'default' => true,
  ),
  'phone_work' =>
  array (
    'vname' => 'LBL_LIST_PHONE',
    'width' => '10%',
    'default' => true,
  ),
  'lead_source_description' =>
  array (
    'name' => 'lead_source_description',
    'vname' => 'LBL_LIST_LEAD_SOURCE_DESCRIPTION',
    'width' => '10%',
    'sortable' => false,
    'default' => true,
  ),
  'assigned_user_name' =>
  array (
    'name' => 'assigned_user_name',
    'vname' => 'LBL_LIST_ASSIGNED_TO_NAME',
    'widget_class' => 'SubPanelDetailViewLink',
    'target_record_key' => 'assigned_user_id',
    'target_module' => 'Employees',
    'width' => '10%',
    'default' => true,
  ),
  'first_name' =>
  array (
    'usage' => 'query_only',
  ),
  'last_name' =>


'usage' => 'query_only',
  ),
  'last_name' =>
  array (
    'usage' => 'query_only',
  ),
  'salutation' =>
  array (
    'name' => 'salutation',
    'usage' => 'query_only',
  ),
);
To modify this layout, navigate to Admin > Studio > {Parent Module} > Subpanels > Bugs and make your changes. Once saved, Sugar will generate ./custom/modules/Bugs/clients/<client>/views/subpanel-for-<link>/subpanel-for-<link>.php which will be used for rendering the fields you selected.
You should note that, just as Sugar mimics the Sidecar layouts in the legacy MVC framework for modules in backward compatibility, it also mimics the field list in ./modules/<module>/metadata/subpanels/default.php and ./custom/modules/<module>/metadata/subpanels/default.php. This is done to ensure that any related modules, whether in Sidecar or Backward Compatibility mode, display the same field list as expected.
  	Last modified: 2023-02-03 21:04:03


Legacy MVC
Overview
TheÂ legacy MVC Architecture.
You should note that the MVC architecture is being deprecated and is being replaced with sidecar. Until the framework is fully deprecated, modules set in backward compatibility mode will still use the MVC framework.
Model-View-Controller (MVC) Overview
A model-view-controller, or MVC, is a design philosophy that creates a distinct separation between business-logic and display logic.
Model : This is the data object built by the business/application logic needed to present in the user interface. For Sugar, it is represented by the SugarBean and all subclasses of the SugarBean.
View : This is the display layer which is responsible for rendering data from the Model to the end-user.
Controller : This is the layer that handles user events such as "Save" and determines what business logic actions to take to build the model, and which view to load for rendering the data to end users.
SugarCRM MVC Implementation
The following is a sequence diagram that highlights some of the main components involved within the Sugar MVC framework.
Â
TopicsViewDisplaying information to the browser.ControllerThe basic actions of a module.MetadataAn overview of the legacy MVC metadata framework.ExamplesProvides an overview of example MVC customizations.
  	Last modified: 2023-02-03 21:04:03


Controller
Overview
The basic actions of a module.
Controllers
The main controller, named SugarController, addresses the basic actions of a module from EditView and DetailView to saving a record. Each module can override this SugarController by adding a controller.php file into its directory. This file extends the SugarController, and the naming convention for the class is: <module>Controller
Inside the controller, you define an action method. The naming convention for the method is: action_<action name>
There are more fine-grained control mechanisms that a developer can use to override the controller processing. For example, if a developer wanted to create a new save action, there are three places where they could possibly override.
action_save: This is the broadest specification and gives the user full control over the save process.
pre_save: A user could override the population of parameters from the form.
post_save: This is where the view is being set up. At this point, the developer could set a redirect URL, do some post-save processing, or set a different view.
Upgrade-Safe Implementation
You can also add a custom Controller that extends the module's Controller if such a Controller already exists. For example, if you want to extend the Controller for a module, you should check if that module already has a module-specific controller. If so, you extend from that controller class. Otherwise, you extend from SugarController class. In both cases, you should place the custom controller class file in ./custom/modules/<module>/Controller.php instead of the module directory. Doing so makes your customization upgrade-safe.
File Structure
./include/MVC/Controller/SugarController.php
./include/MVC/Controller/ControllerFactory.php
./modules/<module>/Controller.php
./custom/modules/<module>/controller.php
Implementation
If the module does not contain a controller.php file in ./modules/<module>/, you will create the following file:


./custom/modules/<module>/controller.php
class <module>Controller extends SugarController
{
    function action_<action>()
    {
        $this->view = '<action lowercase>';
    }
}
If the module does contain a controller.php file, you will need to extend it by doing the following:
./custom/modules/<module>/controller.php
require_once 'modules/<module>/controller.php';
class Custom<module>Controller extends <module>Controller
{
    function action_<action>()
    {
        $this->view = '<action lowercase>';
    }
}
Note: When creating or moving files you will need to rebuild the file map.
More information on rebuilding the file map can be found in the SugarAutoLoader.
Mapping Actions to Files
You can choose not to provide a custom action method as defined above, and instead, specify your mappings of actions to files in $action_file_map. Take a look at ./include/MVC/Controller/action_file_map.php as an example:
$action_file_map['subpanelviewer'] = 'include/SubPanel/SubPanelViewer.php';
$action_file_map['save2'] = 'include/generic/Save2.php';
$action_file_map['deleterelationship'] = 'include/generic/DeleteRelationship.php';
$action_file_map['import'] = 'modules/Import/index.php';
Here the developer has the opportunity to map an action to a file. For example, Sugar uses a generic sub-panel file for handling subpanel actions. You can see above that there is an entry mapping the action 'subpanelviewer' to ./include/SubPanel/SubPanelViewer.php.
The base SugarController class loads the action mappings in the following path sequence:
./include/MVC/Controller
./modules/<module>
./custom/modules/<module>
./custom/include/MVC/Controller


./custom/modules/<module>
./custom/include/MVC/Controller
Each one loads and overrides the previous definition if in conflict. You can drop a new action_file_map in the later path sequence that extends or overrides the mappings defined in the previous one.
Upgrade-Safe Implementation
If you want to add custom action_file_map.php to an existing module that came with the SugarCRM release, you should place the file at ./custom/modules/<module>/action_file_map.php
File Structure
./include/MVC/Controller/action_file_map.php
./modules/<module>/action_file_map.php
./custom/modules/<module>/action_file_map.php
Implementation
$action_file_map['soapRetrieve'] = 'custom/SoapRetrieve/soap.php';
Classic Support (Not Recommended)
Classic support allows you to have files that represent actions within your module. Essentially, you can drop in a PHP file into your module and have that be handled as an action. This is not recommended, but is considered acceptable for backward compatibility. The better practice is to take advantage of the action_<action> structure.
File Structure
./modules/<module>/<action>.php
Controller Flow Overview
For example, if a request comes in for DetailView the controller will handle the request as follows:
Start in index.php and load the SugarApplication instance.
SugarApplication instantiates the SugarControllerFactory.
SugarControllerFactory loads the appropriate Controller.
SugarControllerFactory checks for ./custom/modules/<module>/Controller.php.
If not found, check for ./modules/<module>/Controller.php.
If not found, load SugarController.php.
Calls on the appropriate action.
Look for ./custom/modules/<module>/<action>.php. If found and ./custom/modules/<module>/views/view.<action>.php is not found, use this view.


If not found check for modules/<module>/<action>.php. If found and ./modules/<module>/views/view.<action>.php is not found, then use the ./modules/<module>/<action>.php action.
If not found, check for the method action_<action> in the controller.
If not found, check for an action_file_mapping.
If not found, report error "Action is not defined".
  	Last modified: 2023-02-03 21:04:03


View
Overview
Displaying information to the browser.
What are Views?
Views, otherwise known as actions, are typically used to render views or to process logic. Views are not just limited to HTML data. You can send JSON encoded data as part of a view or any other structure you wish. As with the controllers, there is a default class called SugarView which implements much of the basic logic for views, such as handling of headers and footers.
There are five main actions for a module:
Display Actions
Detail View: A detail view displays a read-only view of a particular record. Usually, this is accessed via the list view. The detail view displays the details of the object itself and related items (subpanels). Subpanels are miniature list views of items that are related to the parent object. For example, Tasks assigned to a Project, or Contacts for an Opportunity will appear in subpanels in the Project or Opportunity detail view. The file ./<module>/metadata/detailviewdefs.php defines a module's detail view page layout. The file ./<module>/metadata/subpaneldefs.php defines the subpanels that are displayed in the module's detail view page.
Edit View: The edit view page is accessed when a user creates a new record or edits details of an existing one. Edit view can also be accessed directly from the list view. The file ./<module>/metadata/editviewdefs.php defines a module's edit view page layout.
List View: This Controller action enables the search form and search results for a module. Users can perform actions such as delete, export, update multiple records (mass update), and drill into a specific record to view and edit the details. Users can see this view by default when they click one of the module tabs at the top of the page. Files in each module describe the contents of the list and search view.
Process Actions


Process Actions
Save: This Controller action is processed when the user clicks Save in the record's edit view.
Delete: This action is processed when the user clicks "Delete" in the detail view of a record or in the detail view of a record listed in a subpanel.
Implementation
Class File Structure
./include/MVC/Views/SugarView.php
./include/MVC/Views/view.<view>.php
./custom/include/MVC/Views/view.<view>.php
./modules/<module>/views/view.<view>.php
./custom/modules/<module>/views/view.<view>.php
Class Loading
The ViewFactory class loads the view based off the the following sequence loading the first file it finds:
./custom/modules/<module>/views/view.<view>.php
./modules/<module>/views/view.<view>.php
./custom/include/MVC/View/view.<view>.php
./include/MVC/Views/view.<view>.php
Methods
There are two main methods to override within a view:
preDisplay(): This performs pre-processing within a view. This method is relevant only for extending existing views. For example, the include/MVC/View/views/view.edit.php file uses it, and enables developers who wishes to extend this view to leverage all of the logic done in preDisplay() and either override the display() method completely or within your own display() method call parent::display().
display(): This method displays the data to the screen. Place the logic to display output to the screen here.
Creating Views
Creating a new/view action consists of a controller action and a view file. The first step is to define your controller action. If the module does not contain a controller.php file in ./modules/<module>/ you will create the following file:
./custom/modules/<module>/controller.php
<?php
class <module>Controller extends SugarController
{
    function action_MyView()


class <module>Controller extends SugarController
{
    function action_MyView()
    {
        $this->view = 'myview';
    }
}
More information on controllers can be found in the Controller section.
The next step is to define your view file. This example extends the ViewDetail class but you can extend any of the classes you choose in ./include/MVC/View/views/.
./custom/modules/<module>/views/view.newview.php
<?php
require_once 'include/MVC/View/views/view.detail.php' ;
class <module>ViewMyView extends ViewDetail
{
    function display()
    {
        echo 'This is my new view
';
    }
}
Overriding Views
The following section will demonstrate how to extend and override a view. When overriding existing actions and views, you won't need to make any changes to the controller. This approach will be very similar for any view you may choose to modify. If the module you are extending the view for does not contain an existing view in its modules views directory ( ./modules/<module>/views/ ), you will need to extend the views base class. Otherwise, you will extend the view class found within the file.
In the case of a detail view, you would check for the file ./modules/<module>/views/view.detail.php. If this file does not exist, you will create ./custom/modules/<module>/views/view.detail.php and extend the base ViewDetail class with the name <module>ViewDetail.
./custom/modules/<module>/views/view.detail.php
<?php
require_once('include/MVC/View/views/view.detail.php');
class <module>ViewDetail extends ViewDetail
{
    function display()
    {
        echo 'This is my addition to the DetailView
';
        //call parent display method
        parent::display();
    }
}


//call parent display method
        parent::display();
    }
}
If ./modules/<module>/views/view.detail.php does exist, you would create ./custom/modules/<module>/views/view.detail.php and extend the base <module>ViewDetail class with the name Custom<module>ViewDetail.
./custom/modules/<module>/views/view.detail.php
<?php
require_once('modules/<module>/views/view.detail.php');
class Custom<module>ViewDetail extends <module>ViewDetail
{
    function display()
    {
        echo 'This is my addition to the DetailView<br>';
        //call parent display method
        parent::display();
    }
}
Display Options for Views
The Sugar MVC provides developers with granular control over how the screen looks when a view is rendered. Each view can have a config file associated with it. In the case of an edit view, the developer would create the file ./customs/modules/<module>/views/view.edit.config.php . When the edit view is rendered, this config file will be picked up. When loading the view, ViewFactory class will merge the view config files from the following possible locations with precedence order (high to low):
./customs/modules/<module>/views/view.<view>.config.php
./modules/<module>/views/view.<view>.config.php
./custom/include/MVC/View/views/view.<view>.config.php
./include/MVC/View/views/view.<view>.config.php
Implementation
The format of these files is as follows:
$view_config = array(
    'actions' =>
        array(
            'popup' => array(
            'show_header' => false,
            'show_subpanels' => false,
            'show_search' => false,
            'show_footer' => false,
            'show_JavaScript' => true,
        ),


'show_JavaScript' => true,
        ),
    ),
    'req_params' => array(
        'to_pdf' => array(
            'param_value' => true,
            'config' => array(
                'show_all' => false
            ),
        ),
    ),
);
To illustrate this process, let us take a look at how the 'popup' action is processed. In this case, the system will go to the actions entry within the view_config and determine the proper configuration. If the request contains the parameter to_pdf, and is set to be true, then it will automatically cause the show_all configuration parameter to be set false, which means none of the options will be displayed.
  	Last modified: 2023-02-03 21:04:03


Examples
Provides an overview of example MVC customizations.Â
TopicsChanging the ListView Default Sort OrderThis article addresses the need to customize the advanced search layout options for modules in backward compatibility mode to change the default sort order from ascending to descending.
  	Last modified: 2023-02-03 21:04:03


Changing the ListView Default Sort Order
Overview
This article addresses the need to customize the advanced search layout options for modules in backward compatibility mode to change the default sort order from ascending to descending.
Customization Information
This customization is only for modules in backward compatibility mode and involves creating custom files that extend stock files. You should note that this customization does not address all scenarios within the view that may assign a sort order.
Extending the Search Form
First, we will need to extend the SearchForm class. To do this, we will create a CustomSearchForm class that extends the original SearchForm class located in ./include/SearchForm/SearchForm2.php. We will then override the _displayTabs method to check the $_REQUEST['sortOrder'] and default it to descending if it isn't set.
./custom/include/SearchForm/SearchForm2.php
<?php
require_once 'include/SearchForm/SearchForm2.php';
class CustomSearchForm extends SearchForm
{
    /**
     * displays the tabs (top of the search form)
     *
     * @param string $currentKey key in $this->tabs to show as the current tab
     *
     * @return string html
     */
    function _displayTabs($currentKey)
    {
        //check and set the default sort order
        if (!isset($_REQUEST['sortOrder']))
        {
            $_REQUEST['sortOrder'] = 'DESC';
        }
        return parent::_displayTabs($currentKey);;
    }
}
?>
Extending the List View
Next, we will need to extend the ListView. We will create a ViewCustomList class that extends the original ListView located in ./include/MVC/View/views/view.list.php. In the ViewCustomList class, we will override the prepareSearchForm and getSearchForm2 methods to call the CustomSearchForm class.


./custom/include/MVC/View/views/view.customlist.php
<?php
require_once 'include/MVC/View/views/view.list.php';
class ViewCustomList extends ViewList
{
    function prepareSearchForm()
    {
        $this->searchForm = null;
        //search
        $view = 'basic_search';
        if(!empty($_REQUEST['search_form_view']) && $_REQUEST['search_form_view'] == 'advanced_search')
            $view = $_REQUEST['search_form_view'];
        $this->headers = true;
        if(!empty($_REQUEST['search_form_only']) && $_REQUEST['search_form_only'])
            $this->headers = false;
        elseif(!isset($_REQUEST['search_form']) || $_REQUEST['search_form'] != 'false')
        {
            if(isset($_REQUEST['searchFormTab']) && $_REQUEST['searchFormTab'] == 'advanced_search')
            {
                $view = 'advanced_search';
            }
            else
            {
                $view = 'basic_search';
            }
        }
        $this->view = $view;
        $this->use_old_search = true;
        if (SugarAutoLoader::existingCustom('modules/' . $this->module . '/SearchForm.html') &&
            !SugarAutoLoader::existingCustom('modules/' . $this->module . '/metadata/searchdefs.php')) {
            require_once('include/SearchForm/SearchForm.php');
            $this->searchForm = new SearchForm($this->module, $this->seed);
            }  else {
            $this->use_old_search = false;
            //Updated to require the extended CustomSearchForm class
            require_once('custom/include/SearchForm/SearchForm2.php');
            $searchMetaData = SearchForm::retrieveSearchDefs($this->module);


$searchMetaData = SearchForm::retrieveSearchDefs($this->module);
            $this->searchForm = $this->getSearchForm2($this->seed, $this->module, $this->action);
            $this->searchForm->setup($searchMetaData['searchdefs'], $searchMetaData['searchFields'], 'SearchFormGeneric.tpl', $view, $this->listViewDefs);
            $this->searchForm->lv = $this->lv;
        }
    }
    /**
     * Returns the search form object
     *
     * @return SearchForm
     */
    protected function getSearchForm2($seed, $module, $action = "index")
    {
        //Updated to use the extended CustomSearchForm class
        return new CustomSearchForm($seed, $module, $action);
    }
}
?>
Extending the Sugar Controller
Finally, we will create a CustomSugarController class that extends the orginal SugarController located in ./include/MVC/Controller/SugarController.php. We will then need to override the do_action and post_action methods to execute their parent methods as well as the action_listview method to assign the custom view to the view attribute.
./custom/include/MVC/Controller/SugarController.php
<?php
/**
* Custom SugarCRM controller
* @api
*/
class CustomSugarController extends SugarController
{
    /**
     * Perform the specified action.
     * This can be overridde in a sub-class
     */
    private function do_action()
    {
        return parent::do_action();
    }
    /**
     * Perform an action after to the specified action has occurred.
     * This can be overridde in a sub-class
     */
    private function post_action()
    {
        return parent::post_action();


private function post_action()
    {
        return parent::post_action();
    }
    /**
     * Perform the listview action
     */
    protected function action_listview()
    {
        parent::action_listview();
        //set the new custom view
        $this->view = 'customlist';
    }
}
?>
  	Last modified: 2023-02-03 21:04:03


Metadata
Overview
An overview of the legacy MVC metadata framework.
You should note that the MVC architecture is being deprecated and is being replaced with sidecar. Until the framework is fully deprecated, modules set in backward compatibility mode will still use the legacy MVC framework.
Metadata Framework
Background
Metadata is defined as information about data. In Sugar, metadata refers to the framework of using files to abstract the presentation and business logic found in the system. The metadata framework is described in definition files that are processed using PHP. The processing usually includes the use of Smarty templates for rendering the presentation and JavaScript libraries to handle some business logic that affects conditional displays, input validation, and so on.
Application Metadata
All application modules are defined in the modules.php file. It contains several variables that define which modules are active and usable in the application.
The file is located under the '<sugar root>/include' folder. It contains the $moduleList() array variable which contains the reference to the array key to look up the string to be used to display the module in the tabs at the top of the application. The coding standard is for the value to be in the plural of the module name; for example, Contacts, Accounts, Widgets, and so on.
The $beanList() array stores a list of all active beans (modules) in the application. The $beanList entries are stored in a 'name' => 'value' fashion with the 'name' value being in the plural and the 'value' being in the singular of the module name. The 'value' of a $beanList() entry is used to lookup values in our next modules.php variable, the $beanFiles() array.


The $beanFiles variable is also stored in a 'name' => 'value' fashion. The 'name', typically in singular, is a reference to the class name of the object, which is looked up from the $beanList 'value', and the 'value' is a reference to the class file.
The remaining relevant variables in the modules.php file are the $modInvisList variable which makes modules invisible in the regular user interface (i.e., no tab appears for these modules), and the $adminOnlyList which is an extra level of security for modules that are accessible only by administrators through the Admin page.
Module Metadata
The following table lists the metadata definition files found in the modules/[module]/metadata directory, and a brief description of their purpose within the system.
File
Description
additionalDetails.php
Used to render the popup information displayed when a user hovers the mouse cursor over a row in the List View.
editviewdefs.php
Used to render a record's EditView.
detailviewdefs.php
Used to render a record's DetailView.
listviewdefs.php
Used to render the List View display for a module.
metafiles.php
Used to override the location of the metadata definition file to be used. The EditView, DetailView, List View, and Popup code check for the presence of these files.
popupdefs.php
Used to render and handle the search form and list view in popups.
searchdefs.php
Used to render a module's basic and advanced search form displays.
sidecreateviewdefs.php
Used to render a module's quick create form shown in the side shortcut panel.
subpaneldefs.php
Used to render a module's subpanels shown when viewing a record's DetailView.
SearchForm Metadata
The search form layout for each module is defined in the module's metadata file searchdefs.php. A sample of the Accounts searchdefs.php appears as:


<?php
    $searchdefs['Accounts'] = array(
        'templateMeta' => array(
            'maxColumns' => '3',
            'widths' => array(
                'label' => '10',
                'field' => '30'
            )
        ),
        'layout' => array(
            'basic_search' => array(
                'name',
                'billing_address_city',
                'phone_office',
                array(
                    'name' => 'address_street',
                    'label' => 'LBL_BILLING_ADDRESS',
                    'type' => 'name',
                    'group' => 'billing_address_street'
                ),
                array(
                    'name' => 'current_user_only',
                    'label' => 'LBL_CURRENT_USER_FILTER',
                    'type'=>'bool'
                ),
            ),
            'advanced_search' => array(
                'name',
                array(
                    'name' => 'address_street',
                    'label' => 'LBL_ANY_ADDRESS',
                    'type' => 'name'
                ),
                array(
                    'name' => 'phone',
                    'label' => 'LBL_ANY_PHONE',
                    'type' => 'name'
                ),
                'website',
                array(
                    'name' => 'address_city',
                    'label' => 'LBL_CITY',
                    'type' => 'name'
                ),
                array(
                    'name' => 'email',
                    'label' =>'LBL_ANY_EMAIL',
                    'type' => 'name'
                ),
                'annual_revenue',
                array(
                    'name' => 'address_state',
                    'label' =>'LBL_STATE',
                    'type' => 'name'


'label' =>'LBL_STATE',
                    'type' => 'name'
                ),
                'employees',
                array(
                    'name' => 'address_postalcode',
                    'label' =>'LBL_POSTAL_CODE',
                    'type' => 'name'
                ),
                array(
                    'name' => 'billing_address_country',
                    'label' =>'LBL_COUNTRY',
                    'type' => 'name'
                ),
                'ticker_symbol',
                'sic_code',
                'rating',
                'ownership',
                array(
                    'name' => 'assigned_user_id',
                    'type' => 'enum',
                    'label' => 'LBL_ASSIGNED_TO',
                    'function' => array(
                        'name' =>'get_user_array',
                        'params' => array(false)
                    )
                ),
                'account_type',
                'industry',
            ),
        ),
    );
?>
The searchdefs.php file contains the Array variable $searchDefs with one entry. The key is the name of the module as defined in $moduleList array defined in include/modules.php. The $searchDefsarray is another array that describes the search form layout and fields.
The 'templateMeta' key points to another array that controls the maximum number of columns in each row of the search form ('maxColumns'), as well as layout spacing attributes as defined by 'widths'. In the above example, the generated search form files will allocate 10% of the width spacing to the labels and 30% for each field respectively.


The 'layout' key points to another nested array which defines the fields to display in the basic and advanced search form tabs. Each individual field definition maps to a SugarField widget. See the SugarField widget section for an explanation about SugarField widgets and how they are rendered for the search form, DetailView, and EditView.
The searchdefs.php file is invoked from the MVC framework whenever a module's list view is rendered (see include/MVC/View/views/view.list.php). Within view.list.php, checks are made to see if the module has defined a SearchForm.html file. If this file exists, the MVC will run in classic mode and use the aforementioned include/SearchForm/SearchForm.php file to process the search form. Otherwise, the new search form processing is invoked using include/SearchForm/SearchForm2.php and the searchdefs.php file is scanned for, first under the custom/modules/[module]/metadata directory and then in modules/[module]/metadata.
The processing flow for the search form using the metadata subpaneldefs.php file is similar to that of EdiView and DetailView.
DetailView and EditView Metadata
Metadata files are PHP files that declare nested Array values that contain information about the view, such as buttons, hidden values, field layouts, and more. Following is a visual diagram representing how the Array values declared in the Metadata file are nested:
The following diagram highlights the process of how the application determines which Metadata file is to be used when rendering a request for a view:
The "Classic Mode" on the right hand side of the diagram represents the SugarCRM pre-5.x rendering of a Detail/Editview. This section will focus on the MVC/Metadata mode.


When the view is first requested, the preDisplay method will attempt to find the correct Metadata file to use. Typically, the Metadata file will exist in the [root level]/modules/[module]/metadata directory, but in the event of edits to a layout through the Studio interface, a new Metadata file will be created and placed in the [root level]/custom/modules/[module]/metadata directory. This is done so that changes to layouts may be restored to their original state through Studio, and also to allow changes made to layouts to be upgrade-safe when new patches and upgrades are applied to the application. The metafiles.php file that may be loaded allows for the loading of Metadata files with alternate naming conventions or locations. An example of the metafiles.php contents can be found for the Accounts module (though it is not used by default in the application).
$metafiles['Accounts'] = array(
    'detailviewdefs' => 'modules/Accounts/metadata/detailviewdefs.php',
    'editviewdefs' => 'modules/Accounts/metadata/editviewdefs.php',
    'ListViewdefs' => 'modules/Accounts/metadata/ListViewdefs.php',
    'searchdefs' => 'modules/Accounts/metadata/searchdefs.php',
    'popupdefs' => 'modules/Accounts/metadata/popupdefs.php',
    'searchfields' => 'modules/Accounts/metadata/SearchFields.php',
);
After the Metadata file is loaded, the preDisplay method also creates an EditView object and checks if a Smarty template file needs to be built for the given Metadata file. The EditView object does the bulk of the processing for a given Metadata file (creating the template, setting values, setting field level ACL controls if applicable, etc.). Please see the EditView process diagram for more detailed information about these steps.


After the preDisplay method is called in the view code, the display method is called, resulting in a call to the EditView object's process method, as well as the EditView object's display method.
The EditView class is responsible for the bulk of the Metadata file processing and creation of the resulting display. The EditView class also checks to see if the resulting Smarty template is already created. It also applies the field level ACL controls for Sugar Sell, Serve, Ultimate, Enterprise, Corporate, and Professional.
The classes responsible for displaying the Detail View and SearchForm also extend and use the EditView class. The ViewEdit, ViewDetail and ViewSidequickcreate classes use the EditView class to process and display their contents. Even the file that renders the quick create form display (SubpanelQuickCreate.php) uses the EditView class. DetailView (in DetailView2.php) and SearchForm (in SearchForm2.php) extend the EditView class while SubpanelQuickCreate.php uses an instance of the EditView class. The following diagram highlights these relationships.
The following diagram highlights the EditView class's main responsibilities and their relationships with other classes in the system. We will use the example of a DetailView request although the sequence will be similar for other views that use the EditView class.
One thing to note is the EditView class's interaction with the TemplateHandler class. The TemplateHandler class is responsible for generating a Smarty template in the cache/modules/<module> directory. For example, for the Accounts module, the TemplateHandler will create the Smarty file, cache/modules/Accounts/DetailView.tpl, based on the Metadata file definition and other supplementary information from the EditView class. The TemplateHandler class actually uses Smarty itself to generate the resulting template that is placed in the aforementioned cache directory.


Some of the modules that are available in the SugarCRM application also extend the ViewDetail class. One example of this is the DetailView for the Projects module. As mentioned in the MVC section, it is possible to extend the view classes by placing a file in the modules/<module>/views directory. In this case, a view.detail.php file exists in the modules/Projects/views folder. This may serve as a useful example in studying how to extend a view and apply additional field/layout settings not provided by the EditView class.
The following diagram shows the files involved with the DetailView example in more detail:
A high level processing summary of the components for DetailViews follows:
The MVC framework receives a request to process the DetaiView.php (A) action for a module. For example, a record is selected from the list view shown on the browser with URL:
index.php?action=DetailView&module=Opportunities&record=46af9843-ccdf-f489-8833
At this point the new MVC framework checks to see if there is a DetailView.php (A2) file in the modules/Opportunity directory that will override the default DetailView.php implementation. The presence of a DetailView.php file will trigger the "classic" MVC view. If there is no DetailView.php (A2) file in the directory, the MVC will also check if you have defined a custom view to handle the DetailView rendering in MVC (that is, checks if there is a file modules/Opportunity/views/view.detail.php). See the documentation for the MVC architecture for more information. Finally, if neither the DetailView.php (A2) nor the view.detail.php exists, then the MVC will invoke include/DetailView/DetailView.php (A).
The MVC framework (see views.detail.php in include/MVC/View/views folder) creates an instance of the generic DetailView (A)
// Call DetailView2 constructor


// Call DetailView2 constructor
$dv = new DetailView2();
// Assign by reference the Sugar_Smarty object created from MVC
// We have to explicitly assign by reference to back support PHP 4.x
$dv->ss =& $this->ss;
// Call the setup function
$dv->setup($this->module, $this->bean, $metadataFile, 'include/DetailView/DetailView.tpl');
// Process this view
$dv->process();
// Return contents to the buffer
echo $dv->display();
When the setup method is invoked, a TemplateHandler instance (D) is created. A check is performed to determine which detailviewdefs.php metadata file to used in creating the resulting DetailView. The first check is performed to see if a metadata file was passed in as a parameter. The second check is performed against the custom/studio/modules/[Module] directory to see if a metadata file exists. For the final option, the DetailView constructor will use the module's default detailviewdefs.php metadata file located under the modules/[Module]/metadata directory. If there is no detailviewdefs.php file in the modules/[Module]/metadata directory, but a DetailView.html exists, then a "best guess" version is created using the metadata parser file in include/SugarFields/Parsers/DetailViewMetaParser.php (not shown in diagram).
The TemplateHandler also handles creating the quick search (Ajax code to do look ahead typing) as well as generating the JavaScript validation rules for the module. Both the quick search and JavaScript code should remain static based on the definitions of the current definition of the metadata file. When fields are added or removed from the file through Studio, this template and the resulting updated quick search and JavaScript code will be rebuilt.


It should be noted that the generic DetailView (A) defaults to using the generic DetailView.tpl smarty template file (F). This may also be overridden through the constructor parameters. The generic DetailView (A) constructor also retrieves the record according to the record id and populates the $focus bean variable.
The process() method is invoked on the generic DetailView.php instance:
function process()
{
    //Format fields first
    if($this->formatFields)
    {
        $this->focus->format_all_fields();
    }
    parent::process();
}
This, in turn, calls the EditView->process() method since DetailView extends from EditView. The EditView->process() method will eventually call the EditView->render() method to calculate the width spacing for the DetailView labels and values. The number of columns and the percentage of width to allocate to each column may be defined in the metadata file. The actual values are rounded as a total percentage of 100%. For example, given the templateMeta section's maxColumns and widths values:
'templateMeta' => array(
    'maxColumns' => '2',
    'widths' => array(
        array(
            'label' => '10',
            'field' => '30'
        ),
        array(
            'label' => '10',
            'field' => '30'
        )
    ),
),
We can see that the labels and fields are mapped as a 1-to-3 ratio. The sum of the widths only equals a total of 80 (10 + 30 x 2) so the actual resulting values written to the Smarty template will be at a percentage ratio of 12.5-to-37.5. The resulting fields defined in the metadata file will be rendered as a table with the column widths as defined:


The actual metadata layout will allow for variable column lengths throughout the displayed table. For example, the metadata portion defined as:
'panels' => array(
    'default' => array(
        array(
            'name',
            array(
                'name' => 'amount',
                'label' => '{$MOD.LBL_AMOUNT} ({$CURRENCY})',
            ),
        ),
        array(
            'account_name',
        ),
        array(
            '',
            'opportunity_type',
        )
    )
)
This specifies a default panel under the panels section with three rows. The first row has two fields (name and amount). The amount field has some special formatting using the label override option. The second row contains the account_name field and the third row contains the opportunity_type column.
Next, the process() method populates the $fieldDefs array variable with the vardefs.php file (G) definition and the $focus bean's value. This is done by calling the toArray () method on the $focus bean instance and combining these values with the field definition specified in the vardefs.php file (G).
The display() method is then invoked on the generic DetailView instance for the final step. When the display() method is invoked, variables to the DetailView.tpl Smarty template are assigned and the module's HTML code is sent to the output buffer.


Before HTML code is sent back, the TemplateHandler (D) first performs a check to see if an existing DetailView template already exists in the cache respository (H). In this case, it will look for file cache/modules/Opportunity/DetailView.tpl. The operation of creating the Smarty template is expensive so this operation ensures that the work will not have to be redone. As a side note, edits made to the DetailView or EditView through the Studio application will clear the cache file and force the template to be rewritten so that the new changes are reflected.
If the cache file does not exist, the TemplateHandler (D) will create the template file and store it in the cache directory. When the fetch() method is invoked on the Sugar_Smarty class (E) to create the template, the DetailView.tpl file is parsed.
TopicsExamplesLegacy MVC metadata examples.
  	Last modified: 2023-02-03 21:04:03


Examples
Legacy MVC metadata examples.
TopicsHiding the Quotes Module PDF ButtonsHow to hide the PDF buttons on a Quote.Manipulating Buttons on Legacy MVC LayoutsHow to add custom buttons to the EditView and DetailView layouts.Manipulating Layouts ProgrammaticallyHow to manipulate and merge layouts programmatically.Modifying Layouts to Display Additional ColumnsHow to add additional columns to layouts.
  	Last modified: 2023-02-03 21:04:03


Manipulating Buttons on Legacy MVC Layouts
Overview
How to add custom buttons to the EditView and DetailView layouts.
Note: This customization is only applicable for modules in backward compatibility mode.
Metadata
Before adding buttons to your layouts, you will need to understand how the metadata framework is used. Detailed information on the metadata framework can be found in the Legacy Metadata section.
Custom Layouts
Before you can add a button to your layout, you will first need to make sure you have a custom layout present. The stock layouts are located in ./modules/<module>/metadata/ and must be recreated in ./custom/modules/<module>/metadata/.
There are two ways to recreate a layout in the custom directory if it does not already exist. The first is to navigate to:
Studio > {Module} > Layouts > {View}
Once there, you can click the "Save & Deploy" button. This will create the layoutdef for you. Alternatively, you can also manually copy the layoutdef from the stock folder to the custom folder.
Editing Layouts
When editing layouts you have three options in having your changes reflected in the UI.
Developer Mode
You can turn on Developer Mode:
Admin > System Settings
Developer Mode will remove the caching of the metadata framework. This will cause your changes to be reflected when the page is refreshed. Make sure this setting is deactivated when you are finished with your customization.
Quick Repair and Rebuild
You can run a Quick Repair and Rebuild:
Admin > Repair > Quick Repair and Rebuild
Doing this will rebuild the cache for the metadata.
Saving & Deploying the Layout in Studio
You may also choose to load the layout in studio and then save & deploy it:
Admin > Studio > {Module} > Layouts > {View}


Admin > Studio > {Module} > Layouts > {View}
This process can be a bit confusing, however, once a layout is changed, you can then choose to load the layout in studio and then click "Save & Deploy" . This will rebuild the cache for that specific layout. Please note that any time you change the layout, you will have to reload the Studio layout view before deploying in order for this to work correctly.
Adding Custom Buttons
When adding buttons, there are several things to consider when determining how the button should be rendered. The following sections will outline these scenarios when working with the accounts editviewdefs located in ./custom/modules/Accounts/metadata/editviewdefs.php.
JavaScript Actions
If you are adding a button solely to execute JavaScript (no form submissions), you can do so by adding the button HTML to:
$viewdefs['<Module>']['<View>']['templateMeta']['form']['buttons']
Example
<?php
$viewdefs['Accounts'] =
array (
  'DetailView' =>
  array (
    'templateMeta' =>
    array (
      'form' =>
      array (
        'buttons' =>
        array (
          0 => 'EDIT',
          1 => 'DUPLICATE',
          2 => 'DELETE',
          3 => 'FIND_DUPLICATES',
          4 => 'CONNECTOR',
          5 =>
          array (
            'customCode' => '<input id="JavaScriptButton" title="JavaScript Button" class="button" type="button" name="JavaScriptButton" value="JavaScript Button" onclick="alert(\'Button JavaScript\')">',
          ),
        ),
      ),
Submitting the Stock View Form


),
        ),
      ),
Submitting the Stock View Form
If you intend to submit the stock layout form ('formDetailView' or 'formEditView') to a new action, you can do so by adding a the button HTML with an onclick event as shown below to:
$viewdefs['<Module>']['<View>']['templateMeta']['form']['buttons']
Example
<?php
$viewdefs['Accounts'] =
array (
  'DetailView' =>
  array (
    'templateMeta' =>
    array (
      'form' =>
      array (
        'hidden' =>
        array (
          0 => '<input type="hidden" id="customFormField" name="customFormField" value="">',
        ),
        'buttons' =>
        array (
          0 => 'EDIT',
          1 => 'DUPLICATE',
          2 => 'DELETE',
          3 => 'FIND_DUPLICATES',
          4 => 'CONNECTOR',
          5 =>
          array (
            'customCode' => '<input id="SubmitStockFormButton" title="Submit Stock Form Button" class="button" type="button" name="SubmitStockFormButton" value="Submit Stock Form Button" onclick="var _form = document.getElementById(\'formDetailView\'); _form.customFormField.value = \'CustomValue\'; _form.action.value = \'CustomAction\'; SUGAR.ajaxUI.submitForm(_form);">',
          ),
        ),
      ),
You should note in this example that there is also a 'hidden' index. This is where you should add any custom hidden inputs:
  $viewdefs['<Module>'][
  '<View>'][
  'templateMeta'][
  'form'][
  'hidden']
Submitting Custom Forms


'templateMeta'][
  'form'][
  'hidden']
Submitting Custom Forms
If you intend to submit a custom form, you will first need to set 'closeFormBeforeCustomButtons' to true. This will close out the current views form and allow you to create your own.
$viewdefs['<Module>']['<View>']['templateMeta']['form']['closeFormBeforeCustomButtons']
Next, you will add the form and button HTML as shown below to:
$viewdefs['<Module>']['<View>']['templateMeta']['form']['buttons']
Example
<?php
$viewdefs['Accounts'] =
array (
  'DetailView' =>
  array (
    'templateMeta' =>
    array (
      'form' =>
      array (
        'closeFormBeforeCustomButtons' => true,
        'buttons' =>
        array (
          0 => 'EDIT',
          1 => 'DUPLICATE',
          2 => 'DELETE',
          3 => 'FIND_DUPLICATES',
          4 => 'CONNECTOR',
          5 =>
          array (
            'customCode' => '<form action="index.php" method="POST" name="CustomForm" id="form"><input type="hidden" name="customFormField" name="customFormField" value="CustomValue"><input id="SubmitCustomFormButton" title="Submit Custom Form Button" class="button" type="submit" name="SubmitCustomFormButton" value="Submit Custom Form Button"></form>',
          ),
        ),
      ),
Removing Buttons
To remove a button from the detail view will require modifying the ./modules/<module>/metadata/detailviewdefs.php.
The code is originally defined as:
$viewdefs[$module_name] = array (
    'DetailView' => array (
        'templateMeta' => array (


'DetailView' => array (
        'templateMeta' => array (
            'form' => array (
                'buttons' => array (
                    'EDIT',
                    'DUPLICATE',
                    'DELETE',
                    'FIND_DUPLICATES'
                ),
            ),
To remove one or more buttons, simply remove the 'buttons' attribute(s) that you do not want on the view.
$viewdefs[$module_name] = array (
    'DetailView' => array (
        'templateMeta' => array (
            'form' => array (
                'buttons' => array (
                    'DELETE',
                ),
            ),
Â
  	Last modified: 2023-02-03 21:04:03


Modifying Layouts to Display Additional Columns
Overview
How to add additional columns to layouts.
By default, the editview, detailview, and quickcreate layouts for each module display two columns of fields. The number of columns to display can be customized on a per-module basis with the following steps.
Note: This customization is only applicable for modules in backward compatibility mode.
Resolution
SugarCloud
First, you will want to ensure your layouts are deployed in the custom directory. If you have not previously customized your layouts via Studio, go to Admin > Studio > {Module Name} > Layouts. From there, select each layout you wish to add additional columns to and click 'Save & Deploy'. This action will create a corresponding layout file under the ./custom/modules/{Module Name}/metadata/ directory. The files will be named editviewdefs.php, detailviewdefs.php, and quickcreatedefs.php depending on the layouts deployed.
To access your custom files, go to Admin > Diagnostic Tool, uncheck all the boxes except for "SugarCRM Custom directory" and then click "Execute Diagnostic". This will generate an archive of your instance's custom directory to download, and you will find the layout files in the above path. Open the custom layout file, locate the 'maxColumns' value, and change it to the number of columns you would like to have on screen:
'maxColumns' => '3',
Once that is updated, locate the 'widths' array to define the spacing for your new column(s). You should have a label and field entry for each column in your layout:
'widths' => array (
    0 => array (
        'label' => '10',
        'field' => '30',
    ),
    1 => array (
        'label' => '10',
        'field' => '30',
    ),
    2 => array (


'field' => '30',
    ),
    2 => array (
        'label' => '10',
        'field' => '30',
    ),
),
After this is completed, you will need to create a module-loadable package to install the changes on your SugarCloud instance. More information on creating this package can be found inÂ Creating an Installable Package that Creates New Fields. To upload and install the package, go to Admin > Module Loader.
Note: Sugar Sell Essentials customers do not have the ability to upload custom file packages to Sugar using Module Loader.
Once the installation completes, you can navigate to Studio and add fields to your new column in the layout. For any rows that already contain two fields, the second field will automatically span the second and third column. Simply click the minus (-) icon to contract the field to one column and expose the new column space:
After you have added the desired fields in Studio, click 'Save & Deploy', and you are ready to go!
On-Site
First, you will want to ensure your layouts are deployed in the custom directory. If you have not previously customized your layouts via Studio, go to Admin > Studio > {Module Name} > Layouts. From there, select each layout you wish to add additional columns to and click 'Save & Deploy'. This action will create a corresponding layout file under the ./custom/modules/{Module Name}/metadata/ directory. The files will be named editviewdefs.php, detailviewdefs.php, and quickcreatedefs.php depending on the layouts deployed.
Next, open the custom layout file, locate the 'maxColumns' value, and change it to the number of columns you would like to have on screen:
'maxColumns' => '3',


'maxColumns' => '3',
Once that is updated, locate the 'widths' array to define the spacing for your new column(s). You should have a label and field entry for each column in your layout:
'widths' => array (
    0 => array (
        'label' => '10',
        'field' => '30',
    ),
    1 => array (
        'label' => '10',
        'field' => '30',
    ),
    2 => array (
        'label' => '10',
        'field' => '30',
    ),
),
Once this is completed, you can navigate to Studio and add fields to your new column in the layout. For any rows that already contain two fields, the second field will automatically span the second and third column. Simply click the minus (-) icon to contract the field to one column and expose the new column space:
After you have added the desired fields in Studio, click 'Save & Deploy', and you are ready to go!
  	Last modified: 2023-02-03 21:04:03


Manipulating Layouts Programmatically
Overview
How to manipulate and merge layouts programmatically.
Note: This customization is only applicable for modules in backward compatibility mode.
The ParserFactory
The ParserFactory can be used to manipulate layouts such as editviewdefs or detailviewdefs. This is a handy when creating custom plugins and needing to merge changes into an existing layout. The following example will demonstrate how to add a button to the detail view:
<?php
//Instantiate the parser factory for the Accounts DetailView.
require_once('modules/ModuleBuilder/parsers/ParserFactory.php');
$parser = ParserFactory::getParser('detailview', 'Accounts');
//Button to add
$button = array(
    'customCode'=>'<input type="button" name="customButton" value="Custom Button">'
);
//Add button into the parsed layout
array_push($parser->_viewdefs['templateMeta']['form']['buttons'], $button);
//Save the layout
$parser->handleSave(false);
Â
  	Last modified: 2023-02-03 21:04:03


Hiding the Quotes Module PDF Buttons
Overview
How to hide the PDF buttons on a Quote.
The PDF buttons on quotes are rendered differently than the standard buttons on most layouts. Since these buttons can't be removed directly from the DetailView in the detailviewdefs, the best approach is using jQuery to hide the buttons.
Note: This customization is only applicable for the quotes module as it is in backward compatibility mode.
Hidding the PDF Buttons
This approach involves modifying the detailviewdefs.php in the custom/modules/Quotes/metadata directory to include a custom JavaScript file. If a custom detailviewdefs.php file doesn't exist, you will need to create it through Studio or by manually
        coping the detailviewdefs.php from the Quotes stock module metadata directory.
First, we will create a javascript file, say removePdfBtns.js, in the ./custom/modules/Quotes directory. This javascript file will contain the jQuery statements to hide the Quotes "Download PDF" and "Email PDF" buttons on the DetailView of the Quote.

./custom/modules/Quotes/removePdfBtns.js
SUGAR.util.doWhen("typeof $ != 'undefined'", function(){
    YAHOO.util.Event.onDOMReady(function(){
        $("#pdfview_button").hide();
        $("#pdfemail_button").hide();
    });
});
Next, we will modify the custom detailviewdefs.php file to contain the 'includes' array element in the templateMeta array as follows:
./custom/modules/Quotes/metadata/detailviewdefs.php
$viewdefs['Quotes'] = array (
  'DetailView' =>
  array (
    'templateMeta' =>
    array (
      'form' =>
      array (
        'closeFormBeforeCustomButtons' => true,
        'buttons' =>
        array (
          0 => 'EDIT',
          1 => 'SHARE',


array (
          0 => 'EDIT',
          1 => 'SHARE',
          2 => 'DUPLICATE',
          3 => 'DELETE',
          4 =>
          array (
            'customCode' => '<form action="index.php" method="POST" name="Quote2Opp" id="form">
                    <input type="hidden" name="module" value="Quotes">
                    <input type="hidden" name="record" value="{$fields.id.value}">
                    <input type="hidden" name="user_id" value="{$current_user->id}">
                    <input type="hidden" name="team_id" value="{$fields.team_id.value}">
                    <input type="hidden" name="user_name" value="{$current_user->user_name}">
                    <input type="hidden" name="action" value="QuoteToOpportunity">
                    <input type="hidden" name="opportunity_subject" value="{$fields.name.value}">
                    <input type="hidden" name="opportunity_name" value="{$fields.name.value}">
                    <input type="hidden" name="opportunity_id" value="{$fields.billing_account_id.value}">
                    <input type="hidden" name="amount" value="{$fields.total.value}">
                    <input type="hidden" name="valid_until" value="{$fields.date_quote_expected_closed.value}">
                    <input type="hidden" name="currency_id" value="{$fields.currency_id.value}">
                    <input id="create_opp_from_quote_button" title="{$APP.LBL_QUOTE_TO_OPPORTUNITY_TITLE}"
                        class="button" type="submit" name="opp_to_quote_button"
                        value="{$APP.LBL_QUOTE_TO_OPPORTUNITY_LABEL}" {$DISABLE_CONVERT}></form>',


),
        ),
        'footerTpl' => 'modules/Quotes/tpls/DetailViewFooter.tpl',
      ),
      'maxColumns' => '2',
      'widths' =>
      array (
        0 =>
        array (
          'label' => '10',
          'field' => '30',
        ),
        1 =>
        array (
          'label' => '10',
          'field' => '30',
        ),
      ),
      'includes' =>
      array (
        0 =>
        array (
          'file' => 'custom/modules/Quotes/removePdfBtns.js',
        ),
      ),
      'useTabs' => false,
      'tabDefs' =>
      array (
        'LBL_QUOTE_INFORMATION' =>
        array (
          'newTab' => false,
          'panelDefault' => 'expanded',
        ),
        'LBL_PANEL_ASSIGNMENT' =>
        array (
          'newTab' => false,
          'panelDefault' => 'expanded',
        ),
      ),
    ),
    ...
Finally, navigate to: Admin > Repair > Quick Repair and Rebuild
The buttons will then be removed from the DetailView layouts.
  	Last modified: 2023-02-03 21:04:03


Events
Overview
The Backbone events module is a lightweight pub-sub pattern that gets mixed into each Backbone class (Model, View, Collection, Router, etc.). This means that you can listen to or dispatch custom named events from any Backbone object.
Backbone events should not be confused with aÂ jQuery events, which are used for working with DOM events in an API. Backbone supports an events hash on views that can be used to attach event handlers to DOM using jQuery. These are not Backbone events. This can be confusing because, among other similarities, both interfaces include an on() functionÂ and allow you to attach an event handler. The targets for jQuery events are DOM elements. The target for Backbone events are Backbone objects.
Sidecar classes extend these base Backbone classes. So each Sidecar object (Layouts, Views, Fields, Beans, Contexts, etc.) supports BackboneÂ events.
Existing Backbone Event Catalog
The current catalog of Backbone eventsÂ is supported and triggered by the Sugar application. For example, we can listen to built-in Backbone router events, such as the route event, that is triggered by Sidecar. Try running the following JavaScript code from your browser's console:
SUGAR.App.router.on('route', function(arguments) {
    console.log(arguments);
});
As you click through the Sugar application, each time the router is called, you will see routing events appear in your browser console.
Sidecar Events
Global Application Events
Application events are all triggered on theÂ app.events (SUGAR.App.events) object. Below is a list of application events with a description of when you can expect them to fire. However, please note that these events can be triggered in more than one placeÂ and some events, such as app:sync:error, can trigger events such asÂ app:logout.
Name
Description
app:init


Name
Description
app:init
Triggered after the Sidecar application initializesNote: Initialization registers events, builds out Sidecar API objects, loads public metadata and config, and initializes modules.
app:start
Triggered after the Sidecar application starts
app:sync
Triggered when metadata is being synced with the user interface,Â for example, after login has occurred
app:sync:complete
Triggered after metadata has completely synced
app:sync:error
Triggered when metadata sync fails
app:sync:public:error
Triggered when public metadata sync fails during initialization
app:view:change
Triggered when a new view is loaded
app:locale:change
Triggered when the locale changes
lang:direction:change
Triggered when the locale changes and the direction of the language is different
app:login
Triggered when the "Login" route is called
app:login:success
Triggered after a successful login
app:logout
Triggered when the application is logging out
app:logout:success
Triggered after a successful logout
Bean Events
The following table lists bean object events.
Name
Description
acl:change
Triggered when the ACLs change for that module
acl:change:<fieldName>
Triggered when the ACLs change for a particular field in that module
validation:success
Triggered when bean validation is valid
validation:complete
Triggered when bean validation completes
error:validation
Triggered when bean validation has an error
error:validation:<fieldName>
Triggered when a particular field has a bean validation error
attributes:revert
Triggered when the bean reverts to the previous attributes
Context Events


attributes:revert
Triggered when the bean reverts to the previous attributes
Context Events
The context object is used to facilitate communication between different Sidecar components on the page using events. For example, the button:save_button:click event triggers whenever a user clicks the Save button. The record view uses this event to run Save routines without being tightly coupled to a particular Save button. A list of these contextual events is not plausible because the user interface is continuously changing between versions, and there are many more possibilities based on the views and layouts in each version.
Utilizing Events
Application events can be bound to in any custom JavaScript controller or any JavaScript file loaded into Sugar and included on the page (such as via JSGroupings framework). An example below shows how one could add custom JavaScript code to trigger after the application log out.
./custom/include/javascript/myAppLogoutSuccessEvent.js
(function(app){
    app.events.on('app:logout:success', function(data) {
        //Add Logic Here
        console.log(data);
    });
})(SUGAR.App);
With the custom event JavaScript file written and in place, include it into the system using the JSGroupings extension.
./custom/Extension/application/Ext/JSGroupings/myAppLogoutSuccessEvent.php
foreach ($js_groupings as $key => $groupings) {
    foreach  ($groupings as $file => $target) {
    	//if the target grouping is found
        if ($target == 'include/javascript/sugar_grp7.min.js') {
            //append the custom JavaScript file
            $js_groupings[$key]['custom/include/javascript/myAppLogoutSuccessEvent.js'] = 'include/javascript/sugar_grp7.min.js';
        }
        break;
    }
}


}
        break;
    }
}
Once in place, navigate to Admin > Repair > Rebuild JS Grouping Files. After the JSGroupings are rebuilt, clear your browser cache and the custom JavaScript will now trigger after a successful logout.
  	Last modified: 2023-02-03 21:04:03


Alerts
Overview
The alert view widget, located in ./clients/base/views/alert/, displays helpful information such as loading messages, notices, and confirmation messages to the user.
Methods
app.alert.show(id, options)
The app.alert.show(id, options) method displays an alert message to the user with the options provided.
Parameters
Name
Description
id
The id of the alert message. Used for dismissing specific messages.
options.level
The alert level
options.title
The alert's title, which corresponds to the alert's level
options.messages
The message that the user sees Note: Process alerts do not display messages.
options.autoClose
Whether or not to auto-close the alert popup
options.onClose
Callback handler for closing confirmation alerts
options.onCancel
Callback handler for canceling confirmation alerts
options.onLinkClick
Callback handler for click actions on a link inside of the alert
Default Alert Values
Alert Level
Alert Appearance
Alert Title
info
blue
"Notice"
success
green
"Success"
warning
yellow
"Warning!"
error
red
"Error"
process
loading message
"Loading..."
confirmation
confirmation dialog
"Warning"
Alert Examples
Standard Alert
app.alert.show('message-id', {
    level: 'success',
    messages: 'Task completed!',
    autoClose: true
});
Confirmation Alert
app.alert.show('message-id', {
    level: 'confirmation',
    messages: 'Confirm?',
    autoClose: false,
    onConfirm: function(){
         alert("Confirmed!");
    },
    onCancel: function(){
        alert("Cancelled!");
    }
});
Process Alert
app.alert.show('message-id', {
    level: 'process',
    title: 'In Process...' //change title to modify display from 'Loading...'
});
app.alert.dismiss(id)


});
app.alert.dismiss(id)
The app.alert.dismiss(id) method dismisses an alert message from view based on the message id.
Parameters
Name
Description
id
The id of the alert message to dismiss.
Example
app.alert.dismiss('message-id');
app.alert.dismissAll
The app.alert.dismissAll dismisses all alert messages from view.
Example
app.alert.dismissAll();
Testing in Console
To test alerts, you can trigger them in your browser's developer tools by using the global App variable as shown below:
App.alert.show('message-id', {
    level: 'success',
    messages: 'Successful!',
    autoClose: false
});
  	Last modified: 2023-02-03 21:04:03


Administration Links
Overview
Administration links are the shortcut URLs foundÂ onÂ the AdministrationÂ pageÂ in the Sugar application. Developers can create additional administration linksÂ using the extension framework.
The global links extension directory is located at ./custom/Extension/modules/Administration/Ext/Administration/.Â After a Quick Repair and Rebuild, the PHP files in this directoryÂ areÂ compiled into ./custom/modules/Administration/Ext/Administration/administration.ext.php. Additional information on this can be found in the extensions Administration section of the Extension Framework documentation. The current links defined in the administration section can be found in ./modules/Administration/metadata/adminpaneldefs.php.
Example
The following example will create a new panel on the Admin page:
./custom/Extension/modules/Administration/Ext/Administration/<file>.php
<?php
    $admin_option_defs = array();
    $admin_option_defs['Administration']['<section key>'] = array(
        //Icon name. Available icons are located in ./themes/default/images
        'Administration',

        //Link name label
        'LBL_LINK_NAME',

        //Link description label
        'LBL_LINK_DESCRIPTION',

        //Link URL - For Sidecar modules
        'javascript:void(parent.SUGAR.App.router.navigate("<module>/<path>", {trigger: true}));',

        //Alternatively, if you are linking to BWC modules
        //'./index.php?module=<module>&action=<action>',
    );
    $admin_group_header[] = array(
        //Section header label
        'LBL_SECTION_HEADER',

        //$other_text parameter for get_form_header()
        '',

        //$show_help parameter for get_form_header()
        false,

        //Section links
        $admin_option_defs,


//Section links
        $admin_option_defs,

        //Section description label
        'LBL_SECTION_DESCRIPTION'
    );
To define labels for administration links in the new panel:
./custom/Extension/modules/Administration/Ext/Language/en_us.<name>.php
<?php
    $mod_strings['LBL_LINK_NAME'] = 'Link Name';
    $mod_strings['LBL_LINK_DESCRIPTION'] = 'Link Description';
    $mod_strings['LBL_SECTION_HEADER'] = 'Section Header';
    $mod_strings['LBL_SECTION_DESCRIPTION'] = 'Section Description';
Finally, navigate to Admin > Repair > Quick Repair and Rebuild. The system will then rebuild the extensions and the panel will appear on the Admin page.
  	Last modified: 2023-02-03 21:04:03


Dashlets
Overview
Dashlets are special view-component plugins that render data from a context and make use of the Dashlet plugin. They are typically made up of a controller JavaScript file (.js) and at least one Handlebars template (.hbs).
Hierarchy Diagram
Sugar loads the dashlet view components in the following manner:
Note: The Sugar application's client type is "base". For more information on the various client types, please refer toÂ the User Interface page.
Dashlet Views
The are three views when working with dashlets: Preview, Dashlet View, and Configuration View. The following sections discuss the differences between views.
Preview
The preview view is used when selecting dashlets to add to your homepage. Preview variables in the metadata will be assigned to the custom model variables.
'preview' => array(
    'key1' => 'value1',
),
The values in the preview metadata can be retrieved using:
this.model.get("key1");
Dashlet View
The dashlet view will render the content for the dashlet. It will also contain the settings for editing, removing, and refreshing the dashlet.
Configuration View
The configuration view is displayed when a user clicks the 'edit' option on the dashlet frame's drop-down menu. Config variables in the metadata will be assigned to the custom model variables
'config' => array(
    //key value pairs of attributes
    'key1' => 'value1',
),
The values in the config metadata can be retrieved using:
this.model.get("key1");
Dashlet Example
The RSSÂ feedÂ dashlet, located in ./clients/base/views/rssfeed/, handles the display of RSSÂ feeds to the user. The sections below outline the various files that render this dashlet.
Metadata
The Dashlet view contains the 'dashlets' metadata:
Parameters
Type
Required


The Dashlet view contains the 'dashlets' metadata:
Parameters
Type
Required
Description
label
String
yes
The name of the dashlet
description
String
no
A description of the dashlet
config
Object
yes
Pre-populated variables in the configuration viewNote:Â Config variables in the metadata areÂ assigned to the custom model variables.
preview
Object
yesÂ
Pre-populated variables in the preview
filter
Object
no
Filter for display
TheÂ RSS feedÂ dashlets metadata is located in:
./clients/base/views/rssfeed/rssfeed.php
<?php
/*
 * Your installation or use of this SugarCRM file is subject to the applicable
 * terms available at
 * http://support.sugarcrm.com/Resources/Master_Subscription_Agreements/.
 * If you do not agree to all of the applicable terms or do not have the
 * authority to bind the entity as an authorized representative, then do not
 * install or use this SugarCRM file.
 *
 * Copyright (C) SugarCRM Inc. All rights reserved.
 */
$viewdefs['base']['view']['rssfeed'] = array(
    'dashlets' => array(
        array(
            'label' => 'LBL_RSS_FEED_DASHLET',
            'description' => 'LBL_RSS_FEED_DASHLET_DESCRIPTION',
            'config' => array(
                'limit' => 5,
                'auto_refresh' => 0,
            ),
            'preview' => array(
                'limit' => 5,
                'auto_refresh' => 0,
                'feed_url' => 'http://blog.sugarcrm.com/feed/',
            ),
        ),
    ),
    'panels' => array(
        array(
            'name' => 'panel_body',