Organizations needing to store and maintain attributes not defined for a given object in the object tables of the EmpowerID Identity Warehouse, can extend those objects as needed by overriding the class definition or schema for those objects with each new attribute. Doing so is preferable to using extension attributes in that extending an object's class definition makes any custom attributes you add to that definition available to your workflows and user interfaces as actual component properties that can be edited and updated in the same way as the stock component properties.
Overriding the class definition for EmpowerID components involves creating a class library project that contains an edited version of a file called GeneratedComponents.cs for the each component you wish to customize. This file contains references pointing to the default WCF data contracts for each EmpowerID component. Because this file changes frequently, you must contact EmpowerID for the correct version for this file before you can customize objects. This ensures that you have the latest version of the file for your build. |
In this topic, we demonstrate how to add a custom attribute to an EmpowerID component by adding an attribute named "Clearance Level" to the Person component. Doing so involves the following:
In this example, we create a class library project named EIDComponents with a custom definition for the Person component that when published to the GAC places a record in the GACReferenceLibrary table of the EmpowerID Identity Warehouse. This tells EmpowerID to ignore the stock EIDComponents.dll that ships with EmpowerID and look instead at this assembly for the attributes available for each EmpowerID component. This project contains four classes, the EIDComponents class, which contains a customized version of the GeneratedComponents.cs file you received from EmpowerID, the AccountStoreIdentityEntry class, and the Person and PersonView classes, which is where we will add code to define the custom attributes. To add the custom attribute to the Person component, we will do the following:
When creating the custom attributes for the Person and PersonView classes, we write code to inherit those classes from the base PersonCore and PersonViewCore objects, respectively. This makes all the properties and methods of the default Person and PersonView objects available to our customized components. Additionally, we implement the CustomizedComponentHelper.ICustomizedComponent interface while declaring those objects. This interface requires us to add a public property named CustomData of type CustomizedComponentHelper.ICustomizedComponent, which is a helper variable used for storing custom information. We then write code to get and set the value for one primitive property, ClearanceLevel . Additionally, we decorate the classes with the [CreateRbacProperty], [System.Runtime.Serialization.IgnoreDataMemberAttribute], and the [TheDotNetFactory.Framework.People.Components.Serialization.CustomSerializationAttribute] decorators. These decorators instruct EmpowerID to make any decorated properties (primitives only) available to forms, lookups, reports and Web pages once the class library project is compiled and published to the Identity Warehouse. |
Locate the GeneratedComponents.cs file sent to you by EmpowerID and copy and paste the contents of the file into the C# Editor for the EIDComponents class.
Reorder the using and namespace statements so that the using statements are contained within the namespace statement.
namespace TheDotNetFactory.Framework.People.Components { using System; using System.Collections.Generic; using System.Linq; using System.Text; [Serializable][global::System.Runtime.Serialization.DataContractAttribute (Namespace = "http://empowerid.sts.com/svc/2010/03/schemas")] public class MultivalueAttribute : TheDotNetFactory.Framework.People.Components.Core.MultivalueAttributeCore { } [Serializable][global::System.Runtime.Serialization.DataContractAttribute (Namespace = "http://empowerid.sts.com/svc/2010/03/schemas")] public class ResourceGroup : TheDotNetFactory.Framework.People.Components.Core.ResourceGroupCore { } [Serializable][global::System.Runtime.Serialization.DataContractAttribute (Namespace = "http://empowerid.sts.com/svc/2010/03/schemas")] public class CommunicationZone : TheDotNetFactory.Framework.People.Components.Core.CommunicationZoneCore { } ... } |
In the C# Editor locate the line of code for the class you wish to extend and comment it out. In our case, we are extending the Person object, so we have commented out the lines specifying the attributes for the default Person and PersonView classes.
The quickest way to search for the line of code pertaining to the Person class is to click on the Search button above the C# Editor and type public partial class Person : in the Find what: field.
|
Add code to inherit the Person class from the base PersonCore object, as well as the CustomizedComponentHelper.ICustomisedComponent interface.
[Serializable][global::System.Runtime.Serialization.DataContractAttribute(Namespace = "http://empowerid.sts.com/svc/2010/03/schemas")] public class Person : TheDotNetFactory.Framework.People.Components.Core.PersonCore, CustomizedComponentHelper.ICustomizedComponent { } |
Create a property for the custom attribute, decorating it with the [CreateRbacProperty], [DataMember] and [TheDotNetFactory.Framework.Components.Serialization.customPropertySerializationAttribute] decorators and using the GetExtendedAttribute() method of the CustomizedComponentHelper class to get and set its value. When getting the value of the attribute, you pass in parameters for the customized component, the name of the custom attribute, and a boolean for xml serialization. When setting the value of the property, you pass in the same parameters with the addition of value.
The following shows what the code for the above looks like:
[Serializable][global::System.Runtime.Serialization.DataContractAttribute(Namespace = "http://empowerid.sts.com/svc/2010/03/schemas")] public class Person : TheDotNetFactory.Framework.People.Components.Core.PersonCore, CustomizedComponentHelper.ICustomizedComponent { [System.Runtime.Serialization.IgnoreDataMemberAttribute] public CustomizedComponentHelper.ConfigurationXmlWrapper CustomData {get; set;} [CreateRbacProperty] [DataMember] [TheDotNetFactory.Framework.People.Components.Serialization.CustomPropertySerializationAttribute] public string ClearanceLevel { get { return CustomizedComponentHelper.GetExtendedAttribValue<string>(this, "Person", "ClearanceLevel", true); } set { CustomizedComponentHelper.SetExtendedAttribValue<string>(this, "Person", "ClearanceLevel", value, true); } } |
Save your changes.
When completed, the code for the Person class should look similar to the following code.
namespace TheDotNetFactory.Framework.People.Components { usings [Serializable][global::System.Runtime.Serialization.DataContractAttribute(Namespace = "http://empowerid.sts.com/svc/2010/03/schemas")] public class Person : TheDotNetFactory.Framework.People.Components.Core.PersonCore, CustomizedComponentHelper.ICustomizedComponent { [System.Runtime.Serialization.IgnoreDataMemberAttribute] public CustomizedComponentHelper.ConfigurationXmlWrapper CustomData {get; set;} [CreateRbacProperty] [DataMember] [TheDotNetFactory.Framework.People.Components.Serialization.CustomPropertySerializationAttribute] public string ClearanceLevel { get { return CustomizedComponentHelper.GetExtendedAttribValue<string>(this, "Person", "ClearanceLevel", true); } set { CustomizedComponentHelper.SetExtendedAttribValue<string>(this, "Person", "ClearanceLevel", value, true); } } } } |
Add code to inherit the PersonView class from the base PersonViewCore object, as well as the CustomizedComponentHelper.ICustomisedComponent interface.
[Serializable][global::System.Runtime.Serialization.DataContractAttribute(Namespace = "http://empowerid.sts.com/svc/2010/03/schemas")] public class PersonView : TheDotNetFactory.Framework.People.Components.Core.PersonViewCore, CustomizedComponentHelper.ICustomizedComponent { } |
Create a property for the custom attribute, decorating it with the [CreateRbacProperty], [DataMember] and [TheDotNetFactory.Framework.Components.Serialization.customPropertySerializationAttribute] decorators and using the GetExtendedAttribute() method of the CustomizedComponentHelper class to get and set its value. When getting the value of the attribute, you pass in parameters for the customized component, the name of the custom attribute, and a boolean for xml serialization. When setting the value of the property, you pass in the same parameters with the addition of value.
The following shows what the code for the PersonView class should look like when you have completed the above steps.
[Serializable][global::System.Runtime.Serialization.DataContractAttribute(Namespace = "http://empowerid.sts.com/svc/2010/03/schemas")] public class Person : TheDotNetFactory.Framework.People.Components.Core.PersonCore, CustomizedComponentHelper.ICustomizedComponent { [System.Runtime.Serialization.IgnoreDataMemberAttribute] public CustomizedComponentHelper.ConfigurationXmlWrapper CustomData {get; set;} [CreateRbacProperty] [DataMember] [TheDotNetFactory.Framework.People.Components.Serialization.CustomPropertySerializationAttribute] public string ClearanceLevel { get { return CustomizedComponentHelper.GetExtendedAttribValue<string>(this, "Person", "ClearanceLevel", true); } set { CustomizedComponentHelper.SetExtendedAttribValue<string>(this, "Person", "ClearanceLevel", value, true); } } } |
The next step is to add an assembly for the new EIDComponents class library to the GAC. This is accomplished by compiling and publishing the class library. We demonstrate this below.
Select an EmpowerID server as the publishing location and then click Next.
When the wizard has completed publishing, you will be promoted to restart one or more services. Restarting the services allows EmpowerID to pick up your changes as well as make the underlying assembly for the class library available to the local GAC of those services. |
Now that the RBAC object data is updated, the new attribute can be used in existing workflows, activities and forms. We demonstrate this next by adding code to the Edit Person Multi Operations operation to allow delegated users the ability to edit the custom attribute.
In the C# Editor for the EditPersonOrganizationAttributes operation that opens, add code to the ExecuteOperation() method to set the ClearanceLevel property for each person being edited to the value entered in the Clearance Level field of the workflow form.
person.ClearanceLevel = CurrentWorkflow.Form_TargetPerson.ClearanceLevel; |
The below image shows what the above code looks like in the C# Editor.
In the C# Editor for the GetEnabledOperations() method that opens, navigate to the case statement for "Edit Person Organization Attributes" and add the following code to the end of the case's if statement to enable the change entered in the form field to be persisted to the database.
|| Form_TargetPerson.IsPropertyChanged(E.PersonColumn.ConfigurationXml) |
EmpowerID stores custom attributes in the ConfigurationXml column of the EmpowerID Identity Warehouse. |
The following image shows what the above code looks like in the C# Editor.
Next, add the custom attribute to any applicable workflow forms so that it shows as a form field to delegated users running connected workflows. In our case, we are going to add the attribute to the PersonEditFullForm form. This form appears when editing people.
From the Components tree of the Form Designer, expand the Resource node and locate the ClearanceLevel attribute you created earlier.
Because we added the attribute to the Edit Person Organization Attributes Executor for the EditPersonMultiOperations Operation activity, we need to add the attribute to the Organization tab of the form. |
Next, create a Localized Text Key for the custom attribute to allow end users to see the attribute's friendly when viewing it in the various EmpowerID Web user interfaces.
Next, the attribute needs to be added to the Person View and Edit pages in the Web so delegated users can see and edit the attribute for a given person. We demonstrate how to do this next.
When customizing Web pages, it is recommended that you use EmpowerID's Override technology rather than modifying the original pages. This allows you to quickly revert back to the original pages if the custom pages are no longer needed. To use the Override technology, you create an Overrides structure in the EmpowerID Web Sites directory and place your custom pages within that directory. For this exercise, we are going to override the ViewOne and EditOne pages for a person by placing the Clearance Level attribute on each of those pages. This allows delegated users the ability to see and edit the attribute on an existing person, as well as providing a value for the attribute when creating new people. |
From your EmpowerID Web server, navigate to EmpowerID > Web Sites directory and open a command prompt. From the command prompt type a command to create the EmpowerID.Web.Overrides directory with all needed sub-directories to override the EditOne page.
If the directory already exists then you do not need to issue the command. |
If you are in the EmpowerID > Web Sites directory, the command should look as follows.
mkdir EmpowerID.Web.Overrides\Areas\Common\Views\EditOne |
The relevant code section is located at or near line 242 and looks as follows:
<div data-bind="eidFormTab: { Title: '@EidResx("Organization", true)' }"> <div data-bind="eidFormSection: { Title: '@EidResx("Organization Information", true)' }"> <div data-bind='eidFormField: { Label: "@EidResx("Title", EscapeMode.HtmlAttribute)", DataFieldName: "Title" }'></div> <div data-bind='eidFormField: { Label: "@EidResx("Location", EscapeMode.HtmlAttribute)", DataFieldName: "Location" }'></div> <div data-bind='eidFormField: { Label: "@EidResx("Department", EscapeMode.HtmlAttribute)", DataFieldName: "Department" }'></div> <div data-bind='eidFormField: { Label: "@EidResx("Department Number", EscapeMode.HtmlAttribute)", DataFieldName: "DepartmentNumber" }'></div> <div data-bind='eidFormField: { Label: "@EidResx("Division", EscapeMode.HtmlAttribute)", DataFieldName: "Division" }'></div> <div data-bind='eidFormField: { Label: "@EidResx("Company", EscapeMode.HtmlAttribute)", DataFieldName: "Company" }'></div> <div data-bind='eidFormField: { Label: "@EidResx("District", EscapeMode.HtmlAttribute)", DataFieldName: "District" }'></div> <div data-bind='eidFormField: { Label: "@EidResx("Office", EscapeMode.HtmlAttribute)", DataFieldName: "Office" }'></div> </div> </div> |
To add a field for the new Clearance Level attribute to the form, add the following line of code, placing it after the line of code for the District field.
<div data-bind='eidFormField: { Label: "@EidResx("Clearance Level", EscapeMode.HtmlAttribute)", DataFieldName: "ClearanceLevel" }'></div> |
Next, override the Edit Person form section of the page to allow delegated users the ability to see and change the value of the attribute when editing other people.
From your text editor, locate the section of the page that is used for editing other people (around line 389). The code that begins this section looks as follows:
@*BEGIN EDIT MODE FORM ################################*@ @if (Model.IsEditMode && EidContext.PersonId != (int)Model.Data.PersonID && EidAuthenticationHandler.HasAccessToPage(new Guid("00bacc31-b2ee-4305-876c-383a03f8796e"))) |
Within this section, modify the code so that a field for the custom attribute appears on the Organization tab of the Edit Person form.
In this example, we are only adding the field to the form when the person being edited by another user. The field will not appear when users are editing themselves. In order to make the field appear for self-service edits, you need to add it to the Organization tab of the the BEGIN SELF SERVICE EDIT MODE FORM section of the page. |
The relevant code section is located at or near line 577 and looks as follows:
<div data-bind="eidFormTab: { Title: '@EidResx("Organization", true)' }"> <div data-bind="eidFormSection: { Title: '@EidResx("Organization Information", true)' }"> <div data-bind='eidFormField: { Label: "@EidResx("Title", EscapeMode.HtmlAttribute)", DataFieldName: "Title" }'></div> <div data-bind='eidFormField: { Label: "@EidResx("Location", EscapeMode.HtmlAttribute)", DataFieldName: "Location" }'></div> <div data-bind='eidFormField: { Label: "@EidResx("Department", EscapeMode.HtmlAttribute)", DataFieldName: "Department" }'></div> <div data-bind='eidFormField: { Label: "@EidResx("DepartmentNumber", EscapeMode.HtmlAttribute)", DataFieldName: "DepartmentNumber" }'></div> <div data-bind='eidFormField: { Label: "@EidResx("Division", EscapeMode.HtmlAttribute)", DataFieldName: "Division" }'></div> <div data-bind='eidFormField: { Label: "@EidResx("Company", EscapeMode.HtmlAttribute)", DataFieldName: "Company" }'></div> <div data-bind='eidFormField: { Label: "@EidResx("District", EscapeMode.HtmlAttribute)", DataFieldName: "District" }'></div> <div data-bind='eidFormField: { Label: "@EidResx("Office", EscapeMode.HtmlAttribute)", DataFieldName: "Office" }'></div> </div> |
To add a field for the new Clearance Level attribute to the form, add the following line of code, placing it after the line of code for the District field.
<div data-bind='eidFormField: { Label: "@EidResx("Clearance Level", EscapeMode.HtmlAttribute)", DataFieldName: "ClearanceLevel" }'></div> |
The ViewOne page is used in EmpowerID for viewing and managing the information associated with a resource object, such as an EmpowerID Person. We will be overriding this page so that the new attribute appears on it. To allow administrators to make quick changes to the form fields that appear on a page, EmpowerID includes a number of System Settings that can modified from the Web interface. For the particular section of the form we are modifying, EmpowerID pulls the value of the PA-PersonDetails-Work system setting, as shown by the below code for the CSHTML file.
<div class="sixcol"> @{ Html.RenderPartial("DataTable", new LocalizedDataListModel(Model.ResourceTypeName) { Caption = EidResx("Page-PersonDetails-Section-GeneralTab-Work", EscapeMode.JavaScript).ToString(), Collapsed = false, Items = Model.SelectProperties(Config<String>("PA-PersonDetails-Work").Split(',')) }); } |
To add the custom attribute to the ViewOne page, you add it to the relevant section of the form, which in this case is the Work section.
In the Value field, add Clearance Level after Location. Be sure to add a comma after Location as the CSHTML file is looking for comma-separated values.
Click Recycle EmpowerID AppPools.
If you do not see the Recycle EmpowerID AppPools, this means you do not have access to the Workflow. If this is the case, recycle IIS from the command line. |
In this exercise, we will test the custom attribute by creating a new EmpowerID Person and then edit the person by setting a value for the attribute from the person's EditOne page.
Set the primary Business Role and Location for the person to Temporary Role in Temporary Location.
|
The General tab of the form should similar to the below image.
Click the Organization tab.
You should see the Clearance Level field.
Enter a value—such as Executive—in the Clearance Level field and then click Save.
After EmpowerID creates the person, your browser is directed to the Edit form for the person. You will use this page to edit the attribute in the next section. |
|