68
Event Handlers Guide Version 8.0 | October 14, 2013 For the most recent version of this document, visit our developer's website .

Relativity Event Handlers Guide · Relativity|EventHandlersGuide-2 TableofContents 1Eventhandlersoverview 3 1.1Gettingstarted 3 1.2Eventhandlerclasslibraries 3 2Gettingstartedwitheventhandlers

  • Upload
    vantruc

  • View
    245

  • Download
    0

Embed Size (px)

Citation preview

Event Handlers GuideVersion 8.0 | October 14, 2013

For the most recent version of this document, visit our developer's website.

Relativity | Event Handlers Guide - 2

Table of Contents

1 Event handlers overview 3

1.1 Getting started 3

1.2 Event handler class libraries 3

2 Getting started with event handlers 3

2.1 Creating event handlers 4

2.1.1 Console event handlers 8

2.1.2 Pre Cascade Delete event handlers 12

2.1.3 Pre Delete event handlers 16

2.1.4 Pre Load event handlers 20

2.1.5 Pre Save event handlers 27

2.1.6 Post Save event handlers 37

2.1.7 Pre and Post Install event handlers overview 41

2.2 Adding assembly source information 57

3 Guidelines for event handlers 58

3.1 Using event handler rules 58

3.1.1 Scenario 1 58

3.1.2 Scenario 2 59

3.2Working with Choice objects 59

3.2.1 Sample Code for choice fields 59

3.3 Remotely debugging event handlers 61

4 Upgrading event handlers 63

4.1 Upgrading to Relativity 8 from 7.x or earlier 64

4.2 Upgrading syncs and Relativity 6.x or earlier installations 65

4.3 Syntax conversion for event handlers 65

Relativity | Event Handlers Guide - 3

1 Event handlers overviewEvent handlers provide you with the ability to apply custom business logic at several different points in thereview process in Relativity. You can attach business logic to documents and Dynamic Objects via modulesthat do not require knowledge of the inner workings of Relativity and are stored outside the case database.

1.1 Getting startedFor example, you can use event handlers to perform the following tasks in Relativity:

n Validate and auto-populate document and Dynamic Object fields based on reviewer coding in layoutsn Send emails to specified usersn Query and update a user’s Relativity workspace databasen Consider fields required if certain criteria aremet

Note: You can identify fields with a unique GUID when building Event Handlers.

After event handlers are loaded, you can attach them to either a document object or Dynamic Objects withina workspace.To begin working with event handlers in Relativity, see Getting started with event handlers below.

1.2 Event handler class librariesThe Relativity developers site also includes fully commented class libraries for Relativity event handlers.

2 Getting started with event handlersSee the following types of event handlers supported by Relativity:

n Console event handlers on page 8n Pre Cascade Delete event handlers on page 12n Pre Delete event handlers on page 16n Pre Load event handlers on page 20n Pre Save event handlers on page 27n Post Save event handlers on page 37n Pre Install event handlers on page 42n Post Install event handlers on page 49

The type of event handler determines how and when the handler is executed or displayed. These eventhandler rules are object-specific, meaning that once a rule is implemented on a Relativity object type, it willoperate on all instances of that object type.Using Microsoft Visual Studio 2010, you can create and use event handlers in Relativity applications. For moreinformation, see Creating event handlers on the next page. The basic process of creating and using eventhandlers in Relativity includes the following steps.

Relativity | Event Handlers Guide - 4

1. Create and compile an event handler in Visual Studio.2. In Relativity, upload the .dll as a new resource file through the Resource Files tab in Admin mode. See

the Admin Guide for more information.3. Associate the event handlers with one or more object types within one or many Relativity workspaces.

For more information on using event handler rules and using event handlers in workspaces, see Guidelines forevent handlers on page 58.

2.1 Creating event handlersIf you're a developer, you can create several types of event handlers in Relativity using Microsoft Visual Studiowith .NET framework v4.0.For more information, see Development environment guidelines on the Relativity 8 developers site.All event handler projects must meet certain requirements. Specifically, each project must:

n Inherit from an event handler class.n Return a response object.n Access only fields on the layout or fields specified in the required fields property.n Cast field values into their appropriate classes.

Perform the following steps to create any type of Relativity event handler in Visual Studio.

1. Create a new solution in Visual Studio 2010 or higher.

Note: When a custom assembly is loaded for an event handler, agent, or custom page, common .dll files (suchas kCura.Relativity.Client.dll) are automatically copied into the domain from the lib folder. If you set the SpecificVersion option in Visual Studio, it may not match the version of the .dll files installed on the environment. Thismismatch may prevent your application from executing properly, if at all.

2. Add references to the following .dll files:n kCura.Relativity.Client.dlln kCura.EventHandler.dlln Relativity.API.dll - Use themethods on IEHHelper class to return a database context, a con-

nection to the Services API, and authentication tokens. For more information, see the API helperclasses on the Relativity 8 developers site.

Note: While the DatabaseConnection property, MasterDatabaseConnection property, and theGetAuthenticationToken method on the EventHandlerBase class are still supported, we recommendusing the Relativity API helper classes for any new development.

3. Define an attribute that provides a Description for each event handler.4. Add the appropriate event handler class to the project. Rename the class and the file name accordingly.5. Include any additional handler-specific requirements:

n Console event handlers on page 8n Pre Cascade Delete event handlers on page 12n Pre Delete event handlers on page 16n Pre Load event handlers on page 20n Pre Save event handlers on page 27n Post Save event handlers on page 37

Relativity | Event Handlers Guide - 5

n Pre Install event handlers on page 42n Post Install event handlers on page 49

6. In the Visual Studio Solution Explorer, right-click on the project name and select Properties.

Relativity | Event Handlers Guide - 6

7. In the Properties window's build tab, changeOption Strict to On.

8. In the Solution Explorer, right-click on the project name and select Add Reference.

9. Browse for and select kCura.EventHandler.dll on your Relativity Agent server at C:\Program Files (x86)\kCura Corporation\Relativity\Agents\bin.

Relativity | Event Handlers Guide - 7

10. Save the completed project as a .dll file.11. In Relativity, go to Adminmode and select the Resource Files tab. See the Admin Guide for more

information.

12. Click New Resource File.13. Click Browse in the Resource File field, and select the .dll file.

14. Click in the Application field and select the application that you want associated with the .dll file.Click OK.

15. Click Save. After the .dll is available as a resource file (that is as an assembly), you can apply it to objectsin a workspace.

Note: Contact kCura Support at [email protected] for assistance with setting up assemblies.

Relativity | Event Handlers Guide - 8

2.1.1 Console event handlersConsole event handlers are constructed and shown on the object view page. The console has a method(GetConsole), which should return a Console event handler. The console should provide header text and abutton collection. When a console button is clicked, a method is called in the event handler, and the consolecan take further action to complete a task.Console event handlers support custom HTML elements. These event handlers work for Dynamic Objects.

2.1.1.1 Creating Console event handlersPerform the following steps to create a Console event handler in Relativity.

1. Create a new solution in Visual Studio.2. Add references to the following .dll files:

n kCura.Relativity.Client.dlln kCura.EventHandler.dlln Relativity.API.dll - Use themethods on IEHHelper class to return a database context, a con-

nection to the Services API, and authentication tokens. For more information, see the API helperclasses on the Relativity 8 developers site.

Note: While the DatabaseConnection property, MasterDatabaseConnection property, and theGetAuthenticationToken method on the EventHandlerBase class are still supported, we recommendusing the Relativity API helper classes for any new development.

3. Define an attribute that provides a description for the Console event handler.4. Inherit from the ConsoleEventHandler class. All Console Handlers must inherit from this class.5. Override the GetConsole() method, which returns a console containing buttons and header text. All

ConsoleEventHandlers must override this method.6. Override the OnButtonClick() method, which executes when a button click is detected.7. Override the RequiredFields property, which detects required fields when a console button is clicked.

All ConsoleEventHandlers must override this property.

Note: The ActiveArtifact.Fields collection includes the fields returned by the RequiredFields property as well asthose on the current layout. It also includes the values of these fields.

8. Review the following sample code for the SampleConsoleEventHandler and ApplicationConstantsclasses.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Relativity.API;

using kCura.EventHandler;

using kCura.Relativity.Client;

using Relativity.Samples.Core;

Relativity | Event Handlers Guide - 9

namespace Relativity.Samples.EventHandlers {

/// <summary>

/// This event handler creates a console that has a button to insert a job in a

queue table in the EDDS database

/// The job should be picked up by an agent in the environment which will do some

background processing

/// </summary>

[kCura.EventHandler.CustomAttributes.Description("Sample Console Event Handler")]

class SampleConsoleEventHandler : kCura.EventHandler.ConsoleEventHandler {

private const String INSERT_JOB_BUTTON_NAME = "_insertJobButton";

private ConsoleButton _insertJobButton = new ConsoleButton() { Name = INSERT_

JOB_BUTTON_NAME, DisplayText = "Insert Job", RaisesPostBack = true, ToolTip =

"Inserts a job into the queue table" };

public override kCura.EventHandler.Console GetConsole

(kCura.EventHandler.ConsoleEventHandler.PageEvent pageEvent) {

kCura.EventHandler.Console retVal = new kCura.EventHandler.Console();

retVal.ButtonList = GetButtons();

retVal.Title = "Job Runner Console";

if (pageEvent == PageEvent.Load) {

kCura.EventHandler.Field containsExtractedTextField =

this.ActiveArtifact.Fields[ApplicationConstants.CONTAINS_EXTRACTEDTEXT_

FIELD_GUID.ToString()];

Boolean containsExtractedText =

((containsExtractedTextField.Value.Value != null) && ((Boolean)

containsExtractedTextField.Value.Value == true));

//Only query for the job if there is extracted text.

Boolean noJobExists = (containsExtractedText && !JobQueries.JobExists

(this.Helper.GetDBContext(-1), this.Helper.GetActiveCaseID(),

this.ActiveArtifact.ArtifactID));

Boolean showButton = noJobExists;

if (showButton) {

_insertJobButton.Enabled = true;}

else {

_insertJobButton.Enabled = false;}

}

Relativity | Event Handlers Guide - 10

return retVal;

}

private List<ConsoleButton> GetButtons() {

List<ConsoleButton> retVal = new List<ConsoleButton>(1);

retVal.Add(_insertJobButton);

return retVal;

}

public override void OnButtonClick(ConsoleButton consoleButton) {

switch (consoleButton.Name) {

case INSERT_JOB_BUTTON_NAME:

InsertJob(this.Helper.GetDBContext(-1), this.Helper.GetActiveCaseID(),this.ActiveArtifact.ArtifactID);break;

}

}

private static void InsertJob(IDBContext dbContext, Int32

workspaceArtifactID, Int32 artifactID) {

JobQueries.InsertJob(dbContext, workspaceArtifactID, artifactID);

}

public override FieldCollection RequiredFields {

get {

FieldCollection retVal = new FieldCollection();

retVal.Add(new kCura.EventHandler.Field(ApplicationConstants.CONTAINS_

EXTRACTEDTEXT_FIELD_GUID));

return retVal;

}

}

}

}

This class references the constants defined in the following sample code. These constants are based onthe GUIDs of Artifacts (such as Fields, Choices, Object Types, and so on) used in a sample application.See the Application Deployment System Guide for more information.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace Relativity.Samples.Core

{

Relativity | Event Handlers Guide - 11

public static class ApplicationConstants

{

public static readonly Guid OLD_CODING_FIELD_GUID = new Guid("eb174a88-068e-

40a9-8835-5f020f80a089");

public static readonly Guid NAME_FIELD_GUID = new Guid("0761E9D6-F775-48BB-

91CC-1DB39B2CE9F0");

public static readonly Guid RELATED_DOCUMENT_FIELD_GUID = new Guid("513B40E6-

1FC5-4E59-9191-1C52D66C89AD");

public static readonly Guid CONTAINS_EXTRACTEDTEXT_FIELD_GUID = new Guid

("181724B3-AB4E-4E9F-82B7-6852D7DBFCBE");

public static readonly Guid HAS_IMAGES_FIELD_GUID = new Guid("DB80E2CB-CE37-

4813-8AAA-45AE54C8AB7B");

}

}

9. Use the following codes samples as templates for logging in to the Services API with your event handler.For more information, see Relativity Services API or Relativity Patch 8.0.291.1 Guidelines on the Relativ-ity 8 developers site.Logging in as a system userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.System))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in with a username and passwordRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Relativity | Event Handlers Guide - 12

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.Manual))

{

// Proxy created.

proxy.LoginWithCredentials("[email protected]", "Test1234!");

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in as the current userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.CurrentUser))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

10. Import the compiled .dll file using the Resource Files tab in Adminmode. See the Admin Guide formore information.

2.1.2 Pre Cascade Delete event handlersYou can call a Pre Cascade Delete event handler when a user attempts to force the deletion of an object withdependent objects. The user must haveDelete Object Dependencies permissions to perform this action.You can use this type of event handler to verify that no restrictions exist on an object selected for deletion.For example, the Processing Set Pre Cascade Delete event handler prevents the deletion of a processing setwhile it is undergoing processing.In a forced delete operation, the Pre Cascade Delete event runs before Relativity begins deleting child objectsor unlinking associative objects. (Relativity deletes any child objects and unlinks any associative objects. Aftercompleting these operations, it deletes the parent object. The Pre Cascade Delete event handler ensures thatthe system initiates a forced delete operation only when it can successfully delete the objects.)

Relativity | Event Handlers Guide - 13

Note: We recommend using a Pre Delete event handler in conjunction with Pre Cascade Delete eventhandler. For more information, see Pre Delete event handlers on page 16. The Pre Cascade Delete eventhandler won't run when no associated or child objects exist.

2.1.2.1 Creating Pre Cascade Delete event handlersComplete the following steps to create a Pre Cascade Delete event handler in Relativity.

1. Create a new solution in Visual Studio.2. Add references to the following .dll files:

n kCura.Relativity.Client.dlln kCura.EventHandler.dlln Relativity.API.dll - Use themethods on IEHHelper class to return a database context, a

connection to the Services API, and authentication tokens. For more information, see the APIhelper classes on the Relativity 8 developers site.

Note: While the DatabaseConnection property, MasterDatabaseConnection property, and theGetAuthenticationToken method on the EventHandlerBase class are still supported, we recommendusing the Relativity API helper classes for any new development.

3. Define an attribute that provides a description for the Pre Cascade Delete event handler.4. Inherit from the PreCascadeDeleteEventHandler class. All Pre Cascade Delete event handlers must

inherit from this class.5. Override the RequiredFields property. You must override this property even though a Pre Cascade

Delete event handler doesn't use it.

using System;using System.Collections.Generic;using System.Linq;using System.Text;using kCura.EventHandler;

namespace CustomEventHandler{

[kCura.EventHandler.CustomAttributes.Description("A description of theevent handler.")]public class TaskPreCascadeDeleteEventHandeler :PreCascadeDeleteEventHandler{

public override Response Execute() {Response resp = new Response() {

Success = true,Message = ""

};

try {

Relativity | Event Handlers Guide - 14

// Execute code specific for the event here.// To prevent the Cascade Delete, the event handler mustreturn an exception, in addition to setting success = false.// i.e., resp.Success = false; resp.Exception = newSystemException(“Some exception message”);

}catch (Exception e) {

resp.Success = false;resp.Exception = new SystemException("ProcessPreDeleteFailurefailure: "

+ e.Message);}return resp;

}public override FieldCollection RequiredFields{

// Add any required fields.// If none are required, you can initialize a new collectioninstead.get { return new FieldCollection(); }

}public override void Commit(){

// Commit is unused (never gets called) in a Pre Cascade Deleteevent.

}public override void Rollback(){

// Code executes when an error occurs in the process.// If you're retrieving information that requires rollback inExecute() method, then handle this here, otherwise leave itblank.

}}

}

6. Use the following codes samples as templates for logging in to the Services API with your event handler.For more information, see Relativity Services API or Relativity Patch 8.0.291.1 Guidelines on the Relativ-ity 8 developers site.Logging in as a system userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

Relativity | Event Handlers Guide - 15

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.System))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in with a username and passwordRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.Manual))

{

// Proxy created.

proxy.LoginWithCredentials("[email protected]", "Test1234!");

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in as the current userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.CurrentUser))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

Relativity | Event Handlers Guide - 16

}

catch (Exception e)

{

// Proxy failure: e.Message

}

7. Import the compiled .dll file using the Resource Files tab in Adminmode. See the Admin Guide formore information.

2.1.3 Pre Delete event handlersPre Delete event handlers execute after a user has clicked the Delete button or performed aMass Delete inRelativity. Once Relativity deletes the intended object, the delete is committed in the event handler. If anerror occurs anywhere in this process, a rollback method is triggered and code is called. You can then executedifferent code in the rollback method.Pre Delete event handlers work for the following:

n Documentsn Dynamic Objectsn Mass Delete

We recommend using a Pre Cascade Delete event handler with the Pre Delete event handler to performvalidation on delete. For more information, see Pre Cascade Delete event handlers on page 12. If you only usea Pre Delete event handler, any associated objects will remain unlinked and child objects will be deletedbefore Pre Delete cancels the delete.

2.1.3.1 Creating Pre Delete event handlersComplete the following steps to create a Pre Delete event handler.

1. Create a new solution in Visual Studio.2. Add references to the following .dll files:

n kCura.Relativity.Client.dlln kCura.EventHandler.dlln Relativity.API.dll - Use themethods on IEHHelper class to return a database context, a

connection to the Services API, and authentication tokens. For more information, see the APIhelper classes on the Relativity 8 developers site.

Note: While the DatabaseConnection property, MasterDatabaseConnection property, and theGetAuthenticationToken method on the EventHandlerBase class are still supported, we recommendusing the Relativity API helper classes for any new development.

3. Define an attribute that provides a description for the Pre Delete event handler.4. Inherit form the PreDeleteEventHandler class. Pre Delete event handlers must inherit from this class.5. Override the Commit() method, which executes after the object is deleted from the database.6. Override the Execute() method, which executes when the handler is triggered.7. Override the Rollback() method, which executes if there's an error during deletion.

Relativity | Event Handlers Guide - 17

8. Override the RequiredFields property, which detects required fields for deleting an object.

Note: The ActiveArtifact.Fields collection includes the fields returned by the RequiredFields property as well asthose on the current layout. It also includes the values of these fields.

9. Review the following sample code for the TaskPostSaveEventHandler class.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using kCura.EventHandler;

using kCura.Relativity.Client;

namespace CustomEventHandler

{

[kCura.EventHandler.CustomAttributes.Description("A description of the event

handler.")]

class TaskPostSaveEventHandler : PreDeleteEventHandler

{

public override Response Execute()

{

Response resp = new Response()

{

Success = true,

Message = ""

};

try

{

// Execute code specific for the event here.

}

catch (EndpointTypeCollectionInvalidException ex)

{

foreach (KeyValuePair<string, Dictionary<EndpointType, Exception>>

exceptionCollection in ex.EndpointTypeExceptions)

{

foreach (KeyValuePair<EndpointType, Exception> endpointTypeExceptionPair inexceptionCollection.Value){resp.Success = false;resp.Message = "TaskPreDeleteFailure: " + ex.Message;}

}

}

Relativity | Event Handlers Guide - 18

catch (Exception e)

{

resp.Success = false;

resp.Message = "TaskPreDeleteFailure failure: " + e.Message;

}

return resp;

}

public override void Commit()

{

// Commit is unused (never gets called) in Pre Cascade Delete event

handlers.

}

public override void Rollback()

{

// If you're retrieving information that requires rollback in Execute()

method, then handle it here, otherwise leave it blank.

}

public override FieldCollection RequiredFields

{

// Add any required fields.

// If none are required, you can initialize a new collection instead.

get { return new FieldCollection(); }

}

}

}

10. Use the following codes samples as templates for logging in to the Services API with your event handler.For more information, see Relativity Services API or Relativity Patch 8.0.291.1 Guidelines on the Relativ-ity 8 Documentation site.Logging in as a system userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.System))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

Relativity | Event Handlers Guide - 19

{

// Proxy failure: e.Message

}

Logging in with a username and passwordRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.Manual))

{

// Proxy created.

proxy.LoginWithCredentials("[email protected]", "Test1234!");

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in as the current userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.CurrentUser))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

11. Import the compiled .dll file using the Resource Files tab in Adminmode. See the Admin Guide formore information.

Relativity | Event Handlers Guide - 20

2.1.4 Pre Load event handlersPre Load event handlers execute before a New, Edit or View page loads. Pre Load event handlers are oftenused to populate default values.You can use Pre Load event handlers for Dynamic Objects and documents, and all fields are fully supported.Note the following field behavior for Pre Load event handlers:

n Both normal and read-only field modes are supported.n Read-only fields that are updated by a Pre Load event handler won't be saved to the database on Edit

pages.n Reflected fields are properly passed into Pre Load event handlers. However, changes made to reflected

fields won't persist on the page.

2.1.4.1 Creating Pre Load event handlersComplete the following steps to create a Pre Load event handler in Relativity.

1. Create a new solution in Visual Studio.2. Add references to the following .dll files:

n kCura.Relativity.Client.dlln kCura.EventHandler.dlln Relativity.API.dll - Use themethods on IEHHelper class to return a database context, a con-

nection to the Services API, and authentication tokens. For more information, see the API helperclasses on the Relativity 8 developers site.

Note: While the DatabaseConnection property, MasterDatabaseConnection property, and theGetAuthenticationToken method on the EventHandlerBase class are still supported, we recommendusing the Relativity API helper classes for any new development.

3. Define an attribute that provides a description for the Pre Load event handler.4. Inherit from the PreLoadEventHandler class. All Pre Load event handlers must inherit from this class.5. Override the RequiredFields property. This property to specify additional fields that the event handler

accessed but may not be on the layout. All Pre Load event handlers must override this property.6. Review the following sample code for the SamplePreLoadEventHandler class.

Relativity 8.0.275.3 or below - view code using the ArtifactManagerProxy class

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Relativity.Samples.Core;

using kCura.EventHandler;

using Relativity.API;

using kCura.Relativity.Client;

namespace Relativity.Samples.EventHandlers {

/// <summary>

Relativity | Event Handlers Guide - 21

/// This event handler executes before the page loads and selects the first

available Document in the Workspace.

/// It sets the Related Document field with the ArtifactID of the Document.

/// </summary>

[kCura.EventHandler.CustomAttributes.Description("Sample Pre-Load Event

Handler")]

class SamplePreLoadEventHandler : kCura.EventHandler.PreLoadEventHandler {

public override kCura.EventHandler.Response Execute() {

kCura.EventHandler.Response retVal = new Response();

retVal.Success = true;

retVal.Message = String.Empty;

try {

if (this.ActiveArtifact.IsNew && this.PageMode ==

kCura.EventHandler.Helper.PageMode.Edit) {

// This example illustrates how to use the Services API to query for the first available Document// and how to set a field called relatedDocsField.IArtifactManager proxy = newArtifactManagerProxy

// Get the Services API URI.(this.Helper.GetServicesManager().GetServicesURL());

//Obtain an authentication token to login to the Services API.proxy.TokenLogin(this.Helper.GetAuthenticationManager().GetAuthenticationToken());

// Set the ArtifactID on aWorkspace on the APIOptions object.proxy.APIOptions.WorkspaceID = this.Helper.GetActiveCaseID();

// Enable StrictMode to return a standard set of Fields.proxy.APIOptions.StrictMode = true;

kCura.Relativity.Client.DTOs.Query<kCura.Relativity.Client.DTOs.Document> docQuery =newkCura.Relativity.Client.DTOs.Query<kCura.Relativity.Client.DTOs.Document>();

docQuery.Fields = kCura.Relativity.Client.DTOs.FieldValue.AllFields;

Relativity | Event Handlers Guide - 22

docQuery.Condition = newWholeNumberCondition("Artifact ID",NumericConditionEnum.GreaterThan, 0);

var docResults = proxy.Repositories.Document.Query(docQuery, 1);

if (docResults.Success && docResults.Results.Count > 0) {kCura.Relativity.Client.DTOs.Document docDTO = docResults.Results[0].Artifact;kCura.EventHandler.Field relatedDocsField =this.ActiveArtifact.Fields[ApplicationConstants.RELATED_DOCUMENT_FIELD_GUID.ToString()];relatedDocsField.Value.Value = docDTO.ArtifactID;}else {thrownew System.Exception("No document was found. Message: " + docResults.Message);}

}

}

catch (System.Exception ex) {

retVal.Success = false;

retVal.Message = ex.ToString();

}

return retVal;

}

public override kCura.EventHandler.FieldCollection RequiredFields {

get {

FieldCollection retVal = new FieldCollection();

retVal.Add(new kCura.EventHandler.Field(ApplicationConstants.RELATED_

DOCUMENT_FIELD_GUID));

return retVal;

}

}

}

}

This class references the constants defined in the following sample code. These constants are based onthe GUIDs of Artifacts (such as Fields, Choices, Object Types, and so on) used in a sample application.See the Application Deployment System Guide for more information.

using System;

using System.Collections.Generic;

using System.Linq;

Relativity | Event Handlers Guide - 23

using System.Text;

namespace Relativity.Samples.Core

{

public static class ApplicationConstants

{

public static readonly Guid OLD_CODING_FIELD_GUID = new Guid("eb174a88-068e-

40a9-8835-5f020f80a089");

public static readonly Guid NAME_FIELD_GUID = new Guid("0761E9D6-F775-48BB-

91CC-1DB39B2CE9F0");

public static readonly Guid RELATED_DOCUMENT_FIELD_GUID = new Guid("513B40E6-

1FC5-4E59-9191-1C52D66C89AD");

public static readonly Guid CONTAINS_EXTRACTEDTEXT_FIELD_GUID = new Guid

("181724B3-AB4E-4E9F-82B7-6852D7DBFCBE");

public static readonly Guid HAS_IMAGES_FIELD_GUID = new Guid("DB80E2CB-CE37-

4813-8AAA-45AE54C8AB7B");

}

}

Relativity 8.0.291.1 or above - view code using the RSAPIClient class

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Relativity.Samples.Core;

using kCura.EventHandler;

using Relativity.API;

using kCura.Relativity.Client;

namespace Relativity.Samples.EventHandlers {

///<summary>

///This event handler will execute before the page loads and select the first

available document

///in the workspace and set the Related Document field with the document

artifactID.

///</summary>

[kCura.EventHandler.CustomAttributes.Description("Sample Pre-Load Event

Handler")]

class SamplePreLoadEventHandler : kCura.EventHandler.PreLoadEventHandler {

public override kCura.EventHandler.Response Execute() {

kCura.EventHandler.Response retVal = new Response();

retVal.Success = true;

Relativity | Event Handlers Guide - 24

retVal.Message = String.Empty;

try {

//This example shows how to use the RSAPI to query for the first

available document

//and set a field.

if (this.ActiveArtifact.IsNew && this.PageMode ==

kCura.EventHandler.Helper.PageMode.Edit) {

//Create an instance of the IRSAPIClient using the context of the current user//logged into the system.using (IRSAPIClient proxy = this.Helper.GetServicesManager().CreateProxy<IRSAPIClient>(ExecutionIdentity.CurrentUser)) { 

//Set the workspace ArtifactID on the APIOptions object.proxy.APIOptions.WorkspaceID = this.Helper.GetActiveCaseID();

//Enable strict mode.proxy.APIOptions.StrictMode = true;

kCura.Relativity.Client.DTOs.Query<kCura.Relativity.Client.DTOs.Document> docQuery =newkCura.Relativity.Client.DTOs.Query<kCura.Relativity.Client.DTOs.Document>();

docQuery.Fields = kCura.Relativity.Client.DTOs.FieldValue.AllFields;docQuery.Condition = newWholeNumberCondition("Artifact ID",NumericConditionEnum.GreaterThan, 0);

var docResults = proxy.Repositories.Document.Query(docQuery, 1);

if (docResults.Success && docResults.Results.Count > 0) {

kCura.Relativity.Client.DTOs.Document docDTO = docResults.Results[0].Artifact;

kCura.EventHandler.Field relatedDocsField = this.ActiveArtifact.Fields[ApplicationConstants.RELATED_DOCUMENT_FIELD_GUID.ToString()];

relatedDocsField.Value.Value = docDTO.ArtifactID;

Relativity | Event Handlers Guide - 25

}else {thrownew System.Exception("No document was found. Message: " + docResults.Message);}}

}

}

catch (System.Exception ex) {

retVal.Success = false;

retVal.Message = ex.ToString();

}

return retVal;

}

public override kCura.EventHandler.FieldCollection RequiredFields {

get {

FieldCollection retVal = new FieldCollection();

retVal.Add(new kCura.EventHandler.Field(ApplicationConstants.RELATED_

DOCUMENT_FIELD_GUID));

return retVal;

}

}

}

}

This class references the constants defined in the following sample code. These constants are based onthe GUIDs of Artifacts (such as Fields, Choices, Object Types, and so on) used in a sample application.See the Application Deployment System Guide for more information.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace Relativity.Samples.Core

{

public static class ApplicationConstants

{

public static readonly Guid OLD_CODING_FIELD_GUID = new Guid("eb174a88-068e-

40a9-8835-5f020f80a089");

public static readonly Guid NAME_FIELD_GUID = new Guid("0761E9D6-F775-48BB-

91CC-1DB39B2CE9F0");

Relativity | Event Handlers Guide - 26

public static readonly Guid RELATED_DOCUMENT_FIELD_GUID = new Guid("513B40E6-

1FC5-4E59-9191-1C52D66C89AD");

public static readonly Guid CONTAINS_EXTRACTEDTEXT_FIELD_GUID = new Guid

("181724B3-AB4E-4E9F-82B7-6852D7DBFCBE");

public static readonly Guid HAS_IMAGES_FIELD_GUID = new Guid("DB80E2CB-CE37-

4813-8AAA-45AE54C8AB7B");

}

}

7. Use the following codes samples as templates for logging in to the Services API with your event handler.For more information, see Relativity Services API or Relativity Patch 8.0.291.1 Guidelines on the Relativ-ity 8 developers site.Logging in as a system userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.System))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in with a username and passwordRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.Manual))

{

// Proxy created.

proxy.LoginWithCredentials("[email protected]", "Test1234!");

// Add your custom code for working with the RSAPIClient.

}

Relativity | Event Handlers Guide - 27

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in as the current userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.CurrentUser))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

8. Import the compiled .dll file using the Resource Files tab in Adminmode. See the Admin Guide formore information.

2.1.5 Pre Save event handlersPre Save classes execute after the user has clicked the Save or Save & Next button after changing a value forat least one field on a layout and before an object’s information has been written to the database. Theinformation can bemanipulated before it's stored, and the save process may be aborted if specific criteriaaren't met.Common uses for Pre Save event handler are:

n Data validationn Auto-updating field valuesn Adding additional data to record during Save based on coded information

2.1.5.1 Creating Pre Save event handlersComplete the following steps to create a Pre Save event handler in Relativity.

1. Create a new solution in Visual Studio 2010 or higher.2. Add references to the following .dll files:

n kCura.Relativity.Client.dlln kCura.EventHandler.dll

Relativity | Event Handlers Guide - 28

n Relativity.API.dll - Use themethods on IEHHelper class to return a database context, a con-nection to the Services API, and authentication tokens. For more information, see the API helperclasses on the Relativity 8 developers site.

Note: While the DatabaseConnection property, MasterDatabaseConnection property, and theGetAuthenticationToken method on the EventHandlerBase class are still supported, we recommendusing the Relativity API helper classes for any new development.

3. Define an attribute that provides a description for the Pre Save event handler.4. Inherit from PreSaveEventHandler. All Pre Save event handlers must inherit from this class.5. Override the Execute() method, which executes when the event handler is triggered.6. Override the RequiredFields property, which represents fields that are required on object creation.

Note: The ActiveArtifact.Fields collection includes the fields returned by the RequiredFields property as well asthose on the current layout. It also includes the values of these fields.

7. Review the following sample codes for the SamplePreSaveEventHandler class.Relativity 8.0.275.3 or below - view code using the ArtifactManagerProxy class

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using kCura.EventHandler;

using Relativity.API;

using kCura.Relativity.Client;

using System.Data.SqlClient;

using Relativity.Samples.Core;

namespace Relativity.Samples.EventHandlers

{

///<summary>

///This is an example of a Pre Save event handler automatically populates

///two field values

///before persisting a user selection to the database.

///It illustrates how to query the Document artifact using the Services API,

///how to obtain the

///Has Images field, and how to use an SQL connection.

///</summary>

[kCura.EventHandler.CustomAttributes.Description("Sample Pre-Save Event

Handler"),

System.Runtime.InteropServices.Guid("207f6e4e-6204-4df3-8264-d0166cd33a77")]

public class SamplePreSaveEventHandler : kCura.EventHandler.PreSaveEventHandler

{

Relativity | Event Handlers Guide - 29

public override kCura.EventHandler.Response Execute()

{

// Initialize response value and set default values.

Response retVal = new Response();

retVal.Success = true;

retVal.Message = string.Empty;

try

{

Boolean hasExtractedText;

Boolean hasImages;

kCura.EventHandler.Field hasExtractedTextField =

this.ActiveArtifact.Fields[ApplicationConstants.CONTAINS_EXTRACTEDTEXT_FIELD_GUID.ToString()];

kCura.EventHandler.Field hasImagesField =

this.ActiveArtifact.Fields[ApplicationConstants.HAS_IMAGES_FIELD_GUID.ToString()];

if (this.ActiveArtifact.Fields[ApplicationConstants.RELATED_DOCUMENT_

FIELD_GUID.ToString()].Value.Value != null)

{

// Get the selected document.Int32docArtifactID =(Int32)this.ActiveArtifact.Fields[ApplicationConstants.RELATED_DOCUMENT_FIELD_GUID.ToString()].Value.Value;

// Get the Services API URI from the system.Uri servicesAPIUri = this.GetServicesManager().GetServicesURL();

// Initiate an instance of ArtifactManagerProxy.IArtifactManager proxy = newArtifactManagerProxy(servicesAPIUri);String authToken = this.GetAuthenticationManager().GetAuthenticationToken();

// Get an authentication token to login to the Services API.proxy.TokenLogin(authToken);

// Set the active workspaceID on the APIOptions object.proxy.APIOptions.WorkspaceID = this.Helper.GetActiveCaseID();proxy.APIOptions.StrictMode = true;

Relativity | Event Handlers Guide - 30

kCura.Relativity.Client.DTOs.Document docDTO = new kCura.Relativity.Client.DTOs.Document(docArtifactID);

//Get the Document by ArtifactID.docDTO = proxy.Repositories.Document.Read(docArtifactID).Results[0].Artifact;

hasImages = String.Equals(docDTO.Fields.Where(field => String.Equals(field.Name, "Has Images",StringComparison.OrdinalIgnoreCase)).Single().ValueAsSingleChoice.Name, "Yes",StringComparison.OrdinalIgnoreCase);

// You can also write a SQL query against the database// using the IDBContext interface if necessary.String sql = "SELECT DATALENGTH([ExtractedText]) AS ExtractedTextCountFROM [Document] WITH(NOLOCK)WHERE [ArtifactID] =@ArtifactID";

SqlParameter artifactIDParam = new SqlParameter("@ArtifactID", System.Data.SqlDbType.Int);artifactIDParam.Value = docArtifactID;

hasExtractedText =this.Helper.GetDBContext(this.Helper.GetActiveCaseID()).ExecuteSqlStatementAsScalar<Int64>(sql,newSqlParameter[] { artifactIDParam }) > 0;

hasImagesField.Value.Value = hasImages;hasExtractedTextField.Value.Value = hasExtractedText;

}

else

{

hasExtractedTextField.Value.Value = null;}

}

catch (System.Exception ex)

{

retVal.Message = ex.ToString();

// Set Success to false because an exception occurred.

retVal.Success = false;

}

Relativity | Event Handlers Guide - 31

return retVal;

}

public override kCura.EventHandler.FieldCollection RequiredFields

{

get

{

//Return a list of fields necessary for this event handler.

FieldCollection retVal = new FieldCollection();

retVal.Add(new kCura.EventHandler.Field(ApplicationConstants.NAME_

FIELD_GUID));

retVal.Add(new kCura.EventHandler.Field(ApplicationConstants.RELATED_

DOCUMENT_FIELD_GUID));

retVal.Add(new kCura.EventHandler.Field(ApplicationConstants.CONTAINS_

EXTRACTEDTEXT_FIELD_GUID));

retVal.Add(new kCura.EventHandler.Field(ApplicationConstants.HAS_

IMAGES_FIELD_GUID));

return retVal;

}

}

private IAuthenticationMgr GetAuthenticationManager()

{

return this.Helper.GetAuthenticationManager();

}

private IServicesMgr GetServicesManager()

{

return this.Helper.GetServicesManager();

}

}

}

This class references the constants defined in the following sample code. These constants are based onthe GUIDs of Artifacts (such as Fields, Choices, Object Types, and so on) used in a sample application.See the Application Deployment System Guide for more information.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

Relativity | Event Handlers Guide - 32

namespace Relativity.Samples.Core

{

public static class ApplicationConstants

{

public static readonly Guid OLD_CODING_FIELD_GUID = new Guid("eb174a88-068e-

40a9-8835-5f020f80a089");

public static readonly Guid NAME_FIELD_GUID = new Guid("0761E9D6-F775-48BB-

91CC-1DB39B2CE9F0");

public static readonly Guid RELATED_DOCUMENT_FIELD_GUID = new Guid("513B40E6-

1FC5-4E59-9191-1C52D66C89AD");

public static readonly Guid CONTAINS_EXTRACTEDTEXT_FIELD_GUID = new Guid

("181724B3-AB4E-4E9F-82B7-6852D7DBFCBE");

public static readonly Guid HAS_IMAGES_FIELD_GUID = new Guid("DB80E2CB-CE37-

4813-8AAA-45AE54C8AB7B");

}

}

Relativity 8.0.291.1 or above - view code using the RSAPIClient class

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using kCura.EventHandler;

using Relativity.API;

using kCura.Relativity.Client;

using System.Data.SqlClient;

using Relativity.Samples.Core;

namespace Relativity.Samples.EventHandlers {

///<summary>

///This is an example of a pre save event handler automatically populates two

field values

///before persisting to the database based on user selection.

///It demonstrates how to query the Document artifact using the Services API and

obtain the

///Has Images field. It also demonstrates how to use a SQL connection when

necessary.

///</summary>

[kCura.EventHandler.CustomAttributes.Description("Sample Pre-Save Event

Handler"),

System.Runtime.InteropServices.Guid("207f6e4e-6204-4df3-8264-d0166cd33a77")]

Relativity | Event Handlers Guide - 33

public class SamplePreSaveEventHandler : kCura.EventHandler.PreSaveEventHandler {

public override kCura.EventHandler.Response Execute() {

Response retVal = new Response(); //Initialize response value and set

default values

retVal.Success = true;

retVal.Message = string.Empty;

try {

Boolean hasExtractedText;

Boolean hasImages;

kCura.EventHandler.Field hasExtractedTextField =

this.ActiveArtifact.Fields[ApplicationConstants.CONTAINS_EXTRACTEDTEXT_FIELD_GUID.ToString()];

kCura.EventHandler.Field hasImagesField =

this.ActiveArtifact.Fields[ApplicationConstants.HAS_IMAGES_FIELD_GUID.ToString()];

if (this.ActiveArtifact.Fields[ApplicationConstants.RELATED_DOCUMENT_

FIELD_GUID.ToString()].Value.Value != null) {

//Get the selected document.

Int32 docArtifactID = (Int32)this.ActiveArtifact.Fields

[ApplicationConstants.RELATED_DOCUMENT_FIELD_GUID.ToString

()].Value.Value;

//Create an instance of the IRSAPIClient using the context of the

current user logged

//into the system.

using (IRSAPIClient proxy =

this.Helper.GetServicesManager().CreateProxy<IRSAPIClient>(ExecutionIdentity.CurrentUser)) {

//Set the active workspaceID on the APIOptions object.proxy.APIOptions.WorkspaceID = this.Helper.GetActiveCaseID();proxy.APIOptions.StrictMode = true;

kCura.Relativity.Client.DTOs.Document docDTO =newkCura.Relativity.Client.DTOs.Document(docArtifactID);

//Get the document by ArtifactID.docDTO = proxy.Repositories.Document.Read(docArtifactID).Results[0].Artifact;

Relativity | Event Handlers Guide - 34

hasImages = String.Equals(docDTO.Fields.Where(field => String.Equals(field.Name, "Has Images",StringComparison.OrdinalIgnoreCase)).Single().ValueAsSingleChoice.Name, "Yes",StringComparison.OrdinalIgnoreCase);

}

//You can also write a SQL query against the database if needed using

the IDBContext interface.

String sql = "SELECT DATALENGTH([ExtractedText]) AS ExtractedTextCount

FROM [Document] WITH(NOLOCK)WHERE [ArtifactID] =@ArtifactID";

SqlParameter artifactIDParam = new SqlParameter("@ArtifactID",

System.Data.SqlDbType.Int);

artifactIDParam.Value = docArtifactID;

hasExtractedText =

this.Helper.GetDBContext(this.Helper.GetActiveCaseID()).ExecuteSqlStatementAsScalar<Int64>(sql, new SqlParameter[] { artifactIDParam }) > 0;

hasImagesField.Value.Value = hasImages;

hasExtractedTextField.Value.Value = hasExtractedText;

}

else {

hasExtractedTextField.Value.Value = null;

}

}

catch (System.Exception ex) {

retVal.Message = ex.ToString();

//Set the success to false because an exception occurred.

retVal.Success = false;

}

return retVal;

}

public override kCura.EventHandler.FieldCollection RequiredFields {

get {

//Return a list of fields necessary for this event handler.

FieldCollection retVal = new FieldCollection();

retVal.Add(new kCura.EventHandler.Field(ApplicationConstants.NAME_

FIELD_GUID));

retVal.Add(new kCura.EventHandler.Field(ApplicationConstants.RELATED_

DOCUMENT_FIELD_GUID));

Relativity | Event Handlers Guide - 35

retVal.Add(new kCura.EventHandler.Field(ApplicationConstants.CONTAINS_

EXTRACTEDTEXT_FIELD_GUID));

retVal.Add(new kCura.EventHandler.Field(ApplicationConstants.HAS_

IMAGES_FIELD_GUID));

return retVal;

}

}

private IAuthenticationMgr GetAuthenticationManager() {

return this.Helper.GetAuthenticationManager();

}

private IServicesMgr GetServicesManager() {

return this.Helper.GetServicesManager();

}

}

}

This class references the constants defined in the following sample code. These constants are based onthe GUIDs of Artifacts (such as Fields, Choices, Object Types, and so on) used in a sample application.See the Application Deployment System Guide for more information.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace Relativity.Samples.Core

{

public static class ApplicationConstants

{

public static readonly Guid OLD_CODING_FIELD_GUID = new Guid("eb174a88-068e-

40a9-8835-5f020f80a089");

public static readonly Guid NAME_FIELD_GUID = new Guid("0761E9D6-F775-48BB-

91CC-1DB39B2CE9F0");

public static readonly Guid RELATED_DOCUMENT_FIELD_GUID = new Guid("513B40E6-

1FC5-4E59-9191-1C52D66C89AD");

public static readonly Guid CONTAINS_EXTRACTEDTEXT_FIELD_GUID = new Guid

("181724B3-AB4E-4E9F-82B7-6852D7DBFCBE");

public static readonly Guid HAS_IMAGES_FIELD_GUID = new Guid("DB80E2CB-CE37-

4813-8AAA-45AE54C8AB7B");

}

}

Relativity | Event Handlers Guide - 36

8. Use the following codes samples as templates for logging in to the Services API with your event handler.For more information, see Relativity Services API or Relativity Patch 8.0.291.1 Guidelines on the Relativ-ity 8 developers site.Logging in as a system userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.System))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in with a username and passwordRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.Manual))

{

// Proxy created.

proxy.LoginWithCredentials("[email protected]", "Test1234!");

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in as the current userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

Relativity | Event Handlers Guide - 37

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.CurrentUser))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

9. Import the compiled .dll file using the Resource Files tab in Adminmode. See the Admin Guide formore information.

2.1.6 Post Save event handlersPost Save event handlers execute after the object’s information has been written to the database. Thesecannot impart any lasting changes to the object unless done so with a SQL operation. Post Save eventhandlers cannot abort the save process, as they occur after the Save or Save and Next button has alreadybeen clicked.Common uses for post save event handlers are:

n Email notificationn Adding additional data to a record based on saved information

2.1.6.1 Creating Post Save event handlersComplete the following steps to create a Post Save event handler in Relativity.

1. Create a new solution in Visual Studio.2. Add references to the following .dll files:

n kCura.Relativity.Client.dlln kCura.EventHandler.dlln Relativity.API.dll - Use themethods on IEHHelper class to return a database context, a con-

nection to the Services API, and authentication tokens. For more information, see the API helperclasses on the Relativity 8 developers site.

Note: While the DatabaseConnection property, MasterDatabaseConnection property, and theGetAuthenticationToken method on the EventHandlerBase class are still supported, we recommendusing the Relativity API helper classes for any new development.

3. Define an attribute that provides a description for the Post Save event handler.4. Inherit from PostSaveEventHandler. All classes that implement event handlers must inherit from this

class.5. Override the Execute() method, which executes when the event handler is triggered.

Relativity | Event Handlers Guide - 38

6. Override the RequiredFields property, which represents fields that are required on object creation.

Note: The ActiveArtifact.Fields collection includes the fields returned by the RequiredFields property as well asthose on the current layout. It also includes the values of these fields.

7. Review the following sample code for the SamplePostSaveEventHandler and ApplicationConstantsclasses.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Relativity.Samples.Core;

using kCura.EventHandler;

using Relativity.API;

namespace Relativity.Samples.EventHandlers {

/// <summary>

/// This event handler will automatically insert a job into the queue table in

the EDDS database after a user has successfully saved an instance of the Sample

Application object

/// </summary>

[kCura.EventHandler.CustomAttributes.Description("Sample Post Save Event

Handler")]

class SamplePostSaveEventHandler : kCura.EventHandler.PostSaveEventHandler {

public override kCura.EventHandler.Response Execute() {

Response retVal = new Response();

retVal.Success = true;

retVal.Message = String.Empty;

try {

kCura.EventHandler.Field containsExtractedTextField =

this.ActiveArtifact.Fields[ApplicationConstants.CONTAINS_EXTRACTEDTEXT_

FIELD_GUID.ToString()];

if ((containsExtractedTextField.Value.Value != null) && (Boolean)

containsExtractedTextField.Value.Value == true) {

kCura.EventHandler.Field relatedDocField = this.ActiveArtifact.Fields[ApplicationConstants.RELATED_DOCUMENT_FIELD_GUID.ToString()];

if (relatedDocField.Value.Value != null) {Int32docArtifactID = (Int32)relatedDocField.Value.Value;IDBContext masterDBContext = this.Helper.GetDBContext(-1);

Relativity | Event Handlers Guide - 39

Int32workspaceArtifactID = this.Helper.GetActiveCaseID();Int32 instanceArtifactID = this.ActiveArtifact.ArtifactID;

if (!JobExists(masterDBContext, workspaceArtifactID, instanceArtifactID)) {InsertJob(masterDBContext, workspaceArtifactID, instanceArtifactID);}}

}

}

catch (System.Exception ex) {

retVal.Success = false;

retVal.Message = ex.ToString();

}

return retVal;

}

private static void InsertJob(IDBContext dbContext, Int32

workspaceArtifactID, Int32 instanceArtifactID) {

JobQueries.InsertJob(dbContext, workspaceArtifactID, instanceArtifactID);

}

private static Boolean JobExists(IDBContext dbContext, Int32

workspaceArtifactID, Int32 instanceArtifactID) {

return JobQueries.JobExists(dbContext, workspaceArtifactID,

instanceArtifactID);

}

public override kCura.EventHandler.FieldCollection RequiredFields {

get {

FieldCollection retVal = new FieldCollection();

retVal.Add(new kCura.EventHandler.Field(ApplicationConstants.RELATED_

DOCUMENT_FIELD_GUID));

retVal.Add(new kCura.EventHandler.Field(ApplicationConstants.CONTAINS_

EXTRACTEDTEXT_FIELD_GUID));

return retVal;

}

}

}

}

This class references the constants defined in the following sample code. These constants are based onthe GUIDs of Artifacts (such as Fields, Choices, Object Types, and so on) used in a sample application.See the Application Deployment System Guide for more information.

Relativity | Event Handlers Guide - 40

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace Relativity.Samples.Core

{

public static class ApplicationConstants

{

public static readonly Guid OLD_CODING_FIELD_GUID = new Guid("eb174a88-068e-

40a9-8835-5f020f80a089");

public static readonly Guid NAME_FIELD_GUID = new Guid("0761E9D6-F775-48BB-

91CC-1DB39B2CE9F0");

public static readonly Guid RELATED_DOCUMENT_FIELD_GUID = new Guid("513B40E6-

1FC5-4E59-9191-1C52D66C89AD");

public static readonly Guid CONTAINS_EXTRACTEDTEXT_FIELD_GUID = new Guid

("181724B3-AB4E-4E9F-82B7-6852D7DBFCBE");

public static readonly Guid HAS_IMAGES_FIELD_GUID = new Guid("DB80E2CB-CE37-

4813-8AAA-45AE54C8AB7B");

}

}

8. Use the following codes samples as templates for logging in to the Services API with your event handler.For more information, see Relativity Services API or Relativity Patch 8.0.291.1 Guidelines on the Relativ-ity 8 Documentation site.Logging in as a system userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.System))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in with a username and passwordRelativity 8.0.291.1 or above - view code for the RSAPIClient

Relativity | Event Handlers Guide - 41

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.Manual))

{

// Proxy created.

proxy.LoginWithCredentials("[email protected]", "Test1234!");

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in as the current userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.CurrentUser))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

9. Import the compiled .dll file using the Resource Files tab in Adminmode. See the Admin Guide formore information.

2.1.7 Pre and Post Install event handlers overviewPre and Post Install event handlers provide you with the ability to perform custom actions during thedeployment of an application. For example, you can develop Pre Install event handlers that set default valueson specific fields during installation, or Post Install event handlers that update fields immediately after anapplication is installed in a workspace.You can run Pre and Post Install event handlers from these entry points used to install applications:

Relativity | Event Handlers Guide - 42

n Agents - Add an application to the queue in Library Application on EDDS database, and click Install.Your custom event handlers run as part of the installation process.

n Procuro - Add a custom application to the required application or library folder in your Relativity install-ation, and run Procuro, which executes your custom event handlers.

n Relativity Web UI - Install an application through the Relativity Application tab on a workspace. Yourcustom Pre and Post Install event handlers execute as part of the application installation.

n Services API - Use the InstallApplication() method on the proxy to install the application and executethe event handlers.

Note: UseWindows Authentication when logging in to Relativity from a Pre or Post Install event handler.When Procuro executes these event handlers, the account that it runs under must have valid credentialsfor Windows Authentication and Script Administrator rights to the Relativity instance. For sample code, seePre Install event handlers below.

In addition, Pre and Post Install event handlers provide the following functionality that you can customize:

n Execution type – You can use the RunOnce class to identify a Pre or Post Install event handler that isrun only when the application is initially installed in a workspace. If the installation fails, these eventhandlers are re-executed until the application is successfully installed, and then no longer run.You can add this class as an attribute to the header of an event handler class file. The RunOnce class isavailable in the kCura.EventHandler.CustomAttributes namespace. See the Event handlers class library.

n Execution order – You can specify the order used to run a group of Pre or Post Install event handlerswhen you add them to an application through the Relativity UI. This functionality provides you with theability to includemultiple Install event handlers in an application while controlling how they areexecuted. The Relativity Applications tab displays the execution type and order for each Pre and PostInstall event handler in the Install Event Handlers associative list. For more information, see the Applic-ation Deployment System Guide.

n Logging messages – Pre and Post Install event handlers also provide you with the ability to return cus-tom status messages about their execution. This information is displayed on the status page in theRelativity UI. For more information, see Logging messages for Pre and Post Install event handlers onpage 53.

2.1.7.1 Pre Install event handlersPre Install event handlers provide you with the ability to develop custom code that executes during theinstallation of an application. You can use Pre Install event handlers to create database tables or performother tasks that you must complete before fully deploying an application. You can identify the execution typeof these event handlers and the order in which you want them to run. Before executing a Pre Install eventhandler, you can also check the version of the application that you want to update or modify. For moreinformation, see Pre and Post Install event handlers overview on the previous page.

Creating Pre Install event handlers

Complete the following steps to create a Pre Install event handler in Relativity:

1. Create a new class or solution in Visual Studio.2. Add references to the following .dll files:

n kCura.Relativity.Client.dlln kCura.EventHandler.dll

Relativity | Event Handlers Guide - 43

n Relativity.API.dll - Use themethods on IEHHelper class to return a database context, a con-nection to the Services API, and authentication tokens. For more information, see the API helperclasses on the Relativity 8 developers site.

Note: While the DatabaseConnection property, MasterDatabaseConnection property, and theGetAuthenticationToken method on the EventHandlerBase class are still supported, we recommendusing the Relativity API helper classes for any new development.

3. Define an attribute that provides a description for the event handler.4. Add a GUID to the event handler. We recommend using the GUID generator in Visual Studio.5. Inherit from the PreInstallEventHandler class. All Pre Install event handlers must inherit from this class.6. Override the Execute() method, which executes when the event handler is triggered.7. Review the following sample code which includes the SamplePreInstallEventHandler class.

Relativity 8.0.275.3 or below - view code using the ArtifactManagerProxy classThis sample code illustrates how to check the version of the application and rename a Field, as well ashow to useWindows authentication to log in to Relativity using the ArtifiactManagerProxy class.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Data.SqlClient;

using Relativity.Samples.Core;

using kCura.EventHandler;

using Relativity.API;

using kCura.Relativity.Client;

namespace Relativity.Samples.EventHandlers

{

[kCura.EventHandler.CustomAttributes.RunOnce(true)]

[kCura.EventHandler.CustomAttributes.Description("Sample Pre-Install Event

Handler")]

[System.Runtime.InteropServices.Guid("81086558-cfae-4d16-aa24-4d53727a95c8")]

public class SamplePreInstallEventHandler :

kCura.EventHandler.PreInstallEventHandler

{

public override Response Execute()

{

//Instantiate a response object.

Response retVal = new Response();

retVal.Success = true;

retVal.Message = String.Empty;

try

Relativity | Event Handlers Guide - 44

{

//Validate that this is not a first-time install

//and that the currently installed version is less than 3.2.1.1.

if (this.CurrentVersion != null && this.CurrentVersion < new

System.Version("3.2.1.1"))

{

IArtifactManager proxy = newArtifactManagerProxy(this.Helper.GetServicesManager().GetServicesURL());

//Login usingWindows Authentication under the Relativity Service Account context.proxy.Login();proxy.APIOptions.WorkspaceID = this.Helper.GetActiveCaseID();

kCura.Relativity.Client.DTOs.Field codingField =newkCura.Relativity.Client.DTOs.Field(ApplicationConstants.OLD_CODING_FIELD_GUID);codingField.Fields = kCura.Relativity.Client.DTOs.FieldValue.AllFields;var results = proxy.Repositories.Field.Read(codingField);

//Verify that the field still exists.if (results.Success == true && results.Results.Count > 0){//Read the existing field.codingField = results.Results[0].Artifact;

foreach (var applicationArtifact in codingField.RelativityApplications) {

//Delete all references to applications associated with this field.Int32applicationArtifactID = applicationArtifact.ArtifactID;

RemoveApplicationReference(this.Helper.GetDBContext(this.Helper.GetActiveCaseID()),applicationArtifactID, codingField.ArtifactID);}//Delete this field.proxy.Repositories.Field.Delete(codingField);}

}

else

{

Relativity | Event Handlers Guide - 45

retVal.Message= "Application was not installed previously or >= 3.2.1.1";}

}

catch (System.Exception ex)

{

retVal.Success = false;

retVal.Message = "Pre-Install field rename failed with message: " +

ex.Message;

}

return retVal;

}

private static void RemoveApplicationReference(IDBContext

workspaceDBContext,

Int32applicationArtifactID, Int32 fieldArtifactID) {

String sql = "DECLARE@fieldGuid UNIQUEIDENTIFIER = (SELECT TOP 1 [ArtifactGuid] FROM[ArtifactGuid]WHEREArtifactID =@fieldArtifactID);"+ " DELETE FROM [ApplicationGuid]WHERE [ApplicationID] =@ApplicationID AND [ArtifactGuid] =@fieldGuid; "+ " DELETE FROM [ApplicationField]WHERE [ApplicationArtifactID] =@ApplicationID AND[FieldArtifactID] =@fieldArtifactID";

SqlParameter fieldArtifactIDParam = new SqlParameter("@fieldArtifactID",System.Data.SqlDbType.Int);fieldArtifactIDParam.Value = fieldArtifactID;

SqlParameter applicationIDParam = newSqlParameter("@ApplicationID",System.Data.SqlDbType.Int);applicationIDParam.Value = applicationArtifactID;

workspaceDBContext.ExecuteNonQuerySQLStatement(sql, new SqlParameter[] { fieldArtifactIDParam,applicationIDParam });

}

}

}

Relativity 8.0.291.1 or above - view code using the RSAPIClient classThis sample code illustrates how to check the version of the application and rename a Field, as well ashow to useWindows authentication to log in to Relativity using the RSAPIClient class.

Relativity | Event Handlers Guide - 46

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Data.SqlClient;

using Relativity.Samples.Core;

using kCura.EventHandler;

using Relativity.API;

using kCura.Relativity.Client;

namespace Relativity.Samples.EventHandlers {

[kCura.EventHandler.CustomAttributes.RunOnce(true)]

[kCura.EventHandler.CustomAttributes.Description("Sample Pre-Install Event

Handler")]

[System.Runtime.InteropServices.Guid("81086558-cfae-4d16-aa24-4d53727a95c8")]

public class SamplePreInstallEventHandler :

kCura.EventHandler.PreInstallEventHandler {

public override Response Execute() {

Response retVal = new Response(); //Instantiate a response object

retVal.Success = true;

retVal.Message = String.Empty;

try {

//Validate that this is not a first time install and that the currently

installed version

//is less than 3.2.1.1.

if (this.CurrentVersion != null && this.CurrentVersion < new

System.Version("3.2.1.1"))

{

//Create an instance of the IRSAPIClient using the service context.using (IRSAPIClient proxy =this.Helper.GetServicesManager().CreateProxy<IRSAPIClient>(ExecutionIdentity.System)) { 

proxy.APIOptions.WorkspaceID = this.Helper.GetActiveCaseID();

kCura.Relativity.Client.DTOs.Field codingField =newkCura.Relativity.Client.DTOs.Field(ApplicationConstants.OLD_CODING_FIELD_GUID);codingField.Fields =kCura.Relativity.Client.DTOs.FieldValue.AllFields;

Relativity | Event Handlers Guide - 47

var results = proxy.Repositories.Field.Read(codingField);

//Verify that the field still exists.if (results.Success == true && results.Results.Count > 0){//Read the existing field.codingField = results.Results[0].Artifact;foreach (var applicationArtifact in codingField.RelativityApplications) {

//Delete all references to applications that this field is associated with.Int32applicationArtifactID = applicationArtifact.ArtifactID;RemoveApplicationReference(this.Helper.GetDBContext(this.Helper.GetActiveCaseID()),applicationArtifactID, codingField.ArtifactID);}

//Delete this field.proxy.Repositories.Field.Delete(codingField);}}

}

else {

retVal.Message= "Application was not installed previously or >= 3.2.1.1";}

}

catch (System.Exception ex) {

retVal.Success = false;

retVal.Message = "Pre-Install field rename failed with message: " +

ex.Message;

}

return retVal;

}

private static void RemoveApplicationReference(IDBContext workspaceDBContext,

Int32 applicationArtifactID, Int32 fieldArtifactID) {

String sql = "DECLARE @fieldGuid UNIQUEIDENTIFIER = (SELECT TOP 1

[ArtifactGuid]

FROM [ArtifactGuid]

WHERE ArtifactID = @fieldArtifactID);"

+ " DELETE FROM [ApplicationGuid] WHERE [ApplicationID] =

@ApplicationID AND [ArtifactGuid] = @fieldGuid; "

Relativity | Event Handlers Guide - 48

+ " DELETE FROM [ApplicationField] WHERE [ApplicationArtifactID] =

@ApplicationID AND [FieldArtifactID] = @fieldArtifactID";

SqlParameter fieldArtifactIDParam = new SqlParameter("@fieldArtifactID",

System.Data.SqlDbType.Int);

fieldArtifactIDParam.Value = fieldArtifactID;

SqlParameter applicationIDParam = new SqlParameter("@ApplicationID",

System.Data.SqlDbType.Int);

applicationIDParam.Value = applicationArtifactID;

workspaceDBContext.ExecuteNonQuerySQLStatement(sql, new SqlParameter[] {

fieldArtifactIDParam,

applicationIDParam });

}

}

}

8. Use the following codes samples as templates for logging in to the Services API with your event handler.For more information, see Relativity Services API or Relativity Patch 8.0.291.1 Guidelines on the Relativ-ity 8 developers site.Logging in as a system userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.System))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in with a username and passwordRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

Relativity | Event Handlers Guide - 49

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.Manual))

{

// Proxy created.

proxy.LoginWithCredentials("[email protected]", "Test1234!");

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in as the current userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.CurrentUser))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

9. Import the compiled .dll file using the Resource Files tab in Admin mode. See the Admin Guide for moreinformation.

2.1.7.2 Post Install event handlersYou can develop Post Install event handlers that execute immediately after the user installs an application.You can identify the execution type of these event handlers and the order in which you want them to run. Thefollowing examples illustrate how you can use Post Install event handlers:

n Set default values on fields immediately after an application is installed. You could use a Post Installevent handler to set a Choice field on an RDO.

Relativity | Event Handlers Guide - 50

Note: You can use Post Install event handlers that run only once to avoid resetting the default values on objecttype instances during the upgrade of an application. The use of these event handlers prevents any updates thatyou make to these instances from being overwritten. See the Application Deployment System Guide for moreinformation.

n Run a Post Install event handler to create new instances of RDOs in a newly installed application.For more information, see the Pre and Post Install event handlers overview on page 41.

Creating Post Install event handlers

Complete the following steps to create a Pre Install event handler in Relativity:

1. Create a new class or solution in Visual Studio.2. Add references to the following .dll files:

n kCura.Relativity.Client.dlln kCura.EventHandler.dlln Relativity.API.dll - Use themethods on IEHHelper class to return a database context, a con-

nection to the Services API, and authentication tokens. For more information, see the API helperclasses on the Relativity 8 developers site.

Note: While the DatabaseConnection property, MasterDatabaseConnection property, and theGetAuthenticationToken method on the EventHandlerBase class are still supported, we recommendusing the Relativity API helper classes for any new development.

3. Define an attribute that provides a description for the event handler.4. Add a GUID to the event handler. We recommend using the GUID generator in Visual Studio.5. Inherit from the PostInstallEventHandler class. All Post Install event handlers must inherit from this

class.6. Override the Execute() method, which executes when the event handler is triggered.7. Review the following sample code for the SampleRunOncePostInstallEventHandler class. It illustrates

how to set the RunOnce attribute, which allows the event handler to execute only a single time. Whenthe event handler executes, it creates a queue table in the EDDS database.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using kCura.EventHandler;

using Relativity.API;

namespace Relativity.Samples.EventHandlers {

/// <summary>

/// This event handler runs once when the application is installed

/// and creates a queue table in the EDDS database.

/// </summary>

[kCura.EventHandler.CustomAttributes.RunOnce(true)]

Relativity | Event Handlers Guide - 51

[kCura.EventHandler.CustomAttributes.Description("Sample Run-Once Post Install

Event Handler")]

[System.Runtime.InteropServices.Guid("c9df616d-d51b-48ae-b9f4-3ce4135c59c5")]

public class SampleRunOncePostInstallEventHandler :

kCura.EventHandler.PostInstallEventHandler {

public override Response Execute() {

Response retVal = new Response();

retVal.Message = String.Empty;

retVal.Success = true;

try {

// Get the master database context.

IDBContext masterDBContext = this.Helper.GetDBContext(-1);

// Run the SQL statement to create the table.

Relativity.Samples.Core.JobQueries.CreateTableIfNotExists

(masterDBContext);

}

catch (System.Exception ex) {

// Catch the exception if it occurs. Fail the installation

// if the queue table does not get created successfully.

retVal.Success = false;

retVal.Message = ex.Message;

}

return retVal;

}

}

}

8. Use the following codes samples as templates for logging in to the Services API with your event handler.For more information, see Relativity Services API or Relativity Patch 8.0.291.1 Guidelines on the Relativ-ity 8 Documentation site.Logging in as a system userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.System))

{

Relativity | Event Handlers Guide - 52

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in with a username and passwordRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.Manual))

{

// Proxy created.

proxy.LoginWithCredentials("[email protected]", "Test1234!");

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

}

Logging in as the current userRelativity 8.0.291.1 or above - view code for the RSAPIClient

try

{

using (IRSAPIClient proxy =

Helper.GetServicesManager().CreateProxy<IRSAPIClient>

(ExecutionIdentity.CurrentUser))

{

// Proxy created.

// Add your custom code for working with the RSAPIClient.

}

}

catch (Exception e)

{

// Proxy failure: e.Message

Relativity | Event Handlers Guide - 53

}

9. Import the compiled .dll file using the Resource Files tab in Adminmode. See the Admin Guide formore information.

2.1.7.3 Logging messages for Pre and Post Install event handlersAs a best practice, you should log messages about the execution of Pre and Post Install event handlers thatyou have added to an application. Thesemessages are listed on the status page displayed immediately afteran application is installed or through the Application Library tab as shown in the following illustration.

You can log this custom status information by setting theMessage property on the Response object returnedby a Pre or Post Install event handler. In addition, you can provide troubleshooting information about aknown exception by customizing the information contained in theMessage property. The following codesamples exemplify how to log messages for Pre and Post Install event handlers.

Relativity 8.0.275.3 or below - view code using the ArtifactManagerProxy class

using System;using System.Collections.Generic;using System.Linq;using System.Text;using Relativity.API;using kCura.EventHandler;using kCura.Relativity.Client;

namespace BestPractice

Relativity | Event Handlers Guide - 54

{[kCura.EventHandler.CustomAttributes.Description("Adds a default User PowerObject")][kCura.EventHandler.CustomAttributes.RunOnce(true)][System.Runtime.InteropServices.Guid("6afd07dc-c0c1-4afc-bcd5-433260a30c77")]

class BestPracticePreLoad : PreInstallEventHandler{

/// <summary>/// This code sample illustrates best practices for logging a status messagefrom an Pre/// or Post Install event handler./// </summary>/// <returns>Response Object with information about the execution of theevent handler. The contents/// of the Message property is displayed in the application.</returns>/// <remarks></remarks>public override Response Execute(){

//Create a StringBuilder object to track the progress of failedexecutions.var debugMessageBuilder = new StringBuilder();var returnValue = new Response();returnValue.Success = true;

try{debugMessageBuilder.AppendLine("Started Event Handler Logic. ");IArtifactManager artifactManager;artifactManager = new ArtifactManagerProxy(this.Helper.GetServicesManager().GetServicesURL());artifactManager.TokenLogin(this.Helper.GetAuthenticationManager().GetAuthenticationToken());debugMessageBuilder.AppendLine("Built Artifact Manager and Loggedin. ");

//YOUR CUSTOM CODE

debugMessageBuilder.AppendLine("Step information on custom code.");

//YOUR CUSTOM CODE

debugMessageBuilder.AppendLine("More step information. ");

//Clean up//Set a successful return message to display in the application.

Relativity | Event Handlers Guide - 55

returnValue.Message = "Best Practice Pre Load Event Handler ransuccessfully";

}catch(CustomException ex) //If you can identify an exception, providea message that//explains how to resolve the issue{

returnValue.Success = false;returnValue.Exception = ex;

//Display information about resolving the issue to the user.

returnValue.Message = "The environment needs to be fixed in thefollowing ways: ....";

}catch(Exception ex) //Unknown exception{

returnValue.Success = false;returnValue.Exception = ex;returnValue.Message = debugMessageBuilder.ToString();

}return returnValue;

}}

}

Relativity 8.0.291.1 or above - view code using the RSAPIClient class

using System;using System.Collections.Generic;using System.Linq;using System.Text;using Relativity.API;using kCura.EventHandler;using kCura.Relativity.Client;

namespace Relativity.Samples.EventHandlers {[kCura.EventHandler.CustomAttributes.Description("Adds a default User PowerObject")][kCura.EventHandler.CustomAttributes.RunOnce(true)][System.Runtime.InteropServices.Guid("6afd07dc-c0c1-4afc-bcd5-433260a30c77")]

class BestPracticePreInstall : PreInstallEventHandler {/// <summary>/// This code sample illustrates best practices for logging a status messagefrom an Pre

Relativity | Event Handlers Guide - 56

/// or Post Install event handler./// </summary>/// <returns>Response Object with information about the execution of the eventhandler. The contents/// of the Message property is displayed in the application.</returns>/// <remarks></remarks>

public override Response Execute() {

//Create a StringBuilder object to track the progress of failedexecutions.var debugMessageBuilder = new StringBuilder();var returnValue = new Response();returnValue.Success = true;

try {debugMessageBuilder.AppendLine("Started Event Handler Logic. ");

//Create an instance of the IRSAPIClient using the context of thecurrent user logged into the system.using (IRSAPIClient proxy =

this.Helper.GetServicesManager().CreateProxy<IRSAPIClient>(ExecutionIdentity.CurrentUser)) {

debugMessageBuilder.AppendLine("Built Artifact Manager and Loggedin. ");

//YOUR CUSTOM CODE.debugMessageBuilder.AppendLine("Step information on custom code.");

//YOUR CUSTOM CODE.debugMessageBuilder.AppendLine("More step information. ");

//Clean up.//Set a successful return message to display in the application.returnValue.Message = "Best Practice Pre Load Event Handler ransuccessfully";

}}//If you can identify an exception, provide a message that explains howto resolve the issue.catch (CustomException ex){

returnValue.Success = false;

Relativity | Event Handlers Guide - 57

returnValue.Exception = ex;

//Display information about resolving the issue to the user.returnValue.Message = "The environment needs to be fixed in thefollowing ways: ....";

}catch (Exception ex) //Unknown exception{

returnValue.Success = false;returnValue.Exception = ex;returnValue.Message = debugMessageBuilder.ToString();

}return returnValue;

}} //End BestPracticePreInstall.

public class CustomException : System.Exception {public CustomException()

: base("This is a custom Exception") {}

}}

2.2 Adding assembly source informationYou can add source information to assemblies to improve troubleshooting. To include company name andcopyright metadata, you need to add System.Reflection.AssemblyCompanyAttribute andSystem.Reflection.AssemblyCopyrightAttribute to the AssemblyInfo.vb/AssemblyInfo.cs file generated withyour Visual Studio project.The following sample code shows how to add the assembly company name and copyright values.

[assembly: AssemblyTitle("TestAgent")][assembly: AssemblyDescription("")][assembly: AssemblyConfiguration("")][assembly: AssemblyCompany("ACME Inc.")][assembly: AssemblyProduct("TestAgent")][assembly: AssemblyCopyright("Copyright © 2012")][assembly: AssemblyTrademark("")][assembly: AssemblyCulture("")]

The Relativity web interface displays fields containing the source information that you specified for yourassemblies.

Relativity | Event Handlers Guide - 58

Note: You can't specify a value for the Application field in your source code. Relativity sets the value of thisfield when you add an assembly through the Resource Files tab. See Resource files in the Relativity 8documentation site.

3 Guidelines for event handlersConsider the following if you're preparing to use event handlers in your Relativity workspace:

n Only the Pre Delete and Pre Cascade Delete event handlers work with Mass Operations.n An event handler will throw an error if it's attached to a multi-choice field with two choices of the same

name and both choices are selected at the time of the save.n An event handler assembly may contain one or more classes, and those classes may contain one or

more rules. See Using event handler rules below.

3.1 Using event handler rulesAn individual Pre Save or Post Save class may contain several rules, and an assembly may contain manyclasses.The following scenarios show how assemblies can use event handlers to implement business logic in aworkspace.

3.1.1 Scenario 1In this scenario, an event handler assembly includes two classes.A Pre Save class contains three rules:

n Rule 1 - If the save occurred from any layout other than MyPreDeterminedLayout, then don't processany more rules.

n Rule 2 - If the Responsiveness field is coded Responsive, then the Privilege field becomes required.n Rule 3 - If the Redact field is coded Yes, then the Redaction Reason field becomes required.

A Post Save class contains an additional rule:

n Rule 4 - If a document is coded Responsive, update the Reviewed field to Yes.

Relativity | Event Handlers Guide - 59

In this example, Rule 4 (in the Post Save class) won't work because the document has already been saved.Instead, we should move Rule 4 to the Pre Save class or move it to a separate event handler, as shown inScenario 2.

3.1.2 Scenario 2In this scenario, there are two event handler assemblies.The first event handler assembly includes a Pre Save class with two rules:

n Rule 1 - If the Responsiveness field is coded Responsive, then the Privilege field becomes required.n Rule 2 - If the Redact field is coded Yes, then the Redaction Reason field becomes required.

The second event handler assembly includes another Pre save class with a third rule:

n Rule 3 - If a document is coded Responsive, update the Reviewed field to Yes.Each class can be added or removed from a workspace object independently of the other classes in itsassembly. All rules within each class will be executed together.So, an advantage to creating event handlers as in Scenario 2 is that Rule 3 (contained in the second eventhandler assembly) can be applied to the workspace separately from all other rules.

3.2 Working with Choice objectsYou can programatically work with the choices that users select through the Relativity UI. The followinginformation provides a brief overview of the classes used for manipulating choices available in thekCura.EventHandler namespace:

n The values that the user assigns to choice fields are stored in instances of the ChoiceFieldValue class,which is a subclass of FieldValue.

n The ChoiceCollection contains only Choice objects representing choices selected by a user in Relativity.n The IsSelected property on each Choice object is always set to true because only selected Choice

objects are returned.n The Choices property on the ChoiceFieldValue class returns a ChoiceCollection instance. Use the Count

property of the ChoiceCollection to determine howmany Choices are set on a choice field. If no Choicesare set, the Count property returns 0.

3.2.1 Sample Code for choice fieldsThese code samples illustrate common uses of choice fields:

n Cast the value of a Choice to the type ChoiceFieldValue:

Guid fieldGuid = new Guid("6bc19a2b-b7d1-4b8f-bf14-9eaed515d2b8");

ChoiceFieldValue choiceFieldValue = (ChoiceFieldValue)ActiveArtifact.Fields

[fieldGuid.ToString()].Value;

n To clear the choice field, remove all the Choices from ChoiceCollection:

//Clear the ChoiceCollection.

Relativity | Event Handlers Guide - 60

Guid fieldGuid = new Guid("6bc19a2b-b7d1-4b8f-bf14-9eaed515d2b8");

ChoiceFieldValue choiceFieldValue = (ChoiceFieldValue)ActiveArtifact.Fields

[fieldGuid.ToString()].Value;

ChoiceCollection myListOfDesiredChoices = new ChoiceCollection();

//The ChoiceCollection is now cleared.

choiceFieldValue.Choices = myListOfDesiredChoices;

n To clear a specific choice from a choice field, remove the Choice instance from a ChoiceCollection:

//Clear a specific Choice from a ChoiceCollection using the ArtifactID.

Guid fieldGuid = new Guid("6bc19a2b-b7d1-4b8f-bf14-9eaed515d2b8");

const Int32 choiceArtifactID = 1036982;

ChoiceFieldValue choiceFieldValue = (ChoiceFieldValue)ActiveArtifact.Fields

[fieldGuid.ToString()].Value;

ChoiceCollection myListOfDesiredChoices = new ChoiceCollection();

//Since you can't directly remove a Choice, create a new collection that

//contains the desired Choice instances but excludes the Choice

//that you no longer need.

foreach (Choice c in choiceFieldValue.Choices)

{

if (c.ArtifactID != choiceArtifactID)

{

myListOfDesiredChoices.Add(c);

}

}

//Set the Choices property to the modified collection.

choiceFieldValue.Choices = myListOfDesiredChoices;

n To add a selected choice to a choice field, add the Choice to the ChoiceCollection using its ArtifactID:

//Add a specific Choice to the ChoiceCollection using its Artifact ID.

Guid fieldGuid = new Guid("6bc19a2b-b7d1-4b8f-bf14-9eaed515d2b8");

const Int32 choiceArtifactID = 1036982;

ChoiceFieldValue choiceFieldValue = (ChoiceFieldValue)ActiveArtifact.Fields

[fieldGuid.ToString()].Value;

ChoiceCollection myListOfDesiredChoices = choiceFieldValue.Choices;

//The ArtifactID parameter is used to specify which choice will be set, not the name

parameter.

//You don't need to set the name parameter to the actual name of the choice, but it

does need to be unique to the collection.

Relativity | Event Handlers Guide - 61

myListOfDesiredChoices.Add(new Choice(choiceArtifactID, choiceArtifactID.ToString

()));

//Set the Choices property to the modified collection.

choiceFieldValue.Choices = myListOfDesiredChoices;

3.3 Remotely debugging event handlersYou can use these instructions to debug event handlers on a Relativity web server from your workstation.

1. Configure remote debugging on the Relativity web server. See How to: Set Up Remote Debugging onMSDN.

2. Make sure that you are an Administrator on the web server and your local workstation. If necessary,update yourWindows credentials to an Administrator account type.

3. On the web server, right-click on the Remote Debugging Monitor (msvmon) icon and select Run asadministrator. (Themsvmon icon was added to your desktop when you completed the remote debug-ging setup in Step 1.)

The Remote Debugging Monitor displays a message containing theWindows credentials that you usedto log in to the remote server and the name of the server, using the format:<username>@<servername>. In step 9, enter this information in theQualifier box.

4. In Relativity, upload a copy of your event handler .dll through the Resource Files tab. See the AdminGuide for more information.

5. On the Relativity server, navigate to the following directory:

C:\Windows\Temp\RelativityTemporaryDLLs\TempDomains\

6. To display the newest folder in the TempDomains directory, sort the folders by Datemodified. Youshould see the folder that was created when the assembly was uploaded.

Note: If the folder with the correct date and time doesn't exist, navigate to the object in Relativity that fires thisevent handler. The folder should now be created.

7. Locate the Symbol files (.pdb) in the bin or other folder in your project, and copy them to the folderthat you located in Step 6.

Relativity | Event Handlers Guide - 62

8. On your workstation, open your agent project in Visual Studio.9. Attach the debugger to thew3wp.exe processes:

a. Click Tools and then click Attach to Process.b. Enter <user>@<servername> in theQualifier box. Use theWindows credentials and server name

displayed by the Remote Debugging Monitor in step 3. For example, the qualifier for the userjsmith on the kCura domain logging in to the Chicago server is kcura\jsmith@chicago.

c. Select thew3wp.exe processes in the Available Processes box.

Relativity | Event Handlers Guide - 63

d. Click Attach.

10. On the Relativity server, check the status of the Remote Debugging Monitor in Visual Studio. It shouldindicate that you are connected.

4 Upgrading event handlersThe Relativity version deployed in your environment determines the upgrade steps that you need tocomplete. As part of the upgrade process, you may need to update the version of the .NET framework andevent handler .dll referenced by your projects. You may also need to update your source code if you arecurrently running a version of Relativity 6.x or earlier.

Relativity | Event Handlers Guide - 64

4.1 Upgrading to Relativity 8 from 7.x or earlierIn Relativity 8, event handlers have been upgraded to use the .NET 4.0 framework. When you build your eventhandlers, you will need to update your solution to use .NET 4.0 and to reference event handler .dll forRelativity 8.

Note: You can still continue to load older .dll versions into your Visual Studio projects as necessary.

Use the following steps to update your project:

1. Download the SDK Installer and install the Relativity SDK. See Development environment guidelines onthe Relativity 8 developers site for the SDK Installer.

2. Open your project in Visual Studio.3. Confirm that the Target framework is .NET Framework 4. (In the Solution Explorer, expand your pro-

ject and right-click Properties. Click Open to display the Application tab in the left pane. Select .NETFramework 4 in the Target framework box.)

Note: Do not select .NET Framework 4 Client Profile in the Target framework box.

4. Add a reference to the kCura.eventhandler.dll for Relativity 8. (In the Solution Explorer, right-click Refer-ences, and then click Add References to display the Add Reference dialog box. Click the Browse tab,and select the kCura.eventhandler.dll.)You can find this .dll in a Relativity web server installation, or in the following folder of the SDKinstallation directory. The folder is installed at this location by default:

...\Program Files\kCura Corporation\Relativity SDK\Event Handlers\Client

Relativity | Event Handlers Guide - 65

4.2 Upgrading syncs and Relativity 6.x or earlier installationsDepending on the version of Relativity that you are currently using, you may need to complete addition stepsto convert syncs and upgrade your .NET framework. For additional information, see the following pages onthe Relativity 7.5 developers site:

n Using syncs and event handlers in Relativity 7.0n Upgrading event handlers from Relativity v5.9 or earlier

4.3 Syntax conversion for event handlersYou can use the syntax changes listed in the following table to upgrade event handlers from Relativity v5.9 orearlier.

Note: For any new development, we recommend using the Helper classes available through the RelativityAPI helper classes instead of calling methods on the base classes. For more information, see the API helperclasses on the Relativity 8 developers site.

Old Syntax New SyntaxConvert 1.1 NullableTypes to 3.5 System.Nullable

Relativity | Event Handlers Guide - 66

Old Syntax New SyntaxNullableTypes.NullableInt32 Nullable(Of Int32)NullableTypes.NullableDateTime Nullable(Of DateTime)NullableTypes.NullableBoolean Nullable(Of Boolean)

NullableTypes.NullableDouble Nullable(Of Double)NullableTypes.NullableInteger Nullable(Of Integer)NullableTypes.NullableDecimal Nullable(Of Decimal)If nullableVar.IsNull Then If Not nullableVar.HasValue ThenDim x As NewNullableTypes.NullableInt32(1234) Dim x As System.Nullable(Of Int32) = 1234

Upgrade Sync to 3.5 Event HandlerImplements kCura.Sync.Interface.iSyncExtendedV2 Inherits kCura.EventHandler.PreSaveEventHandlerPublic ReadOnly Property Description() As StringImplements kCur-a.Sync.Interface.iSyncExtendedV2.Description

Public Overrides ReadOnly Property Description() AsString

Public Function Execute(ByVal fieldCollection AskCura.Sync.FieldsCollection) As kCur-a.Sync.Response Implements kCur-a.Sync.Interface.iSyncExtendedV2.Execute

Public Overloads Overrides Function Execute() As kCur-a.EventHandler.Response

Public ReadOnly Property RequiredFieldArtifactIDs()As Integer() Implements kCur-a.Syn-c.Interface.iSyncExtendedV2.RequiredFieldArtifactIDs

Public Overrides ReadOnly Property RequiredFields()As kCura.EventHandler.FieldCollection

kCura.Sync.FieldsCollection kCura.EventHandler.FieldCollectionkCura.Sync.Response kCura.EventHandler.Response

kCura.Sync.FieldCollection.LayoutArtifactID Me.ActiveLayout.ArtifactID

kCura.Sync.FieldCollection.FieldByArtifactID(fieldAr-tifactID)

Me.ActiveArtifact.Fields.Item(fieldArtifactID)

kCura.Sync.FieldCollection.FieldByName(“fieldName”)

Me.ActiveArtifact.Fields.Item(“fieldName”)

kCur-a.Syn-c.Utility.MulticodeHelper.IsCodeValueSelectedByCodeArtifactID(fieldCollection.FieldByArtifactID(fieldArtifactID),choiceArtifactID)

CType(Me.ActiveArtifact.Fields.Item(fieldAr-tifactID).Value,kCur-a.EventHandler.ChoiceFieldValue).IsChoiceSelected(choiceArtifactID

kCura.Sync.FieldCollection.User Me.ActiveUserkCura.Sync.Utility.DatabaseHelper(con-nectionString, databaseName)

Me.Helper.GetConnection(Me.Help-er.GetActiveCaseID)

kCura.Sync.Utility.DatabaseHelper.Read(sql) Me.Helper.GetConnection(Me.Help-er.GetActiveCaseID).ExecuteAsScalar(sql)Me.Help-er.GetConnection(Me.Helper.GetActiveCaseID).ExecuteAsDataSet(sql)

kCura.Sync.Utility.DatabaseHelper.Update(sql) Me.Helper.GetConnection(Me.Help-

Relativity | Event Handlers Guide - 67

Old Syntax New Syntaxer.GetActiveCaseID).ExecuteNonQuery(sql)

kCura.Sync.Fields.Code kCura.EventHandler.ChoicekCura.Sync.SyncField kCura.EventHandler.FieldCType(fieldCollection.FieldByArtifactID(fieldAr-tifactID).Value, kCura.Sync.Fields.Code())

CType(Me.ActiveArtifact.Fields.Item(fieldAr-tifactID).Value.Value, kCur-a.EventHandler.ChoiceCollection)

kCura.Sync.Fields.Code(choiceArtifactID).Selected kCura.EventHandler.Choice(choiceAr-tifactID).IsSelected

kCura.Sync.FieldCollection.FieldByArtifactID(fieldAr-tifactID).IsNull

CType(Me.ActiveArtifact.Fields.Item(fieldArtifactID),kCura.EventHandler.FieldValue).IsNull

kCura.Sync.FieldCollection.FieldByArtifactID(choiceFieldArtifactID).IsNull

CType(Me.ActiveArtifact.Fields.Item(choiceFieldAr-tifactID), kCur-a.EventHandler.ChoiceFieldValue).Choices.Count = 0

Relativity | Event Handlers Guide - 68

Proprietary RightsThis documentation (“Documentation”) and the software to which it relates (“Software”) belongs to kCuraCorporation and/or kCura’s third party software vendors. kCura grants written license agreements whichcontain restrictions. All parties accessing the Documentation or Softwaremust: respect proprietary rights ofkCura and third parties; comply with your organization’s license agreement, including but not limited tolicense restrictions on use, copying, modifications, reverse engineering, and derivative products; and refrainfrom any misuse or misappropriation of this Documentation or Software in whole or in part. The Software andDocumentation is protected by the Copyright Act of 1976, as amended, and the Software code is protectedby the Illinois Trade Secrets Act. Violations can involve substantial civil liabilities, exemplary damages, andcriminal penalties, including fines and possible imprisonment.©2013. kCura Corporation. All rights reserved. Relativity® and kCura® are registered trademarks of kCuraCorporation.