Sidebar

Using iTop

Creating your iTop

iTop Customization

"How to" examples
DataModel

User Interface

Automation & Ticket management

Portal Customization

:: Version 3.2.0 ::

Form Prefill

Prerequisite: You must be familiar with the Syntax used in Tutorials and have already created an extension.

learning:
Prefill search criterion, prefill an object at creation or on transition
level:
Advanced
domains:
PHP, Automation, Constrain
min version:
2.5.0

Search preset

When you edit a Team and start to add Members, then the search of those Persons is automatically filtered on the Organization of the Team. This was a particular example, which apply on all classes with an organization. When you edit any object A with an Organization field and start to add related objects B, the search of those objects B is automatically filtered with the Organization of the Object A.

Creation preset

If you create a new Caller person while editing a User Request, that Person Organization will be prefilled with the User Request Organization.

This magic is valid in 95% of the cases, but there are always corner cases, which can now be addressed by completing this automatic behavior with some specific one depending on the context. This can be done thanks to…

New API methods

Function Description
PrefillSearchForm(&$aContextParam) to preset fields in the search frame of related objects
PrefillCreationForm(&$aContextParam) to preset fields in an object creation form
PrefillTransitionForm(&$aContextParam) to preset fields in a transition form

You may enter those functions in different situations

  • From a menu / action
  • While adding objects on an Indirect LinkedSet attribute (n:n relationship)
  • From the + button near an ExternalKey attribute while editing another object (1:n relationship)
  • From the edit in line mode when editing a LinkedSet attribute (n:1 relationship)

Prefill Search Form

This is defined on the Source object class to preset fields in each search frame of related objects, so within the same method, depending on the search object class, we will preset different search criteria.

Context Parameter Description
$aContextParam['dest_class'] provide the Searched class
$aContextParam['filter'] provide the filter (DBObjectSearch) object
$aContextParam['user'] provides the login string of the connected user
$aContextParam['origin'] can be either console or portal

Example

class:Contract
/** Example of how on the Contract class, the Search has been modified to search for:
*      Services from the Provider only
*      Contacts and Documents from both the Customer and the Provider **/
public function PrefillSearchForm(&$aContextParam)
{
  // If we want to search for objects of class 'Service' or a sub-class of 'Service'
  if($aContextParam['dest_class'] == 'Service' 
     || is_subclass_of($aContextParam['dest_class'], 'Service'))
  {
    // If 'org_id' is an existing attribute of that searched class 
    // and 'provider_id' of the source Contract is not empty
    if(MetaModel::IsValidAttCode($aContextParam['dest_class'],'org_id') 
       && !empty($this->Get('provider_id')))
    {
        // We remove any criteria set by the default search
        $aContextParam['filter']->ResetCondition();
        // We set a criteria on the 'org_id' of the searched class with the Contract provider value
        $aContextParam['filter']->AddCondition('org_id', $this->Get('provider_id'));
    }
  }
  // If we want to search for objects of class or sub-classes of 'Contact' or 'Document'
  elseif (($aContextParam['dest_class'] == 'Contact'  
          || is_subclass_of($aContextParam['dest_class'], 'Contact'))
          || ($aContextParam['dest_class'] == 'Document' 
          || is_subclass_of($aContextParam['dest_class'], 'Document')))
  {
    // If 'org_id' is an existing attribute of that searched class
    // Defensive programming in case the data model of remote classes is changed.
    if(MetaModel::IsValidAttCode($aContextParam['dest_class'],'org_id'))
    {
 
      $aOrgIds = array();
      if(!empty($this->Get('provider_id'))) $aOrgIds[] = $this->Get('provider_id');
      if(!empty($this->Get('org_id')))      $aOrgIds[] = $this->Get('org_id');
 
      // if 'provider_id' or 'org_id' of the source Contract are not empty
      // This test is not needed when org_id and provider_id are mandatory on the Contract class
      if(count($aOrgIds)>0)
      {
         // We set a criteria on 'org_id' with multiple selected values (IN parameter)
         $aContextParam['filter']->ResetCondition();
         $aContextParam['filter']->AddCondition('org_id', $aOrgIds , 'IN');
      }
    }
  }
}

Prefiltering on a sub-class

class UserRequest extends Ticket
{   /** 
     * Other example, in which we want to limit the CIs search to the Server class 
     * and allow the User to set other Server fields as search criteria
     */
    public function PrefillSearchForm(&$aContextParam)
    {
        if($aContextParam['dest_class'] == 'FunctionalCI')
        {         
            // We change the FunctionalCI search to limit it to Server
            $aContextParam['filter']->ChangeClass('Server');
        }
    }
}

Using →ChangeClass('Server') is much better than →AddCondition('finalclass', 'Server').
With the proposed option the fields usable as search criteria are any Server field, while with the second option they are limited to just those of a FunctionalCI

Prefiltering using a relationship

class UserRequest extends Ticket
{   /** 
     * Other example, in which we want to limit the CIs search to the PC linked to the caller of the User Request
     * SELECT PC JOIN lnkContactToFunctionalCI AS L ON L.functionalci_id=PC.id WHERE L.contact_id = :this->caller_id
     */
    public function PrefillSearchForm(&$aContextParam)
    {
        if($aContextParam['dest_class'] == 'FunctionalCI')
        {         
            // $aContextParam['filter']->ToOQL(); would look like: "SELECT FunctionalCI AS CI WHERE CI.org_id=:this->org_id"
 
            // We filter the FunctionalCI search to limit it to PC
            $aContextParam['filter']->AddCondition('finalclass', 'PC');  
            // We search for lnkContactToFunctionalCI
            $oSearchLnk = new DBObjectSearch('lnkContactToFunctionalCI');
            // We add a condition on that lnk search to limit it to those related to the caller
            $oSearchLnk ->AddCondition('contact_id', $this->Get('caller_id'));
            // Similar to $oSearchLnk = DBSearch::FromOQL("SELECT lnkContactToFunctionalCI AS l WHERE l.contact_id=:this->caller_id ");
            // We join that lnk search with the FunctionalCI search, providing the key for that join
            $aContextParam['filter']->AddCondition_ReferencedBy($oSearchLnk, 'functionalci_id');
 
            // Resulting filter $aContextParam['filter']->ToOQL(); would look like:
            // SELECT FunctionalCI AS CI JOIN lnkContactToFunctionalCI AS l ON l.functionalci_id = CI.id WHERE l.contact_id=:this->caller_id AND CI.org_id=:this->org_id
        }
    }
}

Prefill Creation Form

Prefill fields in the Creation form of a class

  • The automatic mechanism has already done its work, so fields can be preset already, if the object is created from another one.
  • $this reference the current object which is about to be created in Database, if the user submit the form.
Context Parameter Description
$aContextParam['user'] provides the login string of the connected user
$aContextParam['origin'] can be either console or portal
$aContextParam['source_obj'] available only when creating an external key object (for example, a new Team object created from a Person object)

Example 1

In this example, we create a RequestTemplate from a ServiceSubcategory, and we want to not only preset the servicesubcategory_id, which is done automatically, but also the service_id which can be guessed from the servicesubcategory_id

class:RequestTemplate
public function PrefillCreationForm(&$aContextParam)
{
    $id = $this->Get('servicesubcategory_id');
    if ($id != 0)
    {                     
            // Get the original object itself 
            $oSubcategoryObject = MetaModel::GetObject('ServiceSubCategory', $id, false);
 
             // Prefill other data with original object fields value
            $this->Set('service_id',$oSubcategoryObject->Get('service_id'));
    }
}

Example 2

In this example, we create a new Contract and we want to preset the start_date with current date and the provider_id with the user organization.

class:CustomerContract
public function PrefillCreationForm(&$aContextParam)
{
    // Preset the starting date of the contract with the current date 
    // if not already set by Object Copier for eg.
    if(empty($this->Get('start_date')))  { $this->Set('start_date',time());}
 
    // Get the current user as the parameter only provides the login
    $oUser = UserRights::GetUserObject();
    // Preset the Provider of the contract as the organization of the current user 
    // Be cautious ''org_id'' was added to the User class in 2.5.0 only
    $this->Set('provider_id',$oUser->Get('org_id'));
}

Example 3

In this example, we create a new UserRequest and we want to preset the caller id with the contact_id of the connected user.

class:UserRequest
public function PrefillCreationForm(&$aContextParam)
{
        // Get the current user as the parameter only provides the login
        $oUser = UserRights::GetUserObject();
        // Prefill field caller with the contact of user
        $this->SetIfNull('caller_id', $oUser->Get('contactid'));
 
        return true;
}

Internals

When the creation form of a class A object is invoked from a class B object with a AttributeLinkedSet pointing to class A, the Context[“default”] parameter provides the id of the class B object, with the code attribute of the class A ExternalKey pointing to class B: <itop>/pages/UI.php?operation=new&class=RequestTemplate&c[menu]=ServiceSubcategory&default[servicesubcategory_id]=16

Prefill Transition Form

To preset values in a transition form:

  • At this stage the transition is not yet performed.
  • $this reference the current object on which the transition will be applied.
Context Parameter Description
$aContextParam['expected_attributes']['attribute_code'] provides display flags on attributes in the form
$aContextParam['origin'] can be either console or portal
$aContextParam['stimulus'] provide the applied stimulus

This example implements a “pre-assign to me” logic:

  • If I assign or re-assign a ticket, which is not assigned to me and I am part of the team to which this ticket is dispatched, then when I enter the transition form, my name is preset as the 'agent'. I can change it.
class:UserRequest
public function PrefillTransitionForm(&$aContextParam)
{ 
  $oPerson = UserRights::GetContactObject();
  // If we have a Person associated with the current User 
  // and we are in an assignment transition 
  if ($oPerson && ($aContextParam['stimulus'] == 'ev_assign' 
      || $aContextParam['stimulus'] == 'ev_reassign'))
  {
    $iTicketAgentId = $this->Get('agent_id');
    $iPersonId = $oPerson->GetKey();
 
    // If the agent is not already the current user
    if ( $iTicketAgentId != $iPersonId )
    {
      // Check if the current Contact can be assigned to this ticket
      // = There is a team and Contact is part of that team
      $iTicketTeamId = $this->Get('team_id'); 
 
      $oTeamLinkSet = $oPerson->Get('team_list');
      $bIsInTeam = false;
      while($oTeamLink = $oTeamLinkSet->Fetch())
      {
        // We don't use GetKey as it would return the key of the link object
        $iTeam = $oTeamLink->Get('team_id');
        if ($iTicketTeamId == $iTeam)  { $bIsInTeam = true; }
      }
      if($bIsInTeam)
      {
        $this->Set('agent_id',$iPersonId);
        //As we modified a potential MUST CHANGE value, we'll take care of it by removing this flag
        if(array_key_exists('agent_id', $aContextParam['expected_attributes']))
        {
          $aContextParam['expected_attributes']['agent_id'] = ~OPT_ATT_MUSTCHANGE & $aContextParam['expected_attributes']['agent_id'];
        }
      }
    }
  }
}
The last action to “change the flags on the agent_id field”, is made to remove the “must-change” UI control on the agent on the “re-assign” transition (as we changed it programmatically). If you forget it, the UI will ask you to change the agent again and won't let you set yourself.

Known limitations:

  1. When the agent is preset with the user name, it is also done in the details of the ticket below as if the change was done already which is not true (confusing display), if the transition is cancelled, the previous agent is back, as expected.
  2. It is possible for the user to bypass the “must-change” flag in case of preset, by setting back the previous agent. It won't be seen by the UI and “must-change” is not tested on the back-end.
If you prefill a field that's only “mandatory” in the target state, add must_prompt flag to your field in this transition.

Integrate a function within XML

Example of how to declare the above function within XML datamodel definition

    <class id="RequestTemplate" _delta="must_exist">
        <methods>
            <method id="PrefillCreationForm" _delta="define">
                <static>false</static>
                <access>public</access>
                <type>Overload-DBObject</type>
                <arguments>
                    <argument id="1">
                        <type>reference</type>
                        <mandatory>true</mandatory>
                    </argument>
                </arguments>
                <code><![CDATA[public function PrefillCreationForm(&$aContextParam)
              {       // code of the function    }]]>
                </ code>
            </method>
        </methods>
    </class>
3_2_0/customization/form_prefill.txt · Last modified: 2024/09/10 10:25 by 127.0.0.1
Back to top
Contact us