Sidebar

Combodo

iTop Extensions

User actions configurator

name:
User actions configurator
description:
Configure user actions to simplify and automate processes. Eg create an incident from a CI.
version:
1.4.4
release:
2023-07-13
itop-version-min:
2.5.0
code:
combodo-user-actions-configurator
state:
stable
alternate-name:
Object Copier
diffusion:
iTop Hub
php-version-max:
PHP 8.1

Features

This extensions aims at improving the end-users productivity. It adds a menu to create an item prefilled with information coming from an existing item.

Amongst the possible usages, you can:

  • Provide a mean to quickly clone CIs (clone all the attributes but still force the user to enter a new name)
  • Provide a shortcut to create a ticket from a Contact that will be the caller of the ticket
  • Provide a shortcut to create a parent ticket (and record the information into the child ticket)
  • Provide a shortcut to create a ticket from a CI (and record the information into the child ticket)
  • Provide a shortcut to create a change ticket from a user request (and record the information into the user request)

Let's use the last example to illustrate the possibilities of the module:

Users have a menu in the Other Actions drop-down menu of a User Request:

Clicking on the menu, opens the standard creation form with prepopulated data:

The user can adjust the values, then create the item. In addition to the standard report, a message indicates that something has been retrofitted back to the source item.

Principles

A shortcut is governed by a rule definition.

The menu is visible if a series of conditions are met:

  • The item being visited is in the scope of the rule (given by an OQL). For instance, you may want to propose the shortcut only for user request being assigned or pending: SELECT UserRequest WHERE status IN ('assigned', 'pending')
  • The current user has a profile allowed for the rule (can be anybody)

Several rules can coexist for a single source object: there will be as many menus as there are rules that match the conditions.

A rule defines which kind of object to create:

  • Either a given class (can be an abstract class, the user will have to select a subclass)
  • Or the exact same class as the source object

A rule specifies what has to be done to preset or prepopulate the form. This is done by the mean of actions. The following kind of actions are available:

  • Set the value of an attribute depending on the value of one or several attributes from the source object.
  • Copy N-N links (eg. Documents on a Ticket, while cloning a Ticket. Documents are not duplicated, just the links are)
  • Copy 1:n link by duplicating the 1-N linked objects (e.g. Server Interfaces, WorkOrders on a Change)
  • Add an N-N link to the source object or to an external key of the source object.

Additionaly, the rule specifies what has to be done to retrofit some information from the created object to the source object.

Limitations

The following types of attributes are currently not handled and therefore cannot be preset (and no error message is given):

  • Blobs
  • Stop watches
  • TagSet (preset but not saved)
  • EnumSet (preset but not saved)
Tags, Files, Images and Stopwatch cannot be copied.

If apply_stimulus() is used in preset, the form is shown with the object id and the relevant buttons depending on the new state but then it fails to “create” the object because it was created already during the stimuli processing

If you copy or set incoherent data in the creation form, this will not be detected. For examples values which do not match the XML filter, a Caller not aligned with the Organization on a UserRequest. It is up to the object copier rule creator to respect the rules. The underlying reason is that iTop does always consider as a valid value, the one which is already set and the values returned by the filter. The reason being that we don't want to remove an existing value in the back of the user. A common example is related Change, which is suppose to be an open change, but the day that Change is closed, we don't want to loose the relationship (this was fixed in 2.4 or 2.5).

Values set by object copier action are never controlled against the XML filter.

Revision History

Release Date Version Comments
2022-05-23 1.4.4 * N°5447 - Deprecate old unreferenced methods that are @deprecated : Object copier
2022-07-11 1.4.3 * compatibility 3.1 - remove import(application/itopwebpage.class.inc.php)
* Add configuration for Copying Mailbox standard to mailbox OAuth
2022-03-22 1.4.2 Remove all deprecated function from iTopExtensions
2021-12-21 1.4.1 Add ES translation
2021-09-29 1.4.0 * Support of iTop 3.0
* Support of icons and tooltips for the “actions” menu items.
2020-12-15 1.3.5 Restore copy of caselog and 1:n relationships, broken by 1.3.2
2020-03-06 1.3.4 Update DE translations
2019-06-12 1.3.3 * copy_scalar now correctly copy lifecycle's state attribute
* Object copier compatibility with iTop before 2.6
2019-03-26 1.3.2 Fix Issue when copying list with object copier
Fix Custom Date format in Stencils actions
2019-01-16 1.3.1 Security hardening
2018-12-19 1.3.0 New feature enabled: copy of Attachments (require iTop 2.6.0 or above)
2018-06-26 1.2.1 ES translation
2018-01-26 1.2.0 Add copy_head verb for CaseLog attributes - allow creation of a new ticket from the latest log
2017-11-13 1.1.9 Fix error displayed in error message or tooltip, about read-only attributes being set.
2017-09-29 1.1.8 Showing action in object details only when target class is writable.
2017-04-04 1.1.7 Case logs : when using set on a case log, the entry was set twice, the HTML formatting was lost, and if the log was first copied from the source, it was broken.
2017-03-27 1.1.6 With some customizations (preset on ticket dates), the refresh of dependent fields was broken. Regression introduced in iTop 2.3
2017-03-23 1.1.5 Fix for XSS vulnerability.
2016-08-09 1.1.4 Fix for compatibility with iTop 2.3.0 (still backward compatible): properly handle the breadcrumb + bug fix when using hidden fields and case logs.
2015-10-05 1.1.3 Fix for compatibility with iTop 2.2.0 (still backward compatible)
2015-09-30 1.1.2 New verb: nullify. Useful for leaving a date or datetime undefined (differs from the verb reset because the default value for such attributes is “now”)
2015-07-01 1.1.1 New verb: call_method. Allows any kind of modification on the target object (and/or the source object)
2015-02-03 1.1.0 Added placeholders $current_date$ and $current_time$. Added the verb apply_stimulus. Exposed some APIs to allow the reuse of actions in other modules like itop-stencils
2014-12-18 1.0.3 Translated the default configuration in french (while keeping the engish version as the default)
2014-07-18 1.0.2 Added placeholders for set() and append(): $current_contact_id$ (already documented) and $current_contact_friendlyname$ (new!)
2014-04-03 1.0.1 Fixes an issue with linksets: the links were correctly created into the clone, but they were deleted from the source object
2014-03-04 1.0.0 First officialy released version

Installation

Use the Standard installation process for this extension.

If you install iTop without UserRequest, edit the Configuration file and remove manually rules related to that class.

Configuration

Fields

Setting name Description Example
source_scope The OQL to define the source objects. The only parameter available is current_contact_id “SELECT UserRequest WHERE status = 'assigned'”
allowed_profiles CSV list of profiles allowing the shortcut. The user must have at least one profile to have the shortcut available. Wrong profiles names are ignored. Set as an empty string to allow the shortcut to anybody. “Administrator,Support Agent”
menu_label Optional: Label or dictionary entry for the new menu entry. It is optional and defaults to “Clone…”
icon new in 1.4.0 Optional, the Font Awesome icon code. See the note about icons below.
menu_tooltip new in 1.4.0 Optional, a tooltip for the action menu item. Can be localised - see note below.
form_label Optional: Label or dictionary entry for the form header. It is optional and defaults to “Cloning %1$s”
report_label Optional: Label or dictionary entry for the report once the object has been created. It is optional and defaults to “Cloned from %1$s”
dest_class Class of the object to create. If empty, it defaults to the class of the source object “Change”
preset Array of actions to preset the object in the creation form. More information below. array()
retrofit Array of actions to retrofit some information from the created object to the source object. More information below. array()
menu_label, menu_tooltip, form_label and report_label can be localized without the burden of creating dictionary entries. To do so, create the settings menu_label/<language_code> (e.g. “menu_label/FR FR”) for each supported language. The setting menu_label will be the default value.
The icons are displayed using the Font Awesome font (already included in iTop). Therefore the icon codes to use are the Font Awesome 4.7 codes
If the icon string contains spaces, it will be used as-is as the class to be applied to the <i> tag. This enables the support of FontAwesome 5 brand icons. For example when Font Awesome 4.7 is used (iTop up to version 2.7), the code for the LinkedIn icon is linkedin, when FontAwesome 5 is used (iTop version 3.0), the code is fab fa-linkedin.

Actions

An action is specified as a string formatted as verb(arg1[,arg2[…]])
The same action can be requested multiple times.

The following verbs are available:

Verb Parameters Description
clone_scalars <none> Copy all the scalar attributes
clone attcode1,attcode2, … Copy the given attributes
reset attcode Reset the attribute to its default value
nullify attcode New in 1.1.2 - Reset the attribute to its null value (will appear to be undefined from the end users perspective)
copy att_to_read,att_to_write Copy from att_to_read to att_to_write
copy_head att_to_read,att_to_write New in 1.1.11 - Copy last entry in the att_to_read caselog attribute to att_to_write. An exception (non blocking) is thrown if att_to_read is not a caselog
append attcode,string Append the string to the attribute. The string can contain placeholder like $this->attcode$ (or $current_contact_id$, $current_contact_friendlyname$, $current_date$, $current_time$). Commas must be escaped with a backslash. Newlines (\n) are allowed. The character set must be utf-8.
set attcode,value Set a value. If the value is a string, it can then contain placeholder like $this->attcode$ (or $current_contact_id$, $current_contact_friendlyname$, $current_date$, $current_time$). Commas must be escaped with a backslash. Newlines (\n) are allowed. The character set must be utf-8.
add_to_list attRead,attWrite,attLink,value attRead is an external key on the read object, attWrite is a N-N link set (AttributeLinkedSetIndirect) on the written object, attLink is an attribute on the link class that will be set to <value>
apply_stimulus stimulus code Applies the given stimulus (saves the object). To be used in retrofit ONLY. Best practice: It is strongly recommanded to set transition mandatory fields as well, otherwise they will stay empty and could break reporting.
call_method function name New in 1.1.1 - Calls the provided method on the written object. Its prototype must be “public function xxxx($oSource)”. The function can send exceptions in case of failure. In such a case, the error message gets displayed in the log/error.log file
clone_attachments <none> New in 1.3.0 - Requires iTop 2.6+ - Copy all the attachments from source to destination

Examples

'itop-object-copier' => array(
    'rules' => array(
        0 => array(
                'source_scope' => 'SELECT UserRequest WHERE status IN ("assigned", "pending")',
                'allowed_profiles' => 'Administrator,Support Agent',
                'menu_label' => 'Issue a change ticket...',
                'form_label' => 'Issue a change from request %1$s [...]',
                'report_label' => 'Issued from the request %1$s. The request has been updated.',
                'dest_class' => 'Change',
                'preset' => array(
                        'clone(contacts_list,functionalcis_list,org_id,title,caller_id)',
                        'set(description,Original description: $this->description$)',
                ),
                'retrofit' => array(
                        'copy(id, parent_change_id)',
                        'set(private_log,Issued change $this->ref$)',
                ),
        ),
        1 => array(
                'source_scope' => 'SELECT FunctionalCI',
                'allowed_profiles' => '',
                'dest_class' => '',
                'preset' => array(
                        'clone_scalars(*)',
                        'reset(name)',
                ),
                'retrofit' => array(
                ),
        ),
    ),
),

In the above examples, 0 and 1 are the identifier of the rules.

  • You can use a string instead of a number, for identifying an object copier rule (allowed characters: space,A-Za-z0-9_=-).
  • Internally the menu UID will be build this way: object_copier_ + the rule id you gave it.
  • So UID of the above menus, we will be: object_copier_0 and object_copier_1
You can also display the hyperlink as a button (next to the “Other actions…” menu) by specifying in the configuration parameter 'shortcut_actions' its UID

Default configuration

Out of the box Configuration
    'itop-object-copier' => array (
       'rules' => array (
          'clone_location' => array (
            'source_scope' => 'SELECT Location',
            'allowed_profiles' => 'Administrator,Configuration Manager',
            'menu_label' => 'Clone...',
            'menu_label/FR FR' => 'Cloner...',
            'menu_tooltip' => 'Clone this location...',
            'menu_tooltip/FR FR' => 'Cloner ce lieu...',
            'icon' => 'fas fa-clone',
            'form_label' => 'Cloning %1$s',
            'form_label/FR FR' => 'Clonage de %1$s',
            'report_label' => 'Cloned from %1$s',
            'report_label/FR FR' => 'Cloné depuis %1$s',
            'dest_class' => 'Location',
            'preset' => array (
              0 => 'clone_scalars(*)',
              1 => 'reset(name)',
            ),
            'retrofit' => array (
            ),
          ),
          'child_userrequest' => array (
            'source_scope' => 'SELECT UserRequest WHERE status NOT IN (\'resolved\',\'closed\')',
            'allowed_profiles' => 'Support Agent,Administrator',
            'menu_label' => 'Create a child request...',
            'menu_label/FR FR' => 'Créer une demande fille...',
            'menu_tooltip' => 'Create a child request of this request...',
            'menu_tooltip/FR FR' => 'Créer une demande fille de cette demande...',
            'icon' => 'fas fa-comment-alt',
            'form_label' => 'Create a child request from %1$s',
            'form_label/FR FR' => 'Créer une demande fille depuis %1$s',
            'report_label' => 'Created from %1$s',
            'report_label/FR FR' => 'Créée depuis %1$s',
            'dest_class' => 'UserRequest',
            'preset' => array (
              0 => 'clone(caller_id,org_id,contacts_list,functionalcis_list)',
              1 => 'copy(id,parent_request_id)',
            ),
            'retrofit' => array (
            ),
          ),
          'userrequest_from_person' => 
          array (
            'source_scope' => 'SELECT Person',
            'allowed_profiles' => 'Support Agent,Administrator',
            'menu_label' => 'Create a user request...',
            'menu_label/FR FR' => 'Créer une demande utilisateur...',
            'menu_tooltip' => 'Create a user request for this person...',
            'menu_tooltip/FR FR' => 'Créer une demande utilisateur pour cette personne...',
            'icon' => 'fas fa-comment-alt',
            'form_label' => 'Create a user request from %1$s',
            'form_label/FR FR' => 'Créer une demande utilisateur pour %1$s',
            'report_label' => 'Created from %1$s',
            'report_label/FR FR' => 'Créée depuis %1$s',
            'dest_class' => 'UserRequest',
            'preset' => 
            array (
              0 => 'copy(org_id,org_id)',
              1 => 'copy(id,caller_id)',
            ),
            'retrofit' => array (
            ),
          ),
          'userrequest_from_ci' => array (
            'source_scope' => 'SELECT FunctionalCI',
            'allowed_profiles' => 'Support Agent,Administrator',
            'menu_label' => 'Create a user request...',
            'menu_label/FR FR' => 'Créer une demande utilisateur...',
            'menu_tooltip' => 'Create a user request for this CI...',
            'menu_tooltip/FR FR' => 'Créer une demande utilisateur pour cet objet...',
            'icon' => 'fas fa-comment-alt',
            'form_label' => 'Create a user request from  %1$s',
            'form_label/FR FR' => 'Créer une demande utilisateur pour %1$s',
            'report_label' => 'Created from %1$s',
            'report_label/FR FR' => 'Créée depuis %1$s',
            'dest_class' => 'UserRequest',
            'preset' => 
            array (
              0 => 'copy(org_id,org_id)',
              1 => 'add_to_list(id,functionalcis_list,impact,Impacted CI)',
            ),
            'retrofit' => array (
            ),
          ),
          'clone_ci' => array (
            'menu_tooltip' => 'Clone this CI...',
            'menu_tooltip/FR FR' => 'Cloner cet élément...',
            'icon' => 'fas fa-clone',
            'source_scope' => 'SELECT FunctionalCI',
            'allowed_profiles' => 'Administrator,Configuration Manager',
            'dest_class' => '',
            'preset' => array (
              0 => 'clone_scalars(*)',
              1 => 'reset(name)',
            ),
            'retrofit' => array (
            ),
          ),
          'userrequest_from_log' => array (
            'source_scope' => 'SELECT UserRequest WHERE status IN (\'closed\')',
            'allowed_profiles' => 'Support Agent,Administrator',
            'menu_label' => 'Create ticket with last log...',
            'menu_label/FR FR' => 'Créer une demande depuis le journal...',
            'menu_tooltip' => 'Create a ticket based on last log entry...',
            'menu_tooltip/FR FR' => 'Créer une demande basée sur la dernière entrée du journal...',
            'icon' => 'far fa-comment-alt',
            'form_label' => 'Create new request from %1$s',
            'form_label/FR FR' => 'Nouvelle demande depuis %1$s',
            'report_label' => 'Created from %1$s',
            'report_label/FR FR' => 'Créée depuis %1$s',
            'dest_class' => 'UserRequest',
            'preset' => array (
              0 => 'clone(caller_id,org_id,contacts_list,functionalcis_list)',
              1 => 'copy(id,parent_request_id)',
              2 => 'copy_head(public_log,description)',
            ),
            'retrofit' => array (
            ),
          ),
        ),
      ),

Example with call_method

Here we want to copy a n:n relation, which would be of a similar nature but from 2 different Classes of links, for example you want to create a Change Ticket from a FunctionalCI and automatically retrieve Contacts associated to CI and link them to the Change.

class:Change
public function CopyContactsFromCI($oSource)
{
   // This method should be called from an object copier action triggered from a FunctionalCI.
   $sSourceClass = get_class($oSource);
   if (!MetaModel::IsParentClass('FunctionalCI', $sSourceClass))
   {
      throw new Exception("Wrong source class '$sSourceClass' : 
        CopyContactsFromCI method should be called by a user action associated to a FunctionalCI!");
   }        
   // Problem: method is called twice, when new CreatedObject is displayed and when it is saved, 
   // resulting in executing the code twice.
   // Work around: don't perform operation when CreatedObject is being saved
   $sOperation = utils::ReadPostedParam('operation');
   if ($sOperation == 'apply_new') {  return;  }
 
   $id = $oSource->GetKey();
   // get the ids of Contacts linked to the SourceObject (a FunctionalCI)
   $sOQL = "SELECT lnkContactToFunctionalCI AS l WHERE l.functionalci_id = :id ";
   $oContactSetSrc = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array(), array('id' => $id));
   // ensure you get also the obsolete Contacts objects, in case the user preference hide them
   $oContactSetSrc->SetShowObsoleteData(utils::ShowObsoleteData());
 
   $oContactSetDst = $this->Get('contacts_list');
   // Copy the Contact linked objects one by one
   while ($oLnkSrc = $oContactSetSrc->Fetch())
   {
       $oLnkDst = MetaModel::NewObject('lnkContactToTicket');
       $oLnkDst->Set('contact_id', $oLnkSrc->Get('contact_id'));
       $oContactSetDst->AddItem($oLnkDst);
   }
   $this->Set('contacts_list', $oContactSetDst);
}
Configuration file
  3 => array (
    'source_scope' => 'SELECT FunctionalCI',
    'allowed_profiles' => 'Support Agent,Administrator',
    'menu_label' => 'Create a change...',
    'form_label' => 'Create a user request from  %1$s',
    'report_label' => 'Created from %1$s',
    'dest_class' => 'Change',
    'preset' => array (
         0 => 'copy(org_id,org_id)',
         1 => 'add_to_list(id,functionalcis_list,impact,Impacted CI)',
         2 => 'call_method(CopyContactsFromCI)',
         ),
    'retrofit' => array (),
),

Display menu as button

If you want to display a menu as a separate button, outside of the Other actions menu, use the shortcut_actions parameter in the Configuration file.

Example: to display the Clone… menu (rule id=1) in the toolbar after the New… and Modify.. buttons, set this:

  'shortcut_actions' => 'UI:Menu:Modify,UI:Menu:New,object_copier_1',

The result will look like this:

If the specified menu is not available for a given class, it's silently not displayed

Troubleshooting

All errors are logged to the file log/error.log

The behavior from the end-user perspective is:

  • When the parameters source_scope is wrong, the rule is ignored
  • When dest_class is not a valid class, the rule is ignored
  • When source_scope/dest_class/allowed_profiles/preset/retrofit are missing, the rule is ignored
  • When the list of profiles contain wrong names, those names are simply ignored, and if all the names in the list are wrong then no one will have the menu to clone the object.
  • When a preset action is wrong (syntaxically or grammatically), the preset is stopped at that point, the form is displayed with a message explaining the issue: “An error has been encountered while presetting the object to create: <error message>. Please contact your administrator.” The user can proceed with the object creation.
  • When a retrofit action is wrong, the retrofit is stopped at that point, the operation completes (object created as expected) and the report contains: “An error has been encountered while retrofitting some information back to the source object: <error message>. Please contact your administrator.”

Question & Answers

Q: When I try to copy a LinkedSet attributes such as contacts_list from a Ticket to a FunctionalCI, it fails, why?
A:
Also the attribute contacts_list may look similar on a Ticket, a FunctionalCI, in reality those sets contain objects which are absolutely different, they do not belong to the same class: lnkContactToTicket versus lnkContactToFunctionalCI, which is a mandatory requirement for the copy to work.
The workaround to associate all contacts linked to the Ticket, to the FunctionalCI while creating it, is to use a PHP method as explained above.

Q: When I try to create a Ticket from another one, it first prefill the form but on submission fails and empty all fields, why?
A:
A possible cause can be that you are trying to set the status field which is controlling the lifecycle of the created ticket. This is not allowed as this field is read only and can only be changed through the application of a stimulus, which will execute the controls and actions associated with that transition. The error message is not very explicit

Q: Can I add the current object to a LinkedSet attribute of the created object?
A:
Yes, this can be done in the retrofit.

Here an example in Simple Request Management, which create an Incident from a UserRequest and link them together in line 14

configuration file

  1. 10 => array (
  2. 'source_scope' => 'SELECT UserRequest WHERE status NOT IN (\'resolved\',\'closed\')',
  3. 'allowed_profiles' => 'Support Agent,Administrator',
  4. 'menu_label' => 'Create incident from user request...',
  5. 'form_label' => 'Create new incident from %1$s',
  6. 'report_label' => 'Created new incident from %1$s',
  7. 'dest_class' => 'Incident',
  8. 'preset' => array (
  9. 0 => 'clone(description,title,org_id,caller_id,team_id,agent_id,service_id)',
  10. 1 => 'clone(servicesubcategory_id, contacts_list,functionalcis_list,workorders_list)',
  11. ),
  12. 'retrofit' => array (
  13. 0 => 'set(private_log,Converted to incident $this->hyperlink()$.)',
  14. 1 => 'set(parent_request_id,$this->id$)',
  15. 2 => 'apply_stimulus(ev_autoresolve)',
  16. 3 => 'apply_stimulus(ev_close)',
  17. ),
  18. ),
add_to_list(id,xxxx_list) can only be used for an AttributeLinkedSetIndirect, so not applicable here.

Q: Can I delete the source object after cloning?
A:
You can invoke in the retrofit, a method to delete the source object, it will do the job, but it displays a red top bar error message, because the retrofit process does not expect the Source object to be gone in the middle of its work. There is no real issue but the error can be misleading for users who read it.

class:MyObject
public function DeleteSource($oObject) 
{  $this->DBDelete(); }
configuration file
        'retrofit' => array (
          0 => 'call_method(DeleteSource)',
        ),

Q: Can I copy the Request Template (service_details) while cloning a UserRequest?
A: No
. It is currently not working correctly.

Q: Can I copy the Caselogs while cloning a Ticket?
A:
It is covered by this extension, except for versions between 1.3.2 and 1.3.4.
In 1.3.5, there is an known issue, it duplicates the entry made during the cloned object creation.

Q: Can I copy the WorkOrders while cloning a Ticket?
A:
It is covered by this extension, except for versions between 1.3.2 and 1.3.4.

So here is a workaround: you can write a method to do it.

  • Limitations: Sub-objects are copied in the background on form submission. You will have to modify or delete those that are not applicable after.
Class::Ticket
public function CloneWorkorders($oSource)
{
    // Method called by Object-Copier
    // Change those 2 strings to match your needs
    $sAllowedSourceClass = 'Ticket'; // Class of the SourceObject
    $sSetAttCode = 'workorders_list'; // Attribute code of the n-1 relation to copy
 
    if (!MetaModel::IsValidAttCode(get_class($this), $sSetAttCode))
    {
        throw new Exception(__METHOD__.":Unknown attribute ".get_class($this)."::".$sSetAttCode);
    }
    if (!MetaModel::IsParentClass($sAllowedSourceClass, get_class($oSource)))
    {
        throw new Exception(__METHOD__.":Wrong source class 'get_class($oSource)' : 
            This method should be called by a user action associated to a '$sAllowedSourceClass'");
    }
    // Check that AttributeDef is an AttributeLinkedSet
    $oSourceAttDef = MetaModel::GetAttributeDef(get_class($oSource), $sSetAttCode);
    if (is_object($oSourceAttDef) && !$oSourceAttDef->IsLinkSet())
    {
        throw new Exception(__METHOD__.":Attribute ".$sSetAttCode
                ." is not a LinkSet on class ".get_class($this));
    }
    // To avoid confusing the user, we apply this only on submission
    // Otherwise the copied objects seems to appear on the form, but adding or removing some is ignored.
    $sOperation = utils::ReadPostedParam('operation');
    if ($sOperation != 'apply_new')
    {
        return;
    }
 
    $oDestSet = $this->Get($sSetAttCode);       
    $oSet = $oSource->Get($sSetAttCode);
 
    while($oSourceObject = $oSet->Fetch())
    {         
        $sClass = get_class($oSourceObject);
        $oClone = MetaModel::NewObject($sClass);
        foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
        {
            // As of now, ignore other attribute than scalars
            if ($oAttDef->IsScalar() && $oAttDef->IsWritable())
            {
                $oClone->Set($sAttCode, $oSourceObject->Get($sAttCode));
            }
        }
        $oDestSet->AddItem($oClone);
    }
    $this->Set($sSetAttCode,$oDestSet);
}
extensions/itop-object-copier.txt · Last modified: 2024/02/27 10:09 (external edit)
Back to top
Contact us