Display and Get Field Values Dynamically in Form

Hi All,

Project Requirement:
I need to create a form to update the ownership of objects from a terminated user to a new owner. Each object should display in a row with its description and a dropdown for selecting a new owner, arranged in three columns.

Issues Encountered:

Setting the section type to “text” prevents the dropdown from displaying.
Omitting the section type results in HTML being shown as plain text.
I want to use JavaScript to link the dropdown selections: if the first dropdown is changed, it should apply that value to other fields, but if any field is manually changed, those values should remain.
Request for Guidance:

How can I properly display the objects with the dropdown?
How can I process the selected new owners?
Code Snippet Request: Please include example code to help illustrate the solution.

Thanks for your assistance!
reference image:

Code:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Workflow PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Workflow created="1726576115350" explicitTransitions="true" handler="sailpoint.api.StandardWorkflowHandler" id="c0a8d81b91fe1f648191fff4be960281" libraries="Identity,BatchRequest" modified="1727937321052" name="Owner Update WF Test">
  <Variable initializer="string:true" name="trace"/>
  <Variable initializer="string:false" name="transient"/>
  <Variable input="true" name="launcher"/>
  <Variable input="true" name="requester"/>
  <Variable input="true" name="targetIdentity"/>
  <Variable input="true" name="launcherEmail"/>
  <Variable input="true" name="requesterEmail"/>
  <Variable input="true" name="targetIdentityEmail"/>
  <Variable editable="true" name="newOwner"/>
  <Variable editable="true" name="applicationsList"/>
  <Variable editable="true" name="managedAttributesList"/>
  <Variable editable="true" name="bundlesList"/>
  <Variable editable="true" name="selection"/>
  <Variable editable="true" name="identityName"/>
  <Variable editable="true" name="steward"/>
  <Variable editable="true" name="Application Owner"/>
  <Variable editable="true" name="Entitlement Owner"/>
  <Variable editable="true" name="Role Owner"/>
  <Variable editable="true" name="selectionEnt"/>
  <Variable editable="true" name="selectionRole"/>
  <Variable editable="true" name="manager"/>
  <Variable editable="true" name="selectionManager"/>
  <Variable editable="true" name="workitemId"/>
  <Variable editable="true" name="workitemName"/>
  <Step icon="Default" name="PrintInputVariables" posX="145" posY="10">
    <Script>
      <Source>
        // System.out.println("manager = " + manager); 
        System.out.println("requester = " + requester); 
        // System.out.println("managerEmail = " + managerEmail); 
        System.out.println("requesterEmail = " + requesterEmail); 
        // System.out.println("requesterName = " + requesterName); 
      </Source>
    </Script>
    <Transition to="ownerUpdate"/>
  </Step>
  <Step icon="Default" name="ownerUpdate" posX="444" posY="14">
    <Approval name="MRC Ressign a New Owner" owner="ref:launcher" return="newOwner" send="requester,targetIdentity,launcher">
      <Arg name="workItemDescription" value="Owner Reassignment for $(targetIdentity)"/>
      <Arg name="workItemRequester" value="ref:requester"/>
      <Arg name="workItemTargetIdentity" value="ref:targetIdentity"/>
      <Arg name="workItemTargetName" value="ref:targetIdentity"/>
      <Arg name="workItemState" value="Pending"/>
      <Arg name="workItemType" value="Form"/>
      <Form name="Owner Reassignment">
        <Attributes>
          <Map>
            <entry key="pageTitle" value="MRC Owner Reassignment Form"/>
          </Map>
        </Attributes>
        <Section name="htmlSection" type="text">
          <Field filterString="htmlRender" type="text">
            <Attributes>
              <Map>
                <entry key="contentIsEscaped" value="true"/>
              </Map>
            </Attributes>
            <Script>
              <Source>
                return "&lt;span style='color:#037DA1;'>&lt;strong>Note: This Form is to Update/Reassign a New owner for all objects owned by the Terminated user&lt;/strong>&lt;/span>&lt;br>&lt;br>";
              </Source>
            </Script>
          </Field>
        </Section>
        <Section columns="4">
          <Field columnSpan="1" displayName="Select User" dynamic="true" filterString="inactive == true" name="userId" postBack="true" required="true" type="identity">
            <Attributes>
              <Map>
                <entry key="valueProperty" value="name"/>
              </Map>
            </Attributes>
          </Field>
        </Section>
        <Section type="text">
          <Field dependencies="userId" displayName="" dynamic="true" multi="true" name="identityDetails" postBack="true">
            <Attributes>
              <Map>
                <entry key="contentIsEscaped" value="false"/>
                <entry key="hidden" value="script:return form.getField(&quot;userId&quot;).getValue() == null;"/>
              </Map>
            </Attributes>
            <Script>
              <Source>
                import sailpoint.object.Identity;

                String userId = form.getField("userId") != null ? (String) form.getField("userId").getValue() : "Adam.Kennedy";

                log.debug("UserId from form: " + userId);

                String userInfo = "";
                try {

                Identity user = context.getObjectByName(Identity.class, userId);

                if (user != null) {
                userInfo += "&lt;table class='spTable'>&lt;th style='text-align:center; color:#037DA1' colspan='2'>User Information&lt;/th>";
                userInfo += "&lt;tr>&lt;td>&lt;b>Username&lt;/b>&lt;/td>&lt;td>" + user.getName() + "&lt;/td>&lt;/tr>";
                userInfo += "&lt;tr>&lt;td>&lt;b>First Name&lt;/b>&lt;/td>&lt;td>" + user.getFirstname() + "&lt;/td>&lt;/tr>";
                userInfo += "&lt;tr>&lt;td>&lt;b>Last Name&lt;/b>&lt;/td>&lt;td>" + user.getLastname() + "&lt;/td>&lt;/tr>";
                userInfo += "&lt;tr>&lt;td>&lt;b>Manager&lt;/b>&lt;/td>&lt;td>" + (user.getManager() != null ? user.getManager().getDisplayName() : "No Manager") + "&lt;/td>&lt;/tr>";
                userInfo += "&lt;tr>&lt;td>&lt;b>Department&lt;/b>&lt;/td>&lt;td>" + (user.getAttribute("departmentName") != null ? user.getAttribute("departmentName") : "No Department") + "&lt;/td>&lt;/tr>";
                userInfo += "&lt;tr>&lt;td>&lt;b>Job Title&lt;/b>&lt;/td>&lt;td>" + (user.getAttribute("jobTitle") != null ? user.getAttribute("jobTitle") : "No Job Title") + "&lt;/td>&lt;/tr>";
                userInfo += "&lt;/table>";
                } else {
                userInfo = "&lt;font color='red'>User not found: " + userId + "&lt;/font>";
                }
                } catch (Exception ex) {
                log.error("Error fetching user details", ex);
                userInfo = "&lt;font color='red'>Error retrieving user details&lt;/font>";
                }

                return userInfo;


              </Source>
            </Script>
          </Field>
        </Section>
        <Section columns="4" name="appSingleSec" type="text">
          <Field columnSpan="4" dependencies="userId" displayName="Application Objects" dynamic="true" name="ownedObjectsSingle" postBack="true" type="object">
            <Attributes>
              <Map>
                <entry key="contentIsEscaped" value="true"/>
                <entry key="hidden" value="script:return form.getField(&quot;userId&quot;).getValue() == null;"/>
              </Map>
            </Attributes>
            <Script>
              <Source>
                import sailpoint.object.Application;
                import sailpoint.object.QueryOptions;
                import sailpoint.object.Filter;
                import sailpoint.object.Identity;
                import java.util.List;
                import java.util.Map;

                String content = "";

                try {
                String userId = form.getField("userId") != null ? (String) form.getField("userId").getValue() : "Adam.Kennedy";
                Identity selectedUser = context.getObjectByName(Identity.class, userId);

                if (selectedUser != null) {
                // Fetch applications owned by the selected user
                QueryOptions appQueryOptions = new QueryOptions();
                appQueryOptions.addFilter(Filter.eq("owner", selectedUser));
                List applicationsList = context.getObjects(Application.class, appQueryOptions);

                // Fetch active identities for the New Owner dropdown
                QueryOptions queryOptions = new QueryOptions();
                queryOptions.addFilter(Filter.eq("inactive", false));  // Filter active identities
                List inactiveIdentities = context.getObjects(Identity.class, queryOptions);

                // Start building the table content
                content += "&lt;table class='spTable' style='width:100%; border-collapse:collapse;'>";
                content += "&lt;tr>&lt;th style='border:1px solid black;'>Application&lt;/th>&lt;th style='border:1px solid black;'>Description&lt;/th>&lt;th style='border:1px solid black;'>New Owner&lt;/th>&lt;/tr>";

                if (applicationsList != null &amp;&amp; !applicationsList.isEmpty()) {
                for (Application app : applicationsList) {
                String appName = app.getName();
                String description = "No description available";
                Map descriptions = app.getDescriptions();

                if (descriptions != null &amp;&amp; !descriptions.isEmpty()) {
                description = descriptions.getOrDefault("en", descriptions.values().iterator().next());
                }

                content += "&lt;tr>";
                content += "&lt;td style='border:1px solid black;'>" + appName + "&lt;/td>";
                content += "&lt;td style='border:1px solid black;'>" + description + "&lt;/td>";
                content += "&lt;td style='border:1px solid black;'>";
                content += "&lt;select>";

                // Loop through the inactiveIdentities and add options
                for (Identity identity : inactiveIdentities) {
                content += "&lt;option value='" + identity.getName() + "'>" + identity.getName() + "&lt;/option>";
                }

                content += "&lt;/select>";
                content += "&lt;/td>";
                content += "&lt;/tr>";
                }
                } else {
                content += "&lt;tr>&lt;td colspan='3' style='border:1px solid black; text-align:center;'>No Applications Found&lt;/td>&lt;/tr>";
                }

                content += "&lt;/table>";
                } else {
                content = "&lt;font color='red'>User not found: " + userId + "&lt;/font>";
                }
                } catch (Exception ex) {
                content = "&lt;font color='red'>Error retrieving applications or identities: " + ex.getMessage() + "&lt;/font>";
                }

                if ("".equals(content)) {
                content = "&lt;font color='red'>Please select a user to see the owned applications.&lt;/font>";
                }

                return content;
              </Source>
            </Script>
          </Field>
        </Section>
        <Button action="next" label="Next"/>
        <Button action="cancel" label="Exit"/>
      </Form>
      <InterceptorScript>
        <Source>

          import sailpoint.object.Identity;
          import sailpoint.object.Configuration;
          import sailpoint.object.Workflow;
          import sailpoint.object.WorkItem;
          import sailpoint.object.WorkItem.State;

          if (method.equals(Workflow.INTERCEPTOR_START_APPROVAL)) {
          log.error("Method is startApproval");

          } else if (method.equals(Workflow.INTERCEPTOR_OPEN_WORK_ITEM)) {

          if (item == null) {
          log.error("No work item found");
          } else {
          try {

          item.setName("Workitem Form for: " + targetIdentity);
          item.setState(State.Pending);
          context.saveObject(item);
          // log.error("WorkItem saved: " + item.toXml());

          String workitemId = item.getId();
          String workitemName = item.getName();

          workflow.put("workitemId", workitemId);
          workflow.put("workitemName", workitemName);

          log.error("Current Item Name: " + workitemName);
          log.error("Current Item ID: " + workitemId);
          log.error("Current Item Name: " + workitemName);
          log.error("Current Item State: " + item.getState());
          log.error("Current Item targetname: " + item.getTargetName());
          log.error("Current Item level: " + item.getLevel());
          log.error("Current Item type: " + item.getType());
          log.error("Current Item requester: " + item.getRequester());

          } catch (Exception e) {
          log.error("Exception while saving WorkItem: " + e.getMessage(), e);
          }
          }
          } else {
          log.error("Method does not match startApproval or openWorkItem");
          }
        </Source>
      </InterceptorScript>
    </Approval>

    <Transition to="End"/>
  </Step>
  <Step icon="End" name="End" posX="708" posY="30"/>
</Workflow>

Hi @suresh_duraisamy,

I replied on this topic

Hi @enistri_devo ,

Thanks for your info, I check that one, Can you please give some additional details and references instead of go with the plugin approach?

Thanks,
Suresh

Plugins is a long and complex part of SP and I suggest to reading the documentation: Plugins.

Simply, with a plugin you can implent or extend a functionality of IIQ like new REST call, service or change the UI.

There ou can find some plugins to understand how it works and you can start from one and change for your necessities.