SharePoint 2013: Remote Event Receivers

Recently I got rolled off from a SharePoint 2010 project for few weeks. So, I decided to spend some of my free time on SharePoint 2013. Since apps are a new concept in SP 2013 I decided to create some sample apps. I did not make any production apps as I am yet to get allocated on a project which uses SP 2013 apps. In this series of blog posts I will try to share my leanings. 
This series will consist of following posts:
Develop SharePoint 2013 apps with NAPA
Access data in Host web
Isolated App domain Setup Issues
Create SharePoint Hosted App in Visual Studio
Provider Hosted App Development Prerequisites
App Event Receivers
Remote Event Receivers
Scenario: Create a Provider Hosted app and add a custom list to it. Add a choice column named Country to the list. The choice values will be AUS, CAN, NZL and "Other". When an item is created/updated in the list, the item permissions are broken based on selected Country value. If "Other" choice value is selected then the item inherits the permissions from parent. 
Before deploying the app, create three SharePoint Groups in the site in which app will be installed. Name the groups AUS, CAN and NZL and attach Contribute permissions to them. This step can be done manually or if you have followed my earlier blog about app event receivers, then code for creating groups can be added in app installed event and app will need to be given FullControl permissions.
Open Visual Studio and create a Provider Hosted app. If you want to create SharePoint Groups during app installed event then add the app installed event and add the code as shown in app event receivers. Next add a custom list definition to the project.

Add a Country choice column to the newly created list.

Open the Schema.xml file of the list and update the Country filed with choice values as shown.
<Field Name="Country" ID="{1f6825ce-56db-4403-a75b-f56de7c59e8b}" DisplayName="Country" Type="Choice">
    <CHOICES>
      <CHOICE>Other</CHOICE>
      <CHOICE>AUS</CHOICE>
      <CHOICE>CAN</CHOICE>
      <CHOICE>NZL</CHOICE>
    </CHOICES>
    <Default>Other</Default>
  </Field> 
Next add Remote Event Receiver to the app project. Add ItemAdded and ItemUpdated events.

Notice that a WCF service is created with sample code. There are two methods ProcessEvent which handles the "ing" events like ItemAdding, ItemUpdating, etc. and ProcessOneWayEvent which handles "ed" events like ItemAdded. 
Moreover, Elements.xml file is created which contains the information about the events like Name, Type and Url.
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Receivers ListTemplateId="10000">
    <Receiver>
      <Name>DemoRemoteEventReceiverItemAdded</Name>
      <Type>ItemAdded</Type>
      <SequenceNumber>10000</SequenceNumber>
      <Url>~remoteAppUrl/Services/DemoRemoteEventReceiver.svc</Url>
    </Receiver>
    <Receiver>
      <Name>DemoRemoteEventReceiverItemUpdated</Name>
      <Type>ItemUpdated</Type>
      <SequenceNumber>10000</SequenceNumber>
      <Url>~remoteAppUrl/Services/DemoRemoteEventReceiver.svc</Url>
    </Receiver>
  </Receivers>
</Elements>
Update the code in the service file to handle ItemAdded and ItemUpdated events.
public void ProcessOneWayEvent(SPRemoteEventProperties properties)
{
    if (properties.EventType == SPRemoteEventType.ItemAdded)
    {
        string country = properties.ItemEventProperties.AfterProperties["Country"].ToString();
        var itemId = properties.ItemEventProperties.ListItemId;
        var listId = properties.ItemEventProperties.ListId;
        using (ClientContext clientContext = TokenHelper.CreateRemoteEventReceiverClientContext(properties))
        {
            if (clientContext != null)
            {
                List list = clientContext.Web.Lists.GetById(listId);
                ListItem item = list.GetItemById(itemId);
                GroupCollection collGroup = clientContext.Web.SiteGroups;
                clientContext.Load(collGroup);
                clientContext.ExecuteQuery();
                Group grp = collGroup.Where(g => g.Title == country).FirstOrDefault();
                if (grp != null)
                {
                    AddNewGroup(item, clientContext, grp);
                }                        
            }
        }
    }

    else if (properties.EventType == SPRemoteEventType.ItemUpdated)
    {
        string title = properties.ItemEventProperties.AfterProperties["Country"].ToString();
        var itemId = properties.ItemEventProperties.ListItemId;
        var listId = properties.ItemEventProperties.ListId;
        using (ClientContext clientContext = TokenHelper.CreateRemoteEventReceiverClientContext(properties))
        {
            if (clientContext != null)
            {
                List list = clientContext.Web.Lists.GetById(listId);
                ListItem item = list.GetItemById(itemId);
                GroupCollection collGroup = clientContext.Web.SiteGroups;
                clientContext.Load(collGroup);
                clientContext.ExecuteQuery();
                clientContext.Load(item, i => i.HasUniqueRoleAssignments);
                clientContext.ExecuteQuery();
                if (item.HasUniqueRoleAssignments)
                {
                    // Reset permissions
                    item.ResetRoleInheritance();
                    clientContext.ExecuteQuery();

                    // Add new group
                    Group newGrp = collGroup.Where(g => g.Title == title).FirstOrDefault();
                    if (newGrp != null)
                    {
                        AddNewGroup(item, clientContext, newGrp);
                    }
                }
                else
                {
                    Group grp = collGroup.Where(g => g.Title == title).FirstOrDefault();
                    if (grp != null)
                    {
                        AddNewGroup(item, clientContext, grp);
                    }
                }
            }
        }
    }
}

private void AddNewGroup(ListItem item, ClientContext clientContext, Group grp)
{
    item.BreakRoleInheritance(false, false);
    RoleDefinitionBindingCollection collRollDefinitionBinding = new RoleDefinitionBindingCollection(clientContext);
    collRollDefinitionBinding.Add(clientContext.Web.RoleDefinitions.GetByType(RoleType.Contributor));
    item.RoleAssignments.Add(grp, collRollDefinitionBinding);
    clientContext.ExecuteQuery();
}
The code reads the value in the Country field and based on the value it finds a group with the same name and gives only that group permissions on the list item. If it doesn't find the group the item permissions are not broken.
Open the feature definition in the app project and make sure that List Definition, List instance and RemoteEventReceiver are present in "Items in Feature:" section.
Change the landing page of the app from Default.aspx page of web project to AllItems.aspx page of the list in AppManifest.xml file.
Press F5 to launch the app and grant permissions to the app. The AllItems.apsx page of the list will show up. Add new item to the list.
If a break point is added in the code it can be hit on clicking the save button. 
Once the item is saved, right click on it and click on Shared With.


Edit the same item and now select "Other" in the Country option. Notice now the list item permissions are not broken.
profile for Nadeem Yousuf at SharePoint Stack Exchange, Q&A for SharePoint enthusiasts

+