Sidebar

Using iTop

Creating your iTop

iTop Customization

"How to" examples
DataModel

User Interface

Automation & Ticket management

Portal Customization

Force a field to be mandatory on condition

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

learning:
Force a field to be filled on condition
level:
Advanced
domains:
PHP, Constrain
methods:
AddAttributeFlags, AddInitialAttributeFlags, ForceAttributeFlags, ForceInitialAttributeFlags, IsValidAttcode, DoCheckToWrite
min version:
2.3.0

This use case is just one way of forcing a field to be provided.

Check before Submission

This method can force a field to be provided, on the Console, CSV import and in the Portal
But it does not work for DataSynchro and REST/JSON API.
Warning, setting attribute flags is computed at form opening, it is not recompute on the fly.
As a result in the below example, a change of status, will not immediately force the location to be mandatory.

Only Server in production

In this use case, we want to ensure that

  1. At creation, no location is proposed
  2. On modification of a Server in production, the location becomes mandatory.
itop_design / classes
    <class id="PhysicalDevice">
      <fields>
        <field id="location_id">
          <!-- force location_id flags to be recomputed if status is changed -->
          <dependencies _delta="redefine">
            <attribute id="org_id"/>
            <attribute id="status"/>
          </dependencies>
        </field>
      </fields>
    </class>
    <class id="Server" _delta="must_exist">
      <event_listeners>
        <event_listener id="evtSetInitialFlags" _delta="define">
          <!-- Id of the event does not have to be the same as the function, but why not -->
          <event>EVENT_DB_SET_INITIAL_ATTRIBUTES_FLAGS</event>
          <rank>10</rank>
          <!-- The callback must be the name of an existing class method. The name is free -->
          <callback>evtSetInitialFlags</callback>
        </event_listener>
        <event_listener id="evtSetAttributeFlags" _delta="define">
          <event>EVENT_DB_SET_ATTRIBUTES_FLAGS</event>
          <rank>10</rank>
          <callback>evtSetAttributeFlags</callback>
        </event_listener>
      </event_listeners>
      <methods>
        <method id="evtSetInitialFlags" _delta="define">
          <static>false</static>
          <access>public </access>
          <comment>This method is called once, before opening the Server creation form</comment>
          <code><![CDATA[
public function evtSetInitialAttributeFlags(Combodo\iTop\Service\Events\EventData $oEventData)
{   // Force the flags, ignoring existing ones    
    $this->ForceInitialAttributeFlags('location_id', OPT_ATT_HIDDEN); 
    // $this->AddInitialAttributeFlags('location_id', OPT_ATT_HIDDEN); might not do the work if some other code forces it to be mandatory as well
}
          ]]></ code>
        </method>
        <method id="evtSetAttributeFlags" _delta="define">
          <static>false</static>
          <access>public </access>
          <comment>This method is called once, before opening a Server modification form</comment>
          <code><![CDATA[
public function evtSetAttributeFlags(Combodo\iTop\Service\Events\EventData $oEventData)
{              
   if ((MetaModel::IsValidAttCode(get_class($this), 'status')) && ($this->Get('status') == 'production'))
   {  // Adding the mandatory flag
      $this->AddAttributeFlags('location_id', OPT_ATT_MANDATORY);
      // You can decide that you want to set this flag and ignore what other flags already set for this attribute
      // In which case instead of AddAttri... use ForceAttri...
      $this->ForceAttributeFlags('location_id', OPT_ATT_MANDATORY);
   }
}
          ]]></ code>
        </method>
      </methods>
    </class>

Only Server and Network Device in production

Declare the above events and methods twice, one on each class Server and NetworkDevice

Only Datacenter Devices in production

Declare the above events and methods on class DatacenterDevice instead of Server


Check on Submission

We can also check on submission that a location was provided, if not, we prevent the creation/modification and display an error message so the user can fill the Location and submit again.

This method can force a field to be provided, on the Console, CSV import, in the Portal, in DataSynchro and REST/JSON API.

We will subscribe to event EVENT_DB_CHECK_TO_WRITE, and defined a callback method evtCheckToWrite() of the object class:

  • This event is generated just before writing to database - See details of call stack.
  • The callback method should provide error message(s) if it encounters data incoherence.
    • Errors messages are generated using $this->AddCheckIssues('Some Error Message'),
    • Warnings messages use $this->AddCheckWarnings('Some Waring Message'),
  • When returning from this method, if there is at least one error the object is not written to database (creation or update)
  • Error and warning messages are
    • displayed to the user in interactive mode only: Console, Portal, CSV import
    • logged in itop/log/error.log depending on level of tracking for DataSynchro, REST/JSON, CLI

Migration: No visible effect on setup, but objects not compliant can no more be modified, until they are made compliant. So it could prevent a datasynchro or a REST/JSON script to update other fields for eg.
To identify faulty objects, create an audit rule to retrieve objects not compliant to this new constrain and fix them one by one in the UI or by CSV import.

AddCheckIssue() method in the callback of an EVENT_DB_CHECK_TO_WRITE prevents creation/modification in all cases: on the Console, in the Portal, in CSV import, in DataSynchro and in REST/JSON API
Do not Set values on current object in the EVENT_DB_CHECK_TO_WRITE callback method, it has no effect

Before iTop 3.1.0 you could use the Extensibility API and put that same code into iApplicationObjectExtension::OnCheckToWrite()

In this use case we want to prevent a Server to be put in 'production' status without a Location to be provided.

itop_design / classes
    <class id="Server" _delta="must_exist">
      <event_listeners>
        <event_listener id="evtCheckToWrite" _delta="define">
          <!-- Id of the event does not have to be the same as the function, but why not -->
          <event>EVENT_DB_CHECK_TO_WRITE</event>
          <rank>10</rank>
          <!-- The callback must be the name of an existing class method. The name is free -->
          <callback>evtCheckToWrite</callback>
        </event_listener>
      </event_listeners>
class:Server
public function evtCheckToWrite(Combodo\iTop\Service\Events\EventData $oEventData)
{
   // Defensive programming, ensuring that 'status' is an existing field on the current class
   // then checking the condition: an enum value returns code, not label, so we test the code,
   if (MetaModel::IsValidAttCode(get_class($this), 'status')
   && ($this->Get('status') == 'production'))
   {
      // AttributeExternalKey are never NULL, O is the value used when empty
      if (MetaModel::IsValidAttCode(get_class($this), 'location_id')
      && ($this->Get('location_id') == 0))
      {
         // 'Server:Error:LocationMandatoryInProduction' must be declared as a dictionary entry
         $this->AddCheckIssues(Dict::Format('Server:Error:LocationMandatoryInProduction'));
      }
   }
}
         // You may also provide a simple error message in plain text
         $this->AddCheckIssues('Location is mandatory for all Servers in production');

Here the way to define a dictionary entry in XML:

itop_design / dictionaries / dictionary@EN US / entries
    <entry id="Server:Error:LocationMandatoryInProduction" _delta="define">
      <![CDATA['Location is mandatory for all Servers in production']]>
    </entry>
latest/customization/mandatory-field-on-condition.txt ยท Last modified: 2025/05/16 15:20 by 127.0.0.1
Back to top
Contact us