Developing an SharePoint 2013 AutoHosted App with Admin On-Behalf BCS Subscription

Introduction to BCS events

Until SharePoint 2010, Business Connectivity Services (BCS) was all about “pull,” that is, users needed to “refresh” the data from a SharePoint site in order to fetch the latest data records from the external system. With SharePoint 2013, BCS goes one step further by enabling the external system to send proactive “push” notifications into SharePoint whenever a data record changes at its end. For an overview of enabling events from external systems through BCS, see External events and alerts in SharePoint 2013.

Admin on-behalf subscription scenario

Consider a scenario in which a particular organization unit requires that every time there is a change in the vacation calendar, every employee has to be notified automatically. In such cases, an admin user can subscribe through BCS events once on behalf of the entire group of users in one go, and subsequent change event notifications will be delivered to each individual user.

If your app for SharePoint scenario similarly requires that the process of creating a new subscription for an event from the SharePoint side be done by an admin user on behalf of all other app users (instead of each user individually creating subscription for themselves), you can add event receivers on external content types by passing on-behalf subscription information. I have detailed how to develop this kind of app below.

Let’s get started…

Step 1. Create a new project in Visual Studio 2012, and select the App for SharePoint 2013 template. Give a name to the project, and click OK.
Step2: Specify the app details in the New app for SharePoint wizard.
  1. Give a name to the app, like EntityOnBehalfApp.
  2. Specify the SharePoint URL—your SharePoint online developer site URL.
  3. Choose the kind of app you want to build—choose the Autohosted app for this walkthrough.
  4. You can choose to validate the site entered before proceeding further.

An autohosted app allows you to add a remote web project to your app for SharePoint. When you deploy the app on a SharePoint online tenant, this remote web gets deployed to the associated Windows Azure tenant and will be available from within the app. For more information about autohosted apps, see How to: Create a basic autohosted app in SharePoint 2013.

Step 3: Add the external data source to the app for SharePoint.
  1. Right-click the solution, and then select Add, Content Types for an External Data Source.
  2. Provide the OData service URL for connecting to the external data source, and create a new external content type. Note that for this example, you do not need an “external list,” and so you can clear the check box for creating an external list for each external content type.
  3. Depending upon the design of your OData service, you might need to select all the relevant entities to be added to the app. One of the entities you select should be able to create a new subscription record at the external data source.
Step 4. Add the EventSubscriber method tothe external content type.
  1. Select the external content type that has the ability to create a new subscription record.
  2. Right-click the above external content type, and select Open with, XML (text) Editor.
  3. In the XML file for the external content type, you will need to add new methods for EventSubscriber and EventUnsubscriber. For complete definitions of these methods in the BDC model, see External events and alerts in SharePoint 2013.

For handling admin on-behalf subscriptions, you should add one extra parameter, in addition to the parameters defined in the above link, in the EventSubscriber method.

Property name: IsOnBehalfOfField

Type: System.Boolean

Possible values: true, false

BDC model scope: Parameter

Example of the Subscribe method:

 <Parameter 
    Direction="In" 
    Name="@User">
    <TypeDescriptor 
        TypeName="System.String" 
        Name="User" >
         <Properties>
            <Property 
                Name="IsOnBehalfOfField" 
                Type="System.Boolean">
                    true
            </Property>                      
        </Properties>
    </TypeDescriptor>
</Parameter>

Step 5. Add the API call for EntityExtension.Subscribe() .

  1. Right-click the solution, and select Add New Item, Module.
  2. Name the module Pages.
  3. Right-click the Pages module, and select Add New Item, Page.

This page can contain all the SharePoint client object model API calls through JavaScript. For this example, we can add an EntityExtension.Subscribe() call, which can be a button for the admin user to click.

For adding an EventReceiver to an external content type, the EntityExtension.Subscribe() API can be used in the JavaScript page.

Method Name: EntityExtension.Subscribe

Method signature:

 public Subscription Subscribe(
    EntityEventType eventType,
    NotificationCallback notificationCallback,
    string onBehalfOfUser,
    string subscriberName,
    ILobSystemInstance lobSystemInstance
)

The entire description of all parameters is available at EntityExtension.Subscribe method.

The parameters of interest for on-behalf subscriptions are:

onBehalfOfUser

Type: System.String

Purpose: This string value represents a single user’s name or a user group name in a format that is recognizable by the external system.

subscriberName

Type: System.String

Purpose: The BDC Model MethodInstance name of the Subscribe method with the OnBehalfField parameter that should be invoked.

The following is a JavaScript code example for invoking EntityExtension.Subscribe from an app for SharePoint 2013 page:

 var myweb = null;

function Subscribe() {
    context = new SP.ClientContext();
    myweb = context.get_web();
    context.load(myweb);
    entity = myweb.getAppBdcCatalog().getEntity(
        "Customer",
        "TestLOBService"
    );
    context.load(entity);

    lobSystemInstances = entity.getLobSystem().getLobSystemInstances();
    context.load(lobSystemInstances);
    context.executeQueryAsync(GetLobSubscribesystemInstance, failmethod);
}

// Initialize the LobSystemInstance.
function GetLobSubscribesystemInstance() {
    var $$enum_1_0 = lobSystemInstances.getEnumerator();
    while ($$enum_1_0.moveNext()) {
        var instance = $$enum_1_0.get_current();
        lobSystemInstance = instance;
        context.load(lobSystemInstance);
        break;
    }
    context.executeQueryAsync(SubscribeEntity, failmethod);
}

// Invoke the EntityExtension.Subscribe API.
function SubscribeEntity() {

    var RERAddress =
        "https://MyRemoteEventReceiver.cloudapp.net/" +
        "RemoteEventReceiver1.svc";

    // In case of an autohosted app for SharePoint 2013, 
    // RERAddress URL will be 
    // "~remoteAppUrl/RemoteEventReceiver1.svc"

    // Creating a new notificationCallback object by 
    // specifying current site context and NotificationEndPoint 
    // as the RemoteEventReceiver URL (RERAddress).
    var notificationCallback =
        new SP.BusinessData.Runtime.NotificationCallback(
        context,
        RERAddress
    );

    var url = myweb.get_url();

    // Setting NotificationContext as the current web URL.
    notificationCallback.set_notificationContext(url);
    context.load(notificationCallback);

    // Invoking EntityExtension.Subscribe by specifying “1”
    // for ItemAdded eventType, “testuser1@contoso.com as the user 
    // on whose behalf subscription is being made 
    // and “OnBehalfSubscribeCustomer” as the BDC Model’s 
    // MethodInstanceName of the EventSubscriber method 
    // having “OnBehalfField”
    var subscription =
        entity.subscribe(
            1,
            notificationCallback,
            "testuser1@contoso.com",
            "OnBehalfSubscribeCustomer",
            lobSystemInstance
    );
    context.load(subscription);
    context.executeQueryAsync(OnSubscribeSuccess, failmethod);

}

function OnSubscribeSuccess() {
    alert("EventReceiver Added on Entity");
}
function failmethod() {
    alert("Subscribe failed");
}

Step 6: Add a RemoteEventReceiver for consuming the change event Notifications.

  1. Right-click the solution, and select Add New Item, RemoteEventReceiver.
  2. Delete the Elements.xml file under the RemoteEventReceiver1.svc module, as we are not using the declarative addition of an event receiver for an external content type.
  3. In the generated RemoteEventReceiver1.svc, add the code for consuming events. Here you can have code as per your business logic, like creating a new SharePoint workflow, or making an entry into some SharePoint list within the app, or sending push notifications to a phone application.

Following is a code example for consuming events and adding a new item into another list named EventList:

1. Open the TokenHelper.cs file and add the highlighted part to the following method:

 public static ClientContext CreateRemoteEventReceiverClientContext(
    SPRemoteEventProperties properties
)
{
    Uri sharepointUrl = null;
    try
    {
        if (properties.ListEventProperties != null)
        {
            sharepointUrl = 
                new Uri(properties.ListEventProperties.WebUrl);
        }
        else if (properties.ItemEventProperties != null)
        {
            sharepointUrl = 
                new Uri(properties.ItemEventProperties.WebUrl);
        }
        else if (properties.WebEventProperties != null)
        {
            sharepointUrl = 
                new Uri(properties.WebEventProperties.FullUrl);
        }
        else if (properties.EntityInstanceEventProperties != null)
        {
            sharepointUrl = 
            new Uri(
            properties.EntityInstanceEventProperties.NotificationContext
            );
        }
        else
        {
            return null;
        }

        if (ClientCertificate != null)
        {
            return TokenHelper.GetS2SClientContextWithWindowsIdentity(
                sharepointUrl, 
                null
            );
        }
        else
        {
            return CreateAcsClientContextForUrl(
                properties, 
                sharepointUrl
            );
        }
    }
    catch (Exception e)
    {
        logging.log.Add("Exception : " + e.Message + e.StackTrace);
        return null;
    }
}

Note that since while registering the event receiver, we had set the current site URL as the NotificationContext while consuming the event in the event receiver, we are constructing the client context using the property EntityInstanceEventProperties.NotificationProperties.

2. Now open the RemoteEventReceiver1.svc.cs file, and add custom code for handling events, similar to the following:

 using (ClientContext clientContext = 
    TokenHelper.CreateRemoteEventReceiverClientContext(properties))
{
    if (clientContext != null)
    {
         clientContext.Load(clientContext.Web);
         clientContext.ExecuteQuery();
         List list = clientContext.Web.Lists.GetByTitle("EventList");
         clientContext.Load(list);
             clientContext.ExecuteQuery();
       
         ListItemCreationInformation itemInfo = 
             new ListItemCreationInformation();
         ListItem item = list.AddItem(itemInfo);
         item["Title"] = 
            "Event triggered : " + properties.EventType.ToString();

         item.Update();
         list.Update();

         clientContext.ExecuteQuery();
      }
}

Enabling an external system to send notifications for events

Enabling subscriptions:

In order to support BCS events, the OData service from which entity information is being fetched now needs to have additional support to create and delete subscription records. Details about this are available in How to: Create an OData data service that sends notifications to BCS in SharePoint 2013. In addition to the subscription entity fields described earlier, the following extra fields to store on-behalf user information would be required depending upon the design of the external system:

  1. On-behalf caller user: The user by whose identity the subscription creation request has arrived at the external system.
  2. User subscribed: The name of the user or group on whose behalf the subscription is created.
  3. Type of user: Either an individual or a group.

Enabling notifications:

After the one-time action of creating a subscription is done, the external system needs to invoke a notification whenever a change happens in its database. I am working on another post for an example of a polling service that polls for changes that happen at an external system and sends notifications on the specified delivery address per subscription. For handling on-behalf subscriptions, you have to modify your polling service to take into account user field and item associations.