locked
Open a form (popout) from a console task RRS feed

  • Question

  • Hello,

    An analyst launches new incident form, they sometimes may need to create an associated work item called "RMA" (custom work item). I created a console task (called "Create RMA") that creates a new instance of my custom class (RMA) and associates it with the open Incident it was launched from. However, although it creates the new work item and associates it to the incident correctly, the analyst is still forced to manually find the incident to open it up and fill out the details (either through my custom view that displays all items of my custom RMA class, or by saving the associated incident, reopening it, and going to the related items tab to open up the associated RMA).

    I need my script to be able to open the custom form it creates so the analyst can fill out the details quickly versus having to manually locate and open the created object.

    Here is my script:

    # Creates a variable called IncidentID and points Incident # to it for use within the script
    Param(
    [string]$IncidentID
    )
    
    # Load the SMlets module
    Import-Module SMlets 
    
    # Get the Incident Class
    $IncClass = Get-SCSMClass -Name System.WorkItem.Incident$
    
    # Get the RMA Class
    $RMAClass = Get-SCSMClass -Name Flexity.RMA.Class
    
    # Build the Filter String
    $FilterStr = "ID -eq " + $IncidentID
    
    # Find the Incident we need to link to an RMA
    $Inc = Get-SCSMObject -Class $IncClass -Filter $FilterStr
    $RMAIncText = "[Linked to Incident " + $Inc.ID + "]"
    $RMADescription = $RMAIncText 
    New-SCSMObject -Class $RMAClass -PropertyHashtable (@{Title = $Inc.Title; Description = $RMADescription})
    
    # Find the new RMA to be linked
    $FilterStr = "Description -eq '$RMADescription'"
    $RMA = Get-SCSMObject -Class $RMAClass -Filter $FilterStr
    
    #Set RMA Number Variable
    $RMANumber = $RMA.RMA_ID; 
     
    #Clean up DisplayName, Title and Description  
    $RMA | Set-SCSMObject -PropertyHashtable @{"DisplayName"  =  $RMANumber; "Title" =  $RMANumber; "Description" =  $RMANumber;   }
    
    ## Create an Incident Related Items instance referencing the new RMA
    $RWIClass = Get-SCSMRelationshipClass -Name System.WorkItemRelatesToWorkItem$
    New-SCSMRelationshipObject -Relationship $RWIClass -Source $Inc -Target $RMA -Bulk
    
    # Unload the SMlets module
    Remove-Module SMlets
    

    If possible I would like to avoid modifying my custom form's dll assembly in Visual Studio and prefer to add the code directly to my script or via the console task. Thanks in advance.


    • Edited by SirLearnAlot Tuesday, August 5, 2014 5:39 PM Code not displayed properly
    Tuesday, August 5, 2014 5:38 PM

Answers

  • Hello SirLearnAlot,

    first you must add to your assembly a c# class as below:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.EnterpriseManagement.UI.FormsInfra;
    using Microsoft.EnterpriseManagement.UI.SdkDataAccess;
    using Microsoft.EnterpriseManagement.ConsoleFramework;
    using Microsoft.EnterpriseManagement.GenericForm;
    using Microsoft.EnterpriseManagement.UI.DataModel;
    using System.Windows;
    using Microsoft.EnterpriseManagement.UI.WpfWizardFramework;
    using Microsoft.EnterpriseManagement.UI.Extensions.Shared;
    using Microsoft.EnterpriseManagement.ServiceManager.Application.Common;
    
    
    namespace <YourProjectNameSpace>
    {
        class LaunchRelatedRMA: CreateWithLinkHandler
        {
            public LaunchRelatedRMA()
            {
                try
                {
    //below unsealed and sealed you must add your RMA class guid, you can find it in your ServiceManagerDatabase under
    //Select * from ManagedType order by TypeName desc
    //Just change the guid in this code with the guid of your class
    //try to use your sealed class guid, this way you don't get a warning each time you click the task
    
                    //unsealed
                    //this.createClassGuid = new Guid("679B286E-DAB1-8CDA-A61E-30018BE5E336");
    
                    //sealed
                    this.createClassGuid = new Guid("174B27AE-3E11-31D3-FD91-132EA1804CBA");
    
                    this.classToDelegate = new Dictionary<Guid, CreateLinkHelperCallback>()
                    {
    					{ ApplicationConstants.WorkItemTypeId, new CreateLinkHelperCallback(this.WorkItemCallback) }
    				};
                }
                catch (Exception exc1)
                {
                    MessageBox.Show(exc1.Message,"Exception",MessageBoxButton.OK,MessageBoxImage.Error);
                }
            }
    
    //newItem is the RMA you're about to create
    //linkItem is the incident you launched the task from
    
            public void WorkItemCallback(IDataItem newItem, IDataItem linkItem)
            {
                try
                {
    //make sure you have an alias in your xml type projection called RelatedWorkItems if not check the XML part after this c# code
                    if (newItem != null && newItem.HasProperty("RelatedWorkItems"))
                    {
    //Relate the RMA to the Incident
                        newItem["RelatedWorkItems"]= linkItem;
    //Copy the incident title to the RMA title
                        newItem["Title"] = linkItem["Title"];
    //Copy the incident Description to the RMA description
                        newItem["Description"] = linkItem["Description"];
                    }
                }
                catch (Exception exc2)
                {
                    MessageBox.Show(exc2.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }
    }
    

    Now in your class xml you have to make sure that RelatedWorkItems type projection alias exists as below under the TypeProjection tag:

    <Component Path="$Target/Path[Relationship='Alias_40d1e04c_d5af_48ff_b596_3f008b03466b!System.WorkItemRelatesToWorkItem']$" Alias="RelatedWorkItems">
                <Component Path="$Target/Path[Relationship='Alias_40d1e04c_d5af_48ff_b596_3f008b03466b!System.WorkItemAffectedUser']$" Alias="RWIAffectedUser" />
                <Component Path="$Target/Path[Relationship='Alias_40d1e04c_d5af_48ff_b596_3f008b03466b!System.WorkItemAssignedToUser']$" Alias="RWIAssignedUser" />
              </Component>

    and a console task has been created as below:

    <ConsoleTasks>
          <ConsoleTask ID="CreateRelatedRMA" Accessibility="Public" Enabled="true" Target="CoreIncident!System.WorkItem.Incident" RequireOutput="false">
            <Assembly>Console!SdkDataAccessAssembly</Assembly>
            <Handler>Microsoft.EnterpriseManagement.UI.SdkDataAccess.ConsoleTaskHandler</Handler>
            <Parameters>
              <Argument Name="Assembly"><!--your assembly name--></Argument>
              <Argument Name="Type">YourProjectNamespace.LaunchRelatedRMA</Argument>
            </Parameters>
          </ConsoleTask>
        </ConsoleTasks>

    and in your xml reference, 

    Alias_40d1e04c_d5af_48ff_b596_3f008b03466b

      refers to:

    <Reference Alias="Alias_40d1e04c_d5af_48ff_b596_3f008b03466b">
            <ID>System.WorkItem.Library</ID>
            <Version>7.5.1561.0</Version>
            <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
          </Reference>

    and

    CoreIncident

    refers to:

    <Reference Alias="CoreIncident">
            <ID>System.WorkItem.Incident.Library</ID>
            <Version>7.5.1561.0</Version>
            <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
          </Reference>

    Best Regards,

    Georges Mouawad

     

    Jreij12

    • Marked as answer by SirLearnAlot Wednesday, August 27, 2014 7:43 PM
    Wednesday, August 27, 2014 5:27 AM

All replies

  • Dear SirLearnAlot,

    it is better to use Visual Studio and create what you need from your assembly.

    I did that with a custom form I had created, I launched my custom form and related the incident to the instance of my custom class through a console task on the incident form called "Create Related RMA" let's say.

    If you need my sample code let me know.

    Georges Mouawad


    Jreij12

    Monday, August 11, 2014 6:59 AM
  • Hello Georges, Thomas,

    It has been awhile I have been busy working on my solution. So I have gotten up to the point now where I have added the code inside my assembly to open the form, and added the reference inside my MP XML to launch the console task from my assembly, but I am missing one last thing. I need to somehow launch my script from my assembly code, or (preferably) integrate my script code inside my assembly to make it all one file. Any ideas would be greatly appreciated. My code is below:

    Script that does the linking and renaming:
    
    # Creates a variable called IncidentID and points Incident # to it for use within the script
    Param(
    [string]$IncidentID
    )
    
    # Load the SMlets module
    Import-Module SMlets 
    
    # Get the Incident Class
    $IncClass = Get-SCSMClass -Name System.WorkItem.Incident$
    
    # Get the RMA Class
    $RMAClass = Get-SCSMClass -Name Flexity.RMA.Class
    
    # Build the Filter String
    $FilterStr = "ID -eq " + $IncidentID
    
    # Find the Incident we need to link to an RMA
    $Inc = Get-SCSMObject -Class $IncClass -Filter $FilterStr
    $RMAIncText = "[Linked to Incident " + $Inc.ID + "]"
    $RMADescription = $RMAIncText 
    New-SCSMObject -Class $RMAClass -PropertyHashtable (@{Title = $Inc.Title; Description = $RMADescription})
    
    # Find the new RMA to be linked
    $FilterStr = "Description -eq '$RMADescription'"
    $RMA = Get-SCSMObject -Class $RMAClass -Filter $FilterStr
    
    #Set RMA Number Variable
    $RMANumber = $RMA.RMA_ID; 
     
    #Clean up DisplayName, Title and Description  
    $RMA | Set-SCSMObject -PropertyHashtable @{"DisplayName"  =  $RMANumber; "Title" =  $RMANumber; "Description" =  $RMANumber;   }
    
    ## Create an Incident Related Items instance referencing the new RMA
    $RWIClass = Get-SCSMRelationshipClass -Name System.WorkItemRelatesToWorkItem$
    New-SCSMRelationshipObject -Relationship $RWIClass -Source $Inc -Target $RMA -Bulk
    
    # Unload the SMlets module
    Remove-Module SMlets

    Assembly Code:
    
       public class RMATask : ConsoleCommand
        {
            public RMATask()
            {
            }
            public override void ExecuteCommand(IList<NavigationModelNodeBase> nodes, NavigationModelNodeTask task, ICollection<string> parameters)
            {
                IManagementGroupSession session = (IManagementGroupSession)FrameworkServices.GetService<IManagementGroupSession>();
                EnterpriseManagementGroup emg = session.ManagementGroup;
    
                ManagementPack mp = emg.ManagementPacks.GetManagementPack(new Guid("a82d62c5-ece0-35fd-a266-9afa246dea78"));
                ManagementPackClass mpc = emg.EntityTypes.GetClass(new Guid("4b081ab1-f48e-9c62-77bc-76bc31349030"));
                ManagementPackObjectTemplate mpt = emg.Templates.GetObjectTemplate(new Guid("92ed7c4d-aff5-819e-90f8-c92064c50cd6"));
    
                NavigationModelNodeBase nodeIn = nodes[0];
    
                NavigationModelNodeBase nmnbNew;
                NavigationModelNodeTask nmntNew = NavigationTasksHelper.CreateNewInstanceLink(mpc, mpt);
                Microsoft.EnterpriseManagement.GenericForm.GenericCommon.MonitorCreatedForm(nodeIn, nmntNew, out nmnbNew);
            }
        }

    Thanks in advance.

    Wednesday, August 20, 2014 1:22 PM
  • Dear SirLearnAlot,

    as I see and correct me if I'm wrong, you want to add your powershell script code to your assembly c# code.

    The best way to fulfill your request, is to convert your ps1 code where you used the smlets module to a c# code using scsm sdk.

    With SDK you can do lots of powerful things.

    if you need any help, let me know.

    Best Regards

    Georges Mouawad


    Jreij12

    Thursday, August 21, 2014 5:24 AM
  • Hello Georges,

    Yes please! Any help is appreciated as I have been learning this stuff as I go along, I am no expert!

    I referred to @Aarons method in the post @Thomas Bianco posted, and although I was able to conceptually grasp what the code was doing I did not understand it enough to be able to adapt his implementation in my code.

    Thanks in advance for your help!

    Thursday, August 21, 2014 3:29 PM
  • Does anyone else know how to do what Georges is suggesting?

    Monday, August 25, 2014 5:51 PM
  • Hello SirLearnAlot,

    as I see you want to add an RMA to the related work items of an incident, correct me if I'm wrong.

    the console task handler you wrote is launched from an incident or from the RMA?

    what I mean, this task is an incident task? once you click it opens a related RMA and link the 2 work items?

    Best Regards


    Jreij12

    Tuesday, August 26, 2014 5:16 AM
  • Hello Georges,

    Thanks so much for responding! Yes you are correct, I have an incident class, and if I need to I click the "Create RMA" Task which I currently have setup as a console task (the tasks on the right-hand pane). If I have to make the task a button on the incident form, or if I can keep it as a console task does not matter to me, I just need a method to launch the RMA form from the incident form, and have the two objects related in the SMDB.

    Tuesday, August 26, 2014 3:47 PM
  • Hello SirLearnAlot,

    first you must add to your assembly a c# class as below:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.EnterpriseManagement.UI.FormsInfra;
    using Microsoft.EnterpriseManagement.UI.SdkDataAccess;
    using Microsoft.EnterpriseManagement.ConsoleFramework;
    using Microsoft.EnterpriseManagement.GenericForm;
    using Microsoft.EnterpriseManagement.UI.DataModel;
    using System.Windows;
    using Microsoft.EnterpriseManagement.UI.WpfWizardFramework;
    using Microsoft.EnterpriseManagement.UI.Extensions.Shared;
    using Microsoft.EnterpriseManagement.ServiceManager.Application.Common;
    
    
    namespace <YourProjectNameSpace>
    {
        class LaunchRelatedRMA: CreateWithLinkHandler
        {
            public LaunchRelatedRMA()
            {
                try
                {
    //below unsealed and sealed you must add your RMA class guid, you can find it in your ServiceManagerDatabase under
    //Select * from ManagedType order by TypeName desc
    //Just change the guid in this code with the guid of your class
    //try to use your sealed class guid, this way you don't get a warning each time you click the task
    
                    //unsealed
                    //this.createClassGuid = new Guid("679B286E-DAB1-8CDA-A61E-30018BE5E336");
    
                    //sealed
                    this.createClassGuid = new Guid("174B27AE-3E11-31D3-FD91-132EA1804CBA");
    
                    this.classToDelegate = new Dictionary<Guid, CreateLinkHelperCallback>()
                    {
    					{ ApplicationConstants.WorkItemTypeId, new CreateLinkHelperCallback(this.WorkItemCallback) }
    				};
                }
                catch (Exception exc1)
                {
                    MessageBox.Show(exc1.Message,"Exception",MessageBoxButton.OK,MessageBoxImage.Error);
                }
            }
    
    //newItem is the RMA you're about to create
    //linkItem is the incident you launched the task from
    
            public void WorkItemCallback(IDataItem newItem, IDataItem linkItem)
            {
                try
                {
    //make sure you have an alias in your xml type projection called RelatedWorkItems if not check the XML part after this c# code
                    if (newItem != null && newItem.HasProperty("RelatedWorkItems"))
                    {
    //Relate the RMA to the Incident
                        newItem["RelatedWorkItems"]= linkItem;
    //Copy the incident title to the RMA title
                        newItem["Title"] = linkItem["Title"];
    //Copy the incident Description to the RMA description
                        newItem["Description"] = linkItem["Description"];
                    }
                }
                catch (Exception exc2)
                {
                    MessageBox.Show(exc2.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }
    }
    

    Now in your class xml you have to make sure that RelatedWorkItems type projection alias exists as below under the TypeProjection tag:

    <Component Path="$Target/Path[Relationship='Alias_40d1e04c_d5af_48ff_b596_3f008b03466b!System.WorkItemRelatesToWorkItem']$" Alias="RelatedWorkItems">
                <Component Path="$Target/Path[Relationship='Alias_40d1e04c_d5af_48ff_b596_3f008b03466b!System.WorkItemAffectedUser']$" Alias="RWIAffectedUser" />
                <Component Path="$Target/Path[Relationship='Alias_40d1e04c_d5af_48ff_b596_3f008b03466b!System.WorkItemAssignedToUser']$" Alias="RWIAssignedUser" />
              </Component>

    and a console task has been created as below:

    <ConsoleTasks>
          <ConsoleTask ID="CreateRelatedRMA" Accessibility="Public" Enabled="true" Target="CoreIncident!System.WorkItem.Incident" RequireOutput="false">
            <Assembly>Console!SdkDataAccessAssembly</Assembly>
            <Handler>Microsoft.EnterpriseManagement.UI.SdkDataAccess.ConsoleTaskHandler</Handler>
            <Parameters>
              <Argument Name="Assembly"><!--your assembly name--></Argument>
              <Argument Name="Type">YourProjectNamespace.LaunchRelatedRMA</Argument>
            </Parameters>
          </ConsoleTask>
        </ConsoleTasks>

    and in your xml reference, 

    Alias_40d1e04c_d5af_48ff_b596_3f008b03466b

      refers to:

    <Reference Alias="Alias_40d1e04c_d5af_48ff_b596_3f008b03466b">
            <ID>System.WorkItem.Library</ID>
            <Version>7.5.1561.0</Version>
            <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
          </Reference>

    and

    CoreIncident

    refers to:

    <Reference Alias="CoreIncident">
            <ID>System.WorkItem.Incident.Library</ID>
            <Version>7.5.1561.0</Version>
            <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
          </Reference>

    Best Regards,

    Georges Mouawad

     

    Jreij12

    • Marked as answer by SirLearnAlot Wednesday, August 27, 2014 7:43 PM
    Wednesday, August 27, 2014 5:27 AM
  • Thanks so much Georges! Brilliant! It worked with some small modifications :) Much appreciated!

    Just a few missing easy things:

    1. At the moment, if I have an incident open, then click the Create RMA Task, it creates the RMA task correctly. However, if I do not have any incident open, if an analyst clicks the task it creates the RMA anyway without checking for an open incident form.

    So basically I need it to check for an open incident to associate the RMA with, OR if possible check for open incident, if no open incident next check would be if an incident is SELECTED but not open (e.g. if you click all incidents, and select a specific incident without opening it), otherwise display a message to the user (e.g. "Please select or open an Incident before creating an RMA")

    2. The other thing missing is I want to add the ID # of the Incident to the RMA description. So for example if I am working with incident IR24 and I click "Create RMA" then it should add something like "Pertains to IR24" in the RMA description.

    3. At the moment, RMA Display name is very weird... see screenshot below.

    I need the Description of the RMA to be the IR#, and the DisplayName to be the RMA#

    Thanks again Georges you are a hero!



    Wednesday, August 27, 2014 7:28 PM
  • Dear SirLearnAlot,

    you must change the function as below:

    public void WorkItemCallback(IDataItem newItem, IDataItem linkItem)
            {
                try
                {
    //make sure you have an alias in your xml type projection called RelatedWorkItems if not check the XML part after this c# code
                    if (newItem != null && newItem.HasProperty("RelatedWorkItems"))
                    {
    //Relate the RMA to the Incident
                        newItem["RelatedWorkItems"]= linkItem;
    //Copy the incident title to the RMA title
                        newItem["Title"] = linkItem["Title"];
    //Copy the incident Description to the RMA description
                        newItem["Description"] = linkItem["Id"];
    
    newItem["DisplayName"]= newItem["Id"];
                    }
                }
                catch (Exception exc2)
                {
                    MessageBox.Show(exc2.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }
    }

    If the part of DisplayName didn't work for you, you must change the handler in the main class of the assembly.

    PS: Best practice of WorkItem DisplayName is "ID# - Title"

    you can do it if it works this way: newItem["DisplayName"] = newItem["Id"] + " - " + newItem["Title"];

    Best Regards,

    Georges


    Jreij12

    Thursday, August 28, 2014 5:35 AM
  • Awesome George Thanks, I understand the code completely now.

    The last thing I need is to check for an open incident to associate the RMA with, OR if possible check for open incident, if no open incident next check would be if an incident is SELECTED but not open (e.g. if you click all incidents, and select a specific incident without opening it), otherwise display a message to the user (e.g. "Please select or open an Incident before creating an RMA")

    How do I add this part?

    Thursday, August 28, 2014 6:51 PM
  • Dear SirLearnAlot,

    this can be done from the xml, by adding this to the categories tag

    <Category ID="CreateServiceRiskMultiSelect" Target="CreateRMA" Value="Console!Microsoft.EnterpriseManagement.ServiceManager.UI.Console.MultiSelectTask" />

    and in the console task tag point to the task to the incidents views

    <ConsoleTasks>
          <ConsoleTask ID="CreateRMA" Accessibility="Public" Enabled="true" Target="CoreIncident!System.WorkItem.Incident" RequireOutput="false">
            <Assembly>Console!SdkDataAccessAssembly</Assembly>
            <Handler>Microsoft.EnterpriseManagement.UI.SdkDataAccess.ConsoleTaskHandler</Handler>
            <Parameters>
              <Argument Name="Assembly"><!--Your DLL name--></Argument>
              <Argument Name="Type"><!--namespace.taskClass--></Argument>
            </Parameters>
          </ConsoleTask>
        </ConsoleTasks>

    when you target the task to "CoreIncident!System.WorkItem.Incident"

    this task won't show up if you don't select an incident.

    Best Regards


    Jreij12

    Friday, August 29, 2014 12:22 PM