Results 1 to 3 of 3

Thread: How to restrict Relate Field selections with custom popups

  1. #1
    edg
    edg is offline Sugar Community Member
    Join Date
    Feb 2007
    Location
    Atlanta, GA
    Posts
    46

    Post How to restrict Relationship and Relate Field selections with custom popups

    Experience has taught me not to trust users to make the right selections. So, while initialFilter provides the ability to pre-define some selection criteria in a Relate Field popup, it does not provide the ability to restrict the user to this criteria on any subsequent search.

    Note: Relate fields can also be used to restrict relationships. See this Developer Tutorial for that functionality: http://www.sugarcrm.com/forums/showthread.php?t=47776

    For limited related record sets, I can change the relate id field to an enum (dropdown) and source it with a function that returns an array of record ids and names that are pushed into options for the select field on the editview form. This solution does not work well when the related record set is potentially large that could use some additional search criteria. Thus, the need for a custom popup that also restricts the related fields to pre-defined criteria.

    To create a custom popup for a relate field that restricts the records a user can select, follow these steps:

    1) Create a javascript file that contains the code to change the onlick function to call your custom popup for the relate field select button.

    2) Add the javascript file to 'includes' parameter of a custom editviews.def.

    3) create the custom popupdefs file for the related module with the restricted selection criteria in the WhereStatement and reference your custom popup_picker template.

    4) create the custom popup_picker file for the related module with the custom metadata filename value as a hidden field.

    In the following example, I created a custom Keyword module that is related to Mobile Marketing Campaign with a many-to-one relationship. The related campaign must be restricted to campaign types of 'Mobile' only. To make this work, I needed to restrict the related Campaign popup to only select campaigns with that campaign_type value - both intially and on any subsequent user searches.

    First, the javascript code - which is added to a file called pkg_Keywords.js in the custom module.

    Code:
      function custom_onload() {
        set_campaign_search();
      }
    
      function set_campaign_search() {
        form = document.getElementById('EditView');
        for (i = 0; i < form.elements.length; i++) {
          if(form.elements[i].name == 'btn_campaign') {
            elem = form.elements[i];
            // note we use a function pointer here and not a quoted function call
            if(window.addEventListener) {
              elem.addEventListener("click", campaign_search, false);
            } else if (window.attachEvent) {
    	  // IE requires both actions to actually reset the onlick value
              elem.attributes['onclick'].value = null;
              elem.attachEvent('onclick', campaign_search);
            } else if (document.getElementById) {
    	  // fail over - assign the quoted javascript function call		
              elem.attributes['onclick'].value = "campaign_search();";
            }
          }
        }
      }
    
    
      function campaign_search(){
      // the last value in the open_popup is the name of the popupdefs file (leave off the .php)
      // the field_to_name array assigns id and name to respective relate field values (campaign_id_c and campaign)
        return open_popup("Campaigns", 600, 400, "", true, false,
        {"call_back_function":"set_return","form_name":"EditView",
         "field_to_name_array":{"id":"campaign_id_c","name":"campaign"}},
         "single", true, "keyword_popupdefs");
      }
    Second, modify or create a custom editviews.def file. In this example, this is a custom module from modulebuilder, so we just can just modify the editviewdefs.php file in the metadata directory to add the include values in the templateMeta.

    Code:
    <?php
    $module_name = 'pkg_Keywords';
    $viewdefs [$module_name] =
    array (
      'EditView' =>
      array (
        'templateMeta' =>
        array (
          'includes' => array(
            array('file' => 'modules/pkg_Keywords/pkg_Keywords.js'),
          ),
          'maxColumns' => '2',
          'widths' =>
          array (
            0 =>
            array (
              'label' => '10',
              'field' => '30',
            ),
            1 =>
            array (
              'label' => '10',
              'field' => '30',
            ),
          ),
        ),
    ....[code snipped]
    ?>
    Third, create the custom popup defs file and add the necessary record selection criteria to the whereStatement and add the templateForm to point to your custom popup_picker html file. SugarCRM expects that this file exist in the module's metadata directory (i.e. "modules/<MODULE_NAME>/metadata"). I'm working on how to modify that reference in an upgrade safe manner to look at the customs directory and will post a followup on that issue.

    The example whereStatement includes a very limited record selection. While I have not tested this with a complex statement, I see no reason why this could not contain subselect statements to allow for cross-module or relationship based selection criteria.

    Code:
    $popupMeta = array('moduleMain' => 'Campaign',
                'varName' => 'CAMPAIGN',
                'orderBy' => 'name',
                'whereClauses' =>
                  array('name' => 'campaigns.name',
                  ),
                'whereStatement' => "campaign_type='Mobile'",
                'searchInputs' =>
                  array('name'),
                'listviewdefs' => array(
                          'NAME' => array(
                            'width' => '20',
                            'label' => 'LBL_LIST_CAMPAIGN_NAME',
                                'link' => true,
                                'default' => true),
                          'CAMPAIGN_TYPE' => array(
                                'width' => '10',
                                'label' => 'LBL_LIST_TYPE',
                                'default' => true),
                          'STATUS' => array(
                            'width' => '10',
                            'label' => 'LBL_LIST_STATUS',
                                'default' => true),
                          'START_DATE' => array(
                                'width' => '10',
                                'label' => 'LBL_LIST_START_DATE',
                                'default' => true),
                          'END_DATE' => array(
                                'width' => '10',
                                'label' => 'LBL_LIST_END_DATE',
                                'default' => true),
                          ),
                'searchdefs'   => array(
                          'name',
                          'campaign_type',
                          'status',
                          'start_date',
                          'end_date'
                          ),
                'templateForm' => 'custom/modules/Campaigns/Popup_keyword_picker.html',
                );
    Finally, create the custom Popup_picker file. The most significant change to this file is the addition of a hidden metadata field with the same value as the last paramter in the custom open_popup function. This forces each subsequent search to use the same custom popupdefs file. Without this value, the standard
    popupdefs file would be referenced and you'd lose the restrictive record selection in the whereStatement.

    In the example, I'm including just the form section of the template. If you compare it to the standard campaign popup, you'll see that I removed the campaign_type for the obvious reason that I did not want the user selecting a different campaign type. Also note the addition of the hidden metadata field.

    Code:
          <form action="index.php" method="post" name="popup_query_form" id="popup_query_form">
          <tr>
                    <td class="dataLabel" nowrap="nowrap">{MOD.LBL_CAMPAIGN_NAME}</td>
    
                    <td class="dataField" nowrap="nowrap"><input type="text" size="20" name="name" class="dataField" value="{NAME}"/></td>
                    <td valign="top" align="right">
                    <input type="submit" name="button" class="button"
                 title="{APP.LBL_SEARCH_BUTTON_TITLE}"
             accessKey="{APP.LBL_SEARCH_BUTTON_KEY}"
                     value="{APP.LBL_SEARCH_BUTTON_LABEL}" />
            </td>
                    <td align="right"><input type="hidden" name="action" value="Popup"/>
                            <input type="hidden" name="metadata" value="keyword_popupdefs"/>
                            <input type="hidden" name="query" value="true"/>
                            <input type="hidden" name="record" value="{RECORD_VALUE}"/>
                            <input type="hidden" name="module" value="{MODULE_NAME}" />
                            <input type="hidden" name="form_submit" value="{FORM_SUBMIT}" />
                            <input type="hidden" name="request_data" value="{request_data}" />
                            <input type="hidden" name="form" value="{FORM}" />
              </form>
    Last edited by edg; 2009-05-13 at 03:56 AM. Reason: Title and Content correction and added highlights

  2. #2
    edg
    edg is offline Sugar Community Member
    Join Date
    Feb 2007
    Location
    Atlanta, GA
    Posts
    46

    Post Re: How to restrict Relationship and Relate Field selections with custom popups

    Personally, I prefer to add files to the custom/modules/<MODULE_NAME> directory for standard SugarCRM modules. Unfortunately, the MVC file view.popup.php expects the popup metadata file referenced in open_popup to exist in the primary modules directory. It also excludes the custom directories when loading the Popup_picker.php file. Here's follow-up on how to change this and use the custom directories for popups.

    A disclaimer: The following modifications are not required to make custom popups work, but if you also prefer all of your SugarCRM customizations in the custom/modules directory, I suggest you customize the two classes necessary to reference this directory.

    The first customization is the addition of an overload class in the file view.popup.php in the custom/include/MVC/View/views directory. Besides correcting for checking the custom directory for the metadata file, the display method anticipates a custom Popup_picker.php file but does not look in the custom directories. For this reason, the display() method in this class needs to be overridden.

    The standard Popup_picker also references the metadata file and fails to look for it in the custom directories. Because the view.popup.php file specifically calls the Popup_picker constructor, this class must be completely overridden (not just overloaded) for custom popup metadata information to exist in the customs directory.

    Also, note that SugarCRM references the Popup_picker.php in some of its modules (i.e. the Email module). If you employ custom popups be certain to check any customizations of Popup_picker.php or Popup.php in the modules.

    Attached are sample code files that provide this custom directory functionality for popups.

    Below are some of the code changes incorporated into these files:

    File: custom/include/MVC/View/views/view.popup.php
    Code:
     require_once('include/MVC/View/views/view.popup.php');
     class CustomViewPopup extends ViewPopup{
      function CustomViewPopup(){
        parent::ViewPopup();
      }
    
      function display(){
        global $popupMeta, $mod_strings;
        if(isset($_REQUEST['metadata']) && strpos($_REQUEST['metadata'], "..") !== false)
          die("Directory navigation attack denied.");
    
    
        if(!empty($_REQUEST['metadata']) && $_REQUEST['metadata'] != 'undefined') { // if custom metadata is requested
          // Modification from original to include custom directory
          if (file_exists('custom/modules/' . $this->module . '/metadata/' . $_REQUEST['metadata'] . '.php'))
            require_once('custom/modules/' . $this->module . '/metadata/' . $_REQUEST['metadata'] . '.php');
          elseif (file_exists('modules/' . $this->module . '/metadata/' . $_REQUEST['metadata'] . '.php'))
            require_once('modules/' . $this->module . '/metadata/' . $_REQUEST['metadata'] . '.php');
        }
    ...(code snipped)...
        }else{
          /* Add the check for a custom Popup_picker in the module AND the custom includes directory.
             The original also did not look to the custom directories for the metadata file
          */
    
         if (file_exists('custom/modules/' . $this->module . '/Popup_picker.php')){
            require_once('custom/modules/' . $this->module . '/Popup_picker.php');
          } elseif(file_exists('modules/' . $this->module . '/Popup_picker.php')){
            require_once('modules/' . $this->module . '/Popup_picker.php');
    
          } elseif(file_exists('custom/include/Popups/Popup_picker.php')){
            require_once('custom/include/Popups/Popup_picker.php');
          } else {
            require_once('include/Popups/Popup_picker.php');
          }
    
          $popup = new Popup_Picker();
          $popup->_hide_clear_button = true;
          echo $popup->process_page();
        }

    File: custom/include/Popups/Popup_picker.php
    Code:
      function Popup_Picker()
      {
        global $currentModule, $popupMeta;
    
        // cn: bug 12269 - directory navigation attack - detect and stop.
        if(isset($_REQUEST['metadata']) && strpos($_REQUEST['metadata'], "..") !== false)
          die("Directory navigation attack denied.");
        if(empty($popupMeta)){
          if(!empty($_REQUEST['metadata']) && $_REQUEST['metadata'] != 'undefined') {
            // if custom metadata is requested
            // Corrected to include custom modules file
    
           if (file_exists('custom/modules/' . $currentModule . '/metadata/' . $_REQUEST['metadata'] . '.php'))
              require_once('custom/modules/' . $currentModule . '/metadata/' . $_REQUEST['metadata'] . '.php');
            elseif(file_exists('modules/' . $currentModule . '/metadata/' . $_REQUEST['metadata'] . '.php'))
              require_once('modules/' . $currentModule . '/metadata/' . $_REQUEST['metadata'] . '.php');
          }
    Attached Files Attached Files
    Last edited by edg; 2009-05-13 at 03:59 AM. Reason: correct typos

  3. #3
    emkay22 is offline Junior Member
    Join Date
    Sep 2011
    Posts
    5

    Default Re: How to restrict Relate Field selections with custom popups

    Hi edg,

    Thanks for your detailed post and explanation.

    However, I've implemented this (though I did it in the main module directory of a custom module and not in the custom directory). It hasn't worked for me. In which file is the custom_onload function called?

    Regards

    NWN

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Similar Threads

  1. Modify links in popups (relate)
    By Mr.X in forum Developer Help
    Replies: 2
    Last Post: 2009-05-13, 02:27 AM
  2. custom relate field
    By cheku0228 in forum Developer Help
    Replies: 2
    Last Post: 2008-08-22, 09:38 AM
  3. Custom Relate field bug fixed
    By kidpollo in forum Help
    Replies: 2
    Last Post: 2007-04-04, 11:48 PM
  4. custom module custom field relate type error
    By kidpollo in forum General Discussion
    Replies: 0
    Last Post: 2007-03-24, 02:14 AM
  5. add custom field relate other fields
    By longnv in forum Developer Help
    Replies: 6
    Last Post: 2007-02-26, 07:33 AM

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •