Skip to main content
Version: On prem: 15.0.3

Post actions

A post action handler is used to perform an action when a survey object is completed, that is, it has passed all relevant workflow steps in a survey.

A registered post action handler is also executed on survey objects that are not completed when/if a survey is prematurely closed.

The typical uses of post action handlers are:

  • Updating the source data object with some of the changes made in the survey (in case the source object is a data object).
  • Writing survey results to a custom database.

Technically a Post action handler is a .NET class that implements the ISurveyPostActionHandler interface, which is located in the Omada.OE.Solution.OIM.AppLogic assembly.

With the generic post action handler, installed with the Omada Identity, you can update data objects from a survey without using custom code. The post action handler uses the copy rule concept to enable mappings between properties on the survey object and properties on the data objects to which the survey objects relate.

Update the Data object survey post action handler

  1. Create a new copy rule with the shadow object type for the appropriate survey template as the source object and a destination object that matches the destination object type of the survey template for which the copy rule is attended.

  2. Add mappings between the properties on the survey template and the destination object(s).

  3. When you add a new field mapping, you can choose either a constant value or a value of any property from the source object. You can also specify a reference path, which is resolved starting from the survey object.

  4. In the survey template, add a new post action.

  5. In the class property, type: Omada.OE.Solution.OIM.Assembly.SurveyPostActions.UpdateDataObjectSurveyPostActionHandler, Omada.OE.Solution.OIM.Assembly and click OK.

  6. Open the context menu for the new post action, then click Parameters.

  7. In the name field, type copyRuleUId.

    info

    The UpdateDataObjectSurveyPostActionHandler post-action handler also supports an additional childObjectsCopyRuleUIdparameter to be used if the survey template contains child objects. It is possible to provide an additional Copy rule, that can be used to copy values from child objects. The parameter name for the child objects Copy rule is childObjectsCopyRuleUId. If the parameter is not used, the Copy rule referenced by the CopyRuleUId parameter will be used for parent and child survey objects.

  8. In the value field, type the UID of the copy rule defined in the first step, then click OK.

  9. Optional step: To log the changes made to the target data objects, add an additional parameter to the post action with the Name logHistoryPropertySystemName, and Value containing the system name of a value text property on the data object, which is used to save a text log entry for every update that the post action handler makes.

info

The property value is appended with one line per update with the following format: “Updated by {Lastname, Firstname} on {CurrentDateTime}”.

Develop a custom post action handler

If you develop a custom post action handler class, typically, it should be placed in the same assembly as the custom code methods used for an implementation project.

info

It is possible to register multiple post action handlers.

iSurvey post action handler interface

The ISurveyPostActionHandler interface is the following:

namespace Omada.OE.Solution.OIM.AppLogic.SurveyFeature.PostActions

{

/// <summary>

/// Classes that perform "post actions" on survey objects must implement this interface.

/// </summary>

public interface ISurveyPostActionHandler

{

/// <summary>

/// Initialize is called when the handler is initialized.

/// A handler is initialized with a set of configuration parameters.

/// </summary>

/// <param name="arguments"></param>

void Initialize(InitializationArguments arguments);

/// <summary>

/// PerformAction executes an action on a single survey object that has completed its processing in a survey.

/// </summary>

/// <param name="arguments"></param>

void PerformAction(ActionArguments arguments);

}

}
The Action arguments class

The Initialize method is called with a set of all the completed survey objects.

The arguments of type InitializationArguments contain a Dictionary<string, string> Parameters which contains the parameters stated in the survey template.

The PerformAction method is called once per completed survey object. The arguments of type ActionArguments contains the initial property values of the survey object (when the survey launched) as well as the final property values.

The ActionArguments class is the following:

/// <summary>

/// Arguments for performing a post action on a survey object.

/// </summary>

public class ActionArguments

{

/// <summary>

/// Id of the survey that the survey object has been processed in.

/// </summary>

public int SurveyId { get; private set; }

/// <summary>

/// Source key of the survey object to perform post actions on.

/// </summary>

public string SourceKey { get; private set; }

/// <summary>

/// Initial property values of the survey object at the time when the survey was initiated.

/// Keys in the dictionary are property system names.

/// Values are collections of property values.

/// Each value for a property is either a:

/// string (VP datatype text, hyperlink, xml), integer (VP datatype integer, RP, SP), boolean (VP datatype bool), TimeSpan (VP datatype TimeSpan), double (VP datatype decimal) or DateTime (VP datatype DateTime).

/// The result for value property will always contain zero or one value.

/// The result for set- and reference-properties can contain zero, one or multiple values.

/// For set- and reference-properties the value ids are returned (set property value ids or data object ids).

/// </summary>

public Dictionary<string, List<object>> InitialPropertyValues { get; private set; }

/// <summary>

/// Final property values of the survey object (considering the changes made during the syrvey).

/// See InitialPropertyValues for a description of the data types.

/// </summary>

public Dictionary<string, List<object>> FinalPropertyValues { get; private set; }

/// <summary>

/// System names of the properties that has been changed.

/// </summary>

public List<string> ChangedProperties { get; private set; }

/// <summary>

/// PropertyActions can only be utilized in an implementation of ISurveyPostActionHandler by calling ActionArguments.ApplyPropertyChanges().

/// </summary>

private List<Serialization.Model.Property> PropertyActions { get; set; }

/// <summary>

/// Has all (relevant) workflow steps been completed for the survey object?

/// If the post action is being executed as a result of survey being prematurely closed then WorkflowCompleted is false.

/// </summary>

public bool WorkflowCompleted { get; private set; }

/// <summary>

/// Log entries describing who changed which properties and when on the survey object.

/// Tip: can also be accessed with GetMostRecentLogEntry().

/// </summary>

public List<LogEntry> LogEntries { get; private set; }

public SqlConnection DbConnection { get; private set; }

public SqlTransaction DbTransaction { get; private set; }

public Omada.OE.AppLogic.Factory Factory { get; private set; }

// …

}
Useful methods in the Action arguments class

Additionally, the ActionArguments class contains some useful methods:

  • ApplyPropertyChanges() can be used to easily apply the modified property values of the survey object to a (typically the source) data object.
  • ConvertObjectId() (+1 overloads) can be used for convenience to convert between Ids and UIds.
  • GetPropertyValue() can be used to retrieve the value(s) of a property from either the initial or the final state of the survey object.
  • GetMostRecentLogEntry() can be used to retrieve the most recently log entry for a property.
/// <summary>

/// Applies the changes made to the survey object during the survey to a data object.

/// The data object is not saved to the database!

/// </summary>

/// <param name="dataObject">

/// The data object to apply the changes to - normally the source data object.

/// </param>

/// <param name="properties">

/// Property system names.

/// Property changes should only be applied to these properties.

/// </param>

public void ApplyPropertyChanges(DataObject dataObject, IEnumerable<string> properties = null)

{ .. }

/// <summary>

/// Utility method for converting an object uid to an id (of for example a data object or property etc.).

/// </summary>

/// <param name="id"></param>

/// <param name="objectType"></param>

/// <returns></returns>

public int ConvertObjectId(Guid id, ConfigurationObjectType objectType)

{ .. }

/// <summary>

/// Utility method for converting an object id to an uid (of for example a data object or property etc.).

/// </summary>

/// <param name="id"></param>

/// <param name="objectType"></param>

/// <returns></returns>

public Guid ConvertObjectId(int id, ConfigurationObjectType objectType)

{ .. }

/// <summary>

/// Utility method for easy retrieval of a single property value from either InitialPropertyValues or FinalPropertyValues.

/// </summary>

/// <param name="property">

/// System name of a property that has values of the applied data type.

/// </param>

/// <param name="propertyValues"></param>

/// <param name="defaultValue">

/// The default value is returned if the propertyValue dictionary doesn't contain an entry for the property or if the property has no value(s).

/// </param>

/// <returns></returns>

public T GetPropertyValue<T>(string property, Dictionary<string, List<object>> propertyValues, T defaultValue)

{ .. }
Sample post action handler

An example of a complete post action handler is included below. The class is designed to be used in a survey of Resource Assignment Data Objects.

/// <summary>

/// Sample survey "post action handler" to be used in a survey of resource assignment data objects.

/// The post action handler expires the source resource assignment data object if the "action" in the survey is set to "remove".

/// It always updates the SURV_LASTATTESTED property on the resource assignment data object (if present) - regardless of the user's action.

/// Using the post action handler requires that the properties SURV_ACTION and SURV_ACTIONCOMMENT are used as survey properties.

/// </summary>

public class ResourceAssignmentHandler : ISurveyPostActionHandler

{

/// <summary>

/// System names of properties (on the resource assignment DOT) that we want to update.

/// </summary>

private List<string> propertiesToUpdate;

/// <summary>

///

/// </summary>

/// <param name="arguments"></param>

public void Initialize(InitializationArguments arguments)

{

if (arguments.Parameters.ContainsKey("properties"))

{

string propsToUpdate = arguments.Parameters["properties"];

this.propertiesToUpdate = new List<string>(propsToUpdate.Split(','));

}

}

private DataObject LoadSourceResourceAssignment(Factory factory, Guid resourceAssignmentUId)

{

DataObjectController controller = factory.CreateController<DataObjectController>();

DataObjectLoadOptions loadOptions = new DataObjectLoadOptions();

loadOptions.AddFilterExpression("ID", ExpInnerOperator.In, new Guid[] { resourceAssignmentUId });

loadOptions.OverrideSecurity = true;

DataObjects dataObjects = controller.LoadObjects(loadOptions);

if (dataObjects.Count == 0)

throw new ArgumentException("Could not locate data object with uid: " + resourceAssignmentUId, "resourceAssignmentUId");

if (dataObjects[0].TypeId != OIMObjectId.DataObjectType.ResourceAssignment)

throw new ArgumentException("Source data object with uid '" + resourceAssignmentUId + "' is not a resource assignment object - it has type id: " + dataObjects[0].TypeId, "resourceAssignmentUId");

return dataObjects[0];

}

/// <summary>

/// Performs an action on a single survey object (that must originate from a resource assigment data object).

/// </summary>

/// <param name="arguments"></param>

public void PerformAction(ActionArguments arguments)

{

// Load the resource assignment data object

DataObject dataObject = this.LoadSourceResourceAssignment(arguments.Factory, new Guid(arguments.SourceKey));

dataObject.CheckAndAddVersion(true);

// Apply the changes made in the survey to the resource assignment source data object

arguments.ApplyPropertyChanges(dataObject, this.propertiesToUpdate);

// Expire it if the user has chosen to in the survey

int actionId = arguments.GetPropertyValue("SURV_ACTION", arguments.FinalPropertyValues, 0);

if (arguments.ConvertObjectId(actionId, ConfigurationObjectType.PropertyValue) == OIMObjectUId.PropertyValue.SurveyAction.Remove)

{

dataObject["ROLEASSNSTATUS"] = new object[] { OIMObjectId.PropertyValue.RoleAssnStatus.Obsolete };

dataObject["VALIDTO"] = new object[] { DateTime.UtcNow };

// Retrieve who was the last person to change the "action" property

LogEntry logEntry = arguments.GetMostRecentLogEntry("SURV_ACTION");

DataObjectController dataObjectController = arguments.Factory.CreateController<DataObjectController>();

string who = logEntry != null ? dataObjectController.GetDisplayName(logEntry.Who) : "UNKNOWN";

StringBuilder assignmentDescription = new StringBuilder(dataObject.GetPropertyValue("DESCRIPTION", String.Empty));

string actionComment = arguments.GetPropertyValue("SURV_ACTIONCOMMENT", arguments.FinalPropertyValues, String.Empty);

assignmentDescription.AppendLine();

assignmentDescription.AppendFormat("{0}: In survey with id {1} it was decided by \"{3}\" to expire the resource assignment. Stated reason: {2}", DateTime.UtcNow.ToString("g"), arguments.SurveyId, actionComment, who);

dataObject["DESCRIPTION"] = new object[] { assignmentDescription.ToString() };

}

// If the "resource assignment" DOT has the "last attested" property then we update it to "now".

if (dataObject.LastVersion.Properties.GetBySystemName("SURV_LASTATTESTED") != null)

dataObject["SURV_LASTATTESTED"] = new object[] { DateTime.UtcNow };

// Save the updated data object

DataObjectController controller = arguments.Factory.CreateController<DataObjectController>();

controller.CheckAndSaveDataObject(dataObject);

}

}

Post action handler Markup language

A survey template is authored in the SurveyTemplateML xml language. The schema definition file, SurveyTemplateML.xsd.A Post Action Handler is registered in a survey template as in the below example. Notice that it is possible to register multiple Post Action Handlers. If runAsSystem is True, the Post Action Handler will be executed as the System user.

  <postActions>

<postAction class="Omada.OE.Solution.OIM.AppLogic.SurveyFeature.PostActions.ResourceAssignmentHandler, Omada.OE.Solution.OIM.AppLogic" runAsSystem="true">

<parameters>

<parameter name="properties" value="VALIDTO,ROLEASSNSTATUS"/>

</parameters>

</postAction>

</postActions>

postAction

The table below list the available attributes for postAction:

AttributeDescription
ClassThe Post Action Handler class.
runAsSystemTrue/False. If set to true, the Post Action Handler will be executed in the security context of the system user rather than the user committing the survey.
runAfterEveryStepTrue/False. If set to true, the Post Action Handler will be executed when the survey object is routed from one workflow step to another, and not only after the last workflow step.

Parameter

The table below list the available attributes for parameter:

AttributeDescription
ClassThe Post Action Handler class.
runAsSystemTrue/False. If set to true, the Post Action Handler will be executed in the security context of the system user rather than the user committing the survey.
runAfterEveryStepTrue/False. If set to true, the Post Action Handler will be executed when the survey object is routed from one workflow step to another, and not only after the last workflow step.

Survey settings

Survey settings can be used to set flexible settings for post action handlers at survey initiation. This can for example be used in a resource assignment survey to select to deprovision resource assignments with uncompleted questions as well as those that were rejected.

Post Action Handlers can use these survey settings to perform the post actions based on the settings set at survey launch.

AttributeDescription
NameThe name of the survey setting.
TitleThe text that is displayed in the user interface.
PropertySpecify the objectType property the survey setting relates to.
DataTypeThe dataType of the property. Possible values: Reference String DateTime Boolean Integer
Requires ValueTrue/False. If set to True, the survey setting must be set when launching the survey.
ShowInUIDefined whether the setting is shown in the UI.