User Tools

Site Tools


extensions:itop-object-copier

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
extensions:itop-object-copier [2019/10/08 10:38]
vdumas
extensions:itop-object-copier [2021/07/05 16:33] (current)
Line 2: Line 2:
 ---- dataentry summary ---- ---- dataentry summary ----
 name             : User actions configurator name             : User actions configurator
-description_wiki : Configure ​ user actions to simplify and automate processes. Eg create an incident from a CI.+description_wiki : Configure user actions to simplify and automate processes. Eg create an incident from a CI.
 index_hidden ​    : yes index_hidden ​    : yes
-version ​         : 1.3.2 +version ​         : 1.3.5 
-release_dt ​      : ​2019-03-26 +release_dt ​      : ​2020-12-15 
-itop-version-min : 2.6.0+itop-version-min : 2.5.0 
 +download_hidden ​ : http://​www.combodo.com/​itop-extensions/​combodo-user-actions-configurator-1.3.4-526.zip
 code             : combodo-user-actions-configurator code             : combodo-user-actions-configurator
 state            : stable state            : stable
 product_hidden ​  : included product_hidden ​  : included
-download_url ​    : http://​www.combodo.com/​itop-extensions/​ 
 alias-code_hidden : itop-object-copier alias-code_hidden : itop-object-copier
 alternate-name ​  : Object Copier alternate-name ​  : Object Copier
-module-lists_hidden : itop-object-copier/​1.3.2+module-lists_hidden : itop-object-copier/​1.3.4
 diffusion ​       : iTop Hub diffusion ​       : iTop Hub
 ---- ----
Line 19: Line 19:
 <​related_components>​Other versions of this component:</​related_components>​ <​related_components>​Other versions of this component:</​related_components>​
  
 +===== 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. 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.
Line 46: Line 47:
  
  
-===== Principles ​=====+=== Principles ===
  
 A shortcut is governed by a //rule definition//​. A shortcut is governed by a //rule definition//​.
Line 74: Line 75:
   * Stop watches   * Stop watches
  
-<note warning>Using //​apply_stimulus()// on the //created object// does not work.  +<note warning>File, Images and Stopwatch cannot be copied.</note> 
-//If 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 ''​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//​
-</​note>​ +
-===== Requirements ===== +
- +
-n/a+
  
 +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).
 +<note warning>​Values set by object copier action are **never controlled** against the XML filter.</​note>​
 ===== Revision History ===== ===== Revision History =====
 ^  Release Date  ^  Version ​ ^  Comments ​ ^ ^  Release Date  ^  Version ​ ^  Comments ​ ^
-|  2019-03-26 ​ |  1.3.2  | Fix Issue when copying list with object copier \\         * Fix Custom Date format in Stencils actions |+|  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 | |  2019-01-16 ​ |  1.3.1  | Security hardening |
-|  2018-12-19 ​ |  1.3.0  | New feature enabled: copy of Attachments |+|  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-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 | |  2018-01-26 ​ |  1.2.0  | Add copy_head verb for CaseLog attributes - allow creation of a new ticket from the latest log |
Line 111: Line 113:
 When prompted to select the extensions to install, check "​Object copier"​ in the list of available extensions. When prompted to select the extensions to install, check "​Object copier"​ in the list of available extensions.
  
 +<​note>​If you install iTop **without UserRequest**,​ edit the Configuration file and remove manually ''​rules''​ related to that class.</​note>​
 ===== Configuration ===== ===== Configuration =====
 +
 +==== Fields ====
  
 ^ Setting name ^ Description ^ Example ^ ^ Setting name ^ Description ^ Example ^
Line 124: Line 129:
  
 <note tip>​menu_label,​ form_label and report_label can be localized without the burden of create dictionaries. 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.</​note>​ <note tip>​menu_label,​ form_label and report_label can be localized without the burden of create dictionaries. 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.</​note>​
 +
 +
  
 ==== Actions ==== ==== Actions ====
Line 141: Line 148:
 | append | attcode,​string | Append the string to the attribute. The string can contain placeholder like <​nowiki>​$this->​attcode$</​nowiki>​ (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. | | append | attcode,​string | Append the string to the attribute. The string can contain placeholder like <​nowiki>​$this->​attcode$</​nowiki>​ (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 <​nowiki>​$this->​attcode$</​nowiki>​ (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 <​nowiki>​$this->​attcode$</​nowiki>​ (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 on the written object, attLink is an attribute on the link class that will be set to <​value>​ |+| 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. | | 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 | | 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** - Copy all the attachments from //source// to //​destination//​ |+| clone_attachments | <​none>​ | **New in 1.3.0 - Requires iTop 2.6+** - Copy all the attachments from //source// to //​destination//​ |
  
-===== Configuration example =====+==== Examples ​====
  
 <code php> <code php>
 '​itop-object-copier'​ => array( '​itop-object-copier'​ => array(
- '​rules'​ => array( +    ​'​rules'​ => array( 
- array( +        0 => array( 
- '​source_scope'​ => '​SELECT UserRequest WHERE status IN ("​assigned",​ "​pending"​)',​ + '​source_scope'​ => '​SELECT UserRequest WHERE status IN ("​assigned",​ "​pending"​)',​ 
- '​allowed_profiles'​ => '​Administrator,​Support Agent',​ + '​allowed_profiles'​ => '​Administrator,​Support Agent',​ 
- '​menu_label'​ => 'Issue a change ticket...',​ + '​menu_label'​ => 'Issue a change ticket...',​ 
- '​form_label'​ => 'Issue a change from request %1$s. Please review the description before create the change ticketAfter creation of the change ticket, the request ticket will be automatically updated.', + '​form_label'​ => 'Issue a change from request %1$s [...]', 
- '​report_label'​ => '​Issued from the request %1$s. The request has been updated.',​ + '​report_label'​ => '​Issued from the request %1$s. The request has been updated.',​ 
- '​dest_class'​ => '​Change',​ + '​dest_class'​ => '​Change',​ 
- '​preset'​ => array( + '​preset'​ => array( 
- '​clone(contacts_list,​functionalcis_list,​org_id,​title,​caller_id)',​ + '​clone(contacts_list,​functionalcis_list,​org_id,​title,​caller_id)',​ 
- '​set(description,​Original description:​\n\$this->​description\$)'​+ '​set(description,​Original description:​\n\$this->​description\$)',​
- ), +
- '​retrofit'​ => array( +
- '​copy(id,​ parent_change_id)',​ +
- '​set(private_log,​Issued change $this->​ref$)',​ +
- ),+
  ),  ),
- array( + 'retrofit' => array( 
- 'source_scope'​ => '​SELECT FunctionalCI',​ + 'copy(id, parent_change_id)', 
- '​allowed_profiles'​ => '',​ + 'set(private_log,Issued change $this->​ref$)',
- '​dest_class'​ => '',​ +
- '​preset' => array( +
- 'clone_scalars(*)', +
- 'reset(name)', +
- )+
- 'retrofit'​ => array( +
- ),+
  ),  ),
  ),  ),
 + 1 => array(
 + '​source_scope'​ => '​SELECT FunctionalCI',​
 + '​allowed_profiles'​ => '',​
 + '​dest_class'​ => '',​
 + '​preset'​ => array(
 + '​clone_scalars(*)',​
 + '​reset(name)',​
 + ),
 + '​retrofit'​ => array(
 + ),
 + ),
 +    ),
 ), ),
 </​code>​ </​code>​
 +
 +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. 
 +  * 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
 +
 +<note tip>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 </​note>​
 +
 +==== 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.
 +
 +<code PHP 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',​ $sChildClass))
 +   {
 +      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);​
 +}
 +</​code>​
 +
 +<code PHP 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 (),
 +),
 +</​code>​
 +==== Display menu as button ====
 +<note tip> 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. </​note>​
 +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:
 +{{ :​extensions:​object-copier-menu-out.png?​nolink |}}
 +
 +If the specified menu is not available for a given class, it's silently not displayed
  
 ===== Troubleshooting ===== ===== Troubleshooting =====
Line 192: Line 270:
   * 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 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."​   * 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: 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
 +
 +<code PHP configuration file [enable_line_numbers="​true",​highlight_lines_extra="​14"​]>​
 +   10 => array (
 +        '​source_scope' ​    => '​SELECT UserRequest WHERE status NOT IN (\'​resolved\',​\'​closed\'​)',​
 +        '​allowed_profiles'​ => '​Support Agent,​Administrator',​
 +        '​menu_label' ​      => '​Create incident from user request...',​
 +        '​form_label' ​      => '​Create new incident from %1$s',
 +        '​report_label' ​    => '​Created new incident from %1$s',
 +        '​dest_class' ​      => '​Incident',​
 +        '​preset' ​          => array (
 +          0 => '​clone(description,​title,​org_id,​caller_id,​team_id,​agent_id,​service_id)',​
 +          1 => '​clone(servicesubcategory_id,​ contacts_list,​functionalcis_list,​workorders_list)',​
 +        ),
 +        '​retrofit'​ => array (
 +          0 => '​set(private_log,​Converted to incident $this->​hyperlink()$.)',​
 +          1 => '​set(parent_request_id,​$this->​id$)',​
 +          2 => '​apply_stimulus(ev_autoresolve)',​
 +          3 => '​apply_stimulus(ev_close)',​
 +        ),
 +   ),
 +</​code>​
 +<note important>​**add_to_list(id,​xxxx_list)** can only be used for an AttributeLinkedSetIndirect,​ so not applicable here.</​note>​
 +
 +**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.
 +
 +<code PHP class:​MyObject>​
 +public function DeleteSource($oObject) ​
 +{  $this->​DBDelete();​ }
 +</​code>​
 +<code PHP configuration file>
 +        '​retrofit'​ => array (
 +          0 => '​call_method(DeleteSource)',​
 +        ),
 +</​code>​
 +{{ :​extensions:​object-copier-delete-failing.png?​direct |}}
 +
 +**Q: Can I copy the Request Template (''​service_details''​) while cloning a UserRequest?​ \\
 +A:** 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.
 +
 +<code PHP 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);​
 +}
 +</​code>​
 +
  
extensions/itop-object-copier.1570523938.txt.gz · Last modified: 2019/10/08 10:38 by vdumas

";