Editing custom layout in Studio

Hello.

I have created a custom view for the Lead object (i.e. not an EditView, DetailView, etc) and I would like to be able to edit the layout using Studio. Having looked at the relevant code, I can see that I need to create a class in custom/modules/Leads called LeadsStudioModule.php, which I have done:

<?php

require_once 'modules/ModuleBuilder/Module/StudioModule.php' ;

class LeadsStudioModule extends StudioModule
{
    public function __construct($module)
    {
        parent::__construct($module);

       $this->sources['createfromcaseviewdefs.php'] = array (
                'name' => 'CREATE', // translate('LBL_EDITVIEW'),
                'type' => 'createfromcaseview', //MB_EDITVIEW,
                'image' => 'EditView',
                'view' => 'createfromcaseview',
       );
    }
}

At this point, my new view appears in the Layout tab for Leads in Studio. However, when I click on it, I get “[FATAL] Action = editLayout with unknown view=createfromcaseview” in the log. This is coming from ModuleBuilderController:


    public function action_editLayout()
    {
        $view = strtolower($_REQUEST [ 'view' ]);
        $found = false;
        //Check the StudioModule first for mapping overrides
        if (empty($_REQUEST [ 'view_package' ])|| $_REQUEST [ 'view_package' ] == "studio") {
            $sm = StudioModuleFactory::getStudioModule($_REQUEST [ 'view_module' ]);
            foreach ($sm->sources as $file => $def) {
                if (!empty($def['type']) && !empty($def['view']) && $def['view'] == $view) {
                    $view = $def['type'];
                }
            }
        }
        if (!$found) {
            switch ($view) {
                case MB_EDITVIEW:
                case MB_DETAILVIEW:
                case MB_QUICKCREATE:
                    $this->view = 'layoutView' ;
                    break ;
                case MB_LISTVIEW:
                    $this->view = 'listView' ;
                    break ;
                case MB_BASICSEARCH:
                case MB_ADVANCEDSEARCH:
                    $this->view = 'searchView' ;
                    break ;
                case MB_DASHLET:
                case MB_DASHLETSEARCH:
                    $this->view = 'dashlet' ;
                    break ;
                case MB_POPUPLIST:
                case MB_POPUPSEARCH:
                    $this->view = 'popupview' ;
                    break ;
                default:
                    $GLOBALS [ 'log' ]->fatal('Action = editLayout with unknown view=' . $_REQUEST [ 'view' ]) ;
            }
        }
    }

This seems wrong to me, as $found is never set, however it may just be that I am not understanding what it is supposed to be doing.

Can anyone shed some light on this?

Thanks,

Carl

Normally people do this the other way around, they create the layouts in Studio, so they can then change the files manually.

Anyway, if you want to understand better what Studio does, you can check which files it is trying to read and write, with this technique:

https://pgorod.github.io/Audit-File-Accesses/

You can try seeing how it handles your layout. You can also try creating a fresh layout from within Studio and seeing which files it creates, maybe your missing one of them.

Thanks for the reply, although I am now rather confused. If I go to Studio, select “Leads” and then “Layouts”, I get a list of the standard layouts (Edit View, Detail View, etc), but I can’t see any way to create a new custom view.

Can you tell me what I am missing?

Thanks,

Carl

It’s possible that there never was a way to edit custom views in Studio, I didn’t think about it before, but that is likely the case.

I guess you’ll have to do everything in code, then, without Studio…

Or maybe try something from “Module builder” instead…

My thinking on this was that it would allow non-technical users to be able to change the screen layout, rather having to change the PHP code.

I think I have managed to divine the correct incantation to do this:

  1. custom/modules/Leads/views/view.createfromcase.php
    Contains the “LeadsViewCreateFromCase” class, which extends the ViewEdit class and contains the view-specific code (e.g. load the related Case record and pre-populate the display fields):

<?php
if (!defined('sugarEntry') || !sugarEntry) {
    die('Not A Valid Entry Point');
}

class LeadsViewCreateFromCase extends ViewEdit
{

    /**
     * @inheritdoc
     */
    public $type = 'createfromcase';

    public function __construct()
    {
        parent::__construct();
    }

    public function preDisplay()
    {
        parent::preDisplay();
        .
        .
        .
    }

}
  1. custom/Extension/modules/Leads/Ext/ActionViewMap/createfromcase.php
    Maps the action “createfromcaseview” to the “createfromcase” view:

<?php

$action_view_map['createfromcaseview'] = 'createfromcase';

  1. custom/modules/Leads/metadata/createfromcaseviewdefs.php
    Defines the layout of the “Create From Case” view:

<?php
$viewdefs ['Leads'] =
array(
  'createfromcaseview' =>
    .
    .
    .
  1. custom/modules/Cases/metadata/detailviewdefs.php
    Overrides the default layout for Case detail view to add a button to go to the “Create From Case” page:

<?php

$viewdefs ['Cases'] =
array(
  'DetailView' =>
  array(
    'templateMeta' =>
    array(
      'form' =>
      array(
        'buttons' =>
        array(
          0 => 'EDIT',
          1 => 'DUPLICATE',
          2 => 'DELETE',
          3 => 'FIND_DUPLICATES',
          4 => array (
            'customCode' => '<input title="{$MOD.LBL_CREATE_LEAD_BUTTON_TITLE}" accessKey="{$MOD.LBL_CREATE_LEAD_BUTTON_KEY}" class="button" onclick="this.form.return_module.value=\'Cases\'; this.form.return_action.value=\'DetailView\'; this.form.return_id.value=\'{$id}\'; this.form.action.value=\'CreateFromCaseView\'; this.form.module.value=\'Leads\' " type="submit" name="CreateFromCase" id="create_from_case_button" value="{$MOD.LBL_CREATE_LEAD_BUTTON_TITLE}">'
          ),
        ),
      ),
    .
    .
    .
  1. custom/Extension/modules/Cases/Ext/Language/en_us.lang.php
    Adds text for new button:

<?php
$mod_strings['LBL_CREATE_LEAD_BUTTON_TITLE'] = 'Create Lead';
$mod_strings['LBL_CREATE_LEAD_BUTTON_KEY'] = 'L';

?>

So far, so normal. This is where the interesting stuff is.

  1. custom/modules/Leads/LeadsStudioModule.php
    Extends StudioModule to include the new viewdef:

<?php

require_once 'modules/ModuleBuilder/Module/StudioModule.php' ;

class LeadsStudioModule extends StudioModule
{
    public function __construct($module)
    {
        parent::__construct($module);

       $this->sources['createfromcaseviewdefs.php'] = array (
                'name' => translate('LBL_CREATE_FROM_CASE_VIEW', 'Leads'),
                'type' => 'createfromcaseview',
                'image' => 'EditView',
                'view' => 'createfromcaseview',
       );
    }
}
  1. custom/modules/ModuleBuilder/controller.php
    Extends standard controller to handle createfromcaseview:

<?php

require_once 'modules/ModuleBuilder/controller.php' ;

class CustomModuleBuilderController extends ModuleBuilderController
{
    public function action_editLayout()
    {
        $view = strtolower($_REQUEST [ 'view' ]);
        //Check the StudioModule first for mapping overrides
        if (empty($_REQUEST [ 'view_package' ])|| $_REQUEST [ 'view_package' ] == "studio") {
            $sm = StudioModuleFactory::getStudioModule($_REQUEST [ 'view_module' ]);
            foreach ($sm->sources as $file => $def) {
                if (!empty($def['type']) && !empty($def['view']) && $def['view'] == $view) {
                    $view = $def['type'];
                }
            }
        }
        switch ($view) {
           case 'createfromcaseview':
                $this->view = 'layoutView' ;
                break ;
            default:
                parent::action_editLayout();
        }
    }
}
  1. custom/modules/Leads/parsers/parser.createfromcaseview.php
    Extends GridLayoutMetaDataParserparser, to parse the viewdef:

<?php

require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;

class Parsercreatefromcaseview extends GridLayoutMetaDataParser {

    public function __construct()
    {
        parent::__construct('createfromcaseview', 'Leads');
    }
}
  1. custom/themes/SuiteP/css/Dawn/style.css
    Add style for button:

.suitepicon-module-createfromcaseview:before {
    content: "\f198";
}
  1. custom/Extension/modules/Leads/Ext/Language/en_us.lang.php
    Add text labels:

<?php
$mod_strings['LBL_CREATE_FROM_CASE_VIEW'] = 'Create From Case';

?>
1 Like

This is VERY cool B-) thanks for sharing here!

Just realised I made a slight mistake. In custom/modules/Leads/views/view.createfromcase.php, you also need:


    public function getEditView()
    {
        parent::getEditView();

        $this->ev->view='createfromcaseview';

        return $this->ev;
    }
2 Likes

This is really helpful. Thanks carbar for the code.

I created my custom EditView but the buttons Save and Cancel were missing. To solve it I called the standard EditView template with in the viewDefs from step 3:

<?php
$viewdefs ['Leads'] =
array(
  'createfromcaseview' =>
  array (
    'templateMeta' => 
    array (
		'form' =>
		array (
			'headerTpl' => 'include/EditView/header.tpl', 
		),
 	.
	.
    	.

The other thing I want to point out is that you can create a direct link from the standard menu by creating a
custom/Extension/modules/Leads/Ext\Menus/menu.php


<?php 
 //WARNING: The contents of this file are auto-generated


if (!defined('sugarEntry') || !sugarEntry) {
    die('Not A Valid Entry Point');
}



global $module_menu;
$module_menu[]=	array("index.php?module=Leads&action&action=CreateFromCaseView","Any Tittle","");	

The only thing I’m struggling now is that SQS (autofill function) on relate fields is not working on custom view. Hope somebody can help me to solve the issue.

Thanks,

AlxGr