Walkthrough: Deploying a SignalR autohosted App to Office 365

baasopchoppySecurity

Nov 5, 2013 (3 years and 7 months ago)

186 views

Walkthrough: Deploying a SignalR autohosted App to
Office 365

W
RITTEN BY
E
LLIOT
W
OOD AND
M
ATT
M
ENEZES


Step 1: Add an App for SharePoint 2013 project to your solution

New project > SharePoint App



Step 2: Add proxy AutoDetect (if you are behind one)


The following code should be manually added to the web.config file:


<system.net>


<defaultProxy>


<proxy autoDetect="True" />


</defaultProxy>


</system.net>


Step 3: Configure your App to include an App
Event Receiver

Goto your App’s propert
ies window, Set Handle App Installed to True



Step 4: Add a Remote Event Receiver to your App

Add

a
New Item

to the SharePoint app project


Select
Remote Event Receiver
, and click
Add



Choose
List Item Events
, and
Custom list

(We can ignore this event source as we will register one
manually later on).


Choose to handle the following events: An item is being
added
,
updated
,
deleted
. Click
Finish
.



Step 5: Configure the required permissions for your App

Give your app
Full
Control

permissions to the
Web

and

List



Step 6: Add a Global.asax file to your App Web project

In your web project Add a
Global Application Class
called
Global



Then add the following code



using

Microsoft.AspNet.SignalR;

using

System;

using

System.Web.Routing;


namespace

SharePointRWeb

{


public

class

Global

: System.Web.
HttpApplication


{


protected

void

Application_Start(
object

sender,
EventArgs

e)


{


var

hubConfiguration =
new

HubConfiguration
();



hubConfiguration.EnableCrossDomain =
true
;


hubConfiguration.EnableDetailedErrors =
true
;


RouteTable
.Routes.MapHubs(
"/signalr"
, hubConfiguration);


}


}

}


Step 7: Add the SignalR Hub class to your App Web project

Go to your web project and
Add

a

New Item


Add

a
SignalR Hub Class



Also
a
dd

a new
Class
, we are going to call ours SharePointR



Add the following code to the new class



using

Microsoft.AspNet.SignalR;

using

Microsoft.AspNet.SignalR.Hubs;

using

System;


namespace

SharePointRWeb

{


public

class

SharePointR


{


// Singleton instance


private

readonly

static

Lazy
<
SharePointR
> _instance =
new

Lazy
<
SharePointR
>(()
=>


new

SharePointR
(
GlobalHost
.ConnectionManager.GetHubContext<
SharePointRHub
>().Clients));


public

SharePointR(
IHubConnectionContext

clients)


{


Clients = clients;


}


public

static

SharePointR

Instance


{


get


{



return

_instance.Value;


}


}


private

IHubConnectionContext

Clients


{


get
;


set
;


}


public

void

NotifyDataChanged(
string

ListName,
string

Event)


{


Clients.Group(ListName).dataChanged(Event);


}


}

}


Edit the hub class and add the following code


using

Microsoft.AspNet.SignalR;

using

Microsoft.AspNet.SignalR.Hubs;


namespace

SharePointRWeb

{


[
HubName
(
"SharePointRHub"
)]


public

class

SharePointRHub

:
Hub


{


private

readonly

SharePointR

_sharePointR;



public

SharePointRHub() :
this
(
SharePointR
.Instance) { }



public

SharePointRHub(
SharePointR

sharePointR)


{


_sharePointR = sharePointR;


}



public

void

Subscribe(
string

ListName)


{


Groups.Add(Context.ConnectionId, ListName);


}



public

void

UnSubscribe(
string

ListName)


{


Groups.Remove(Context.ConnectionId, ListName);



}



public

void

NotifyDataChanged(
string

ListName,
string

Event)


{


_sharePointR.NotifyDataChanged(ListName, Event);


}


}

}


Step 8: Configure the App Event Receiver to manually bind the Item Event
Receivers to a g
iven List

O
pen the
App Event Receiver

code behind. Add the code below

which

will manually hook up an item
event receiver to a list in the host web
(for the example we use

the

Announcements

list
)
. You can
easily hook this up to more than one list here and
because we implement a subscribe model above
,
the

client will
only
receive the items they are sub
scribed

to.




using

System.Collections.Generic;

using

System.Linq;

using

Microsoft.SharePoint.Client;

using

Microsoft.SharePoint.Client.EventReceivers;

using

System.ServiceModel;

using

System.Reflection;


namespace

SharePointRWeb.Services

{


public

class

AppEventReceiver

:
IRemoteEventService


{


private

string

ReceiverName =
"RemoteEventReceiver"
;


private

List
<
EventReceiverType
> EventType

=
new

List
<
EventReceiverType
>()


{


EventReceiverType
.ItemAdding,


EventReceiverType
.ItemUpdating,


EventReceiverType
.ItemDeleting


};


private

string

ListTitle =
"Announcements"
;



public

SP
RemoteEventResult

ProcessEvent(
SPRemoteEventProperties

properties)


{


SPRemoteEventResult

result =
new

SPRemoteEventResult
();



using

(
ClientContext

clientContext =
TokenHelper
.CreateAppEventClientContext(properties,
false
))



{


Web

hostWeb = clientContext.Web;


clientContext.Load(hostWeb);



List

docLib = clientContext.Web.Lists.GetByTitle(ListTitle);



string

opContext =
OperationContext
.Current.Channel.LocalAddress.Uri.AbsoluteUri.Substring(0,


OperationContext
.Current.Channel.LocalAddress.Uri.AbsoluteUri.LastIndexOf(
"/"
));


string

remoteUrl =
string
.Format(
"{0}/RemoteEventReceiver.svc"
,
opContext);




if

(properties.EventType ==
SPRemoteEventType
.AppInstalled)


{


foreach

(
var

eventType
in

EventType)


{


EventReceiverDefinitionCreationInformation

newEventReceiver =
new

EventReceiverDefinitionCreationInformation
()


{


EventType = eventType,


ReceiverAssembly =
Assembly
.GetExecutingAssembly().FullName,


ReceiverClass

=
"SignalRProxyService.Services.RemoteEventReceiver"
,


ReceiverName = ReceiverName + eventType.ToString(),


ReceiverUrl = remoteUrl,


SequenceNumber = 1000



};


docLib.EventReceivers.Add(newEventReceiver);


}


clientContext.ExecuteQuery();


}


else

if

(properties.EventType ==
SPRemoteEventType
.AppUninstalling)



{


IEnumerable
<
EventReceiverDefinition
> receivers =
clientContext.LoadQuery(docLib.EventReceivers


.Where(e => e.ReceiverName == ReceiverName));



foreach

(
var

rec
in

receivers)


{


rec.DeleteObject();


}


clientContext.ExecuteQuery();


}


}


return

result;


}



public

void

ProcessOneWayEvent(
SPRemoteE
ventProperties

properties)


{


// This method is not used by app events


}


}

}


Note:

For synchronous “
-
ing” events (like

adding, deleting, updating)

you must use
the

ProcessEvent

method. For

asynchronous


-
ed”

events (like


added, updated, deleted),

use
the

ProcessOneWayEvent

method.


Step 9: Configure the Remote Event Receiver to communicate with the page
via SignalR


Open the
Remote Event Receiver

code behind, add the following code. This will notify the hub when
events o
ccur.


using

System;

using

System.Collections.Generic;

using

System.Linq;

using

System.Text;

using

Microsoft.SharePoint.Client;

using

Microsoft.SharePoint.Client.EventReceivers;


namespace

SharePointRWeb.Services

{


public

class

RemoteEventReceiver

:
IRemoteEventService


{


private

SharePointRHub

client =
new

SharePointRHub
();



public

SPRemoteEventResult

ProcessEvent(
SPRemoteEventProperties

properties)


{


client.NotifyDataChanged(properties.ItemEventProperties.ListT
itle,
properties.EventType.ToString());


return

new

SPRemoteEventResult
(); ;


}



public

void

ProcessOneWayEvent(
SPRemoteEventProperties

properties)


{



}


}

}


Step 10: Create the page that will receive the Signa
lR messages (including
slight branding to the page fits the site)

Using
Manage NuGet Packages

add the json2 package.




It will add json2 to the
Scripts

directory


Add

a new
JavaScript File

called
chromeLoader.js
,

this will allow our app to be branded
correctly.





// Callback for the onCssLoaded event defined

// in the options object of the chrome control

function

chromeLoaded() {


// When the page has loaded the required


// resources for the chrome control,


// display the page body.



$(
"body"
).show();

}


//Function to prepare the options and render the control

function

renderChrome() {


// The Help, Account and Contact pages receive the


// same query string parameters as the main page


var

options = {


"
appIconUrl"
:
"/Images/AppIcon.png"
,


"appTitle"
:
"SignalR Sample Application"
,


"appHelpPageUrl"
:
"default.aspx?"


+ document.URL.split(
"?"
)[1],


// The onCssLoaded event allows you to


// specify a callback to exec
ute when the


// chrome resources have been loaded.


"onCssLoaded"
:
"chromeLoaded()"
,


"settingsLinks"
: [


{


"linkUrl"
:
"Account.html?"


+ document.URL.split(
"?"
)[1],


"
displayName"
:
"Account settings"


},


{


"linkUrl"
:
"Contact.html?"


+ document.URL.split(
"?"
)[1],


"displayName"
:
"Contact us"


}


]


};



var

nav =
new

SP.UI.C
ontrols.Navigation(


"chrome_ctrl_placeholder"
,


options


);


nav.setVisible(
true
);

}


// Function to retrieve a query string value.

// For production purposes you may want to
use

// a library to handle the query string.

function

getQueryStringParameter(paramToRetrieve) {


var

params =


document.URL.split(
"?"
)[1].split(
"&"
);


var

strParams =
""
;


for

(
var

i = 0; i < params.length; i = i + 1) {


var

singleParam = params[i].split(
"="
);


if

(singleParam[0] == paramToRetrieve)


return

singleParam[1];


}

}


var

hostweburl;


//load the SharePoint resources

$(document).ready(
function

() {


//Get the URI decoded URL.


hostweburl =


decodeURIComponent(


getQueryStringParameter(
"SPHostUrl"
)


);



// The SharePoint js files URL are in the form:


// web_url/_layouts/15/resource


var

scriptbase = hostweburl +
"/_layouts/15/"
;



// Load the js file and con
tinue to the


// success handler


$.getScript(scriptbase +
"SP.UI.Controls.js"
, renderChrome)

});

In the web project go to the
default.aspx.cs

file under
/pages
, replace the

code behind with the
following




using

System;

using

System.Collections.Generic;

using

System.Linq;

using

System.Web;

using

System.Web.UI;

using

System.Web.UI.WebControls;


namespace

SharePointRWeb.Pages

{


public

partial

class

Default

: System.Web.UI.
Page


{


public

string

SiteTitle =
string
.Em
pty;



protected

void

Page_Load(
object

sender,
EventArgs

e)


{


// The following code gets the client context and Title property by using
TokenHelper.


// To access other properties, you may need to request permissions o
n the
host web.



var

contextToken =
TokenHelper
.GetContextTokenFromRequest(Page.Request);


var

hostWeb = Page.Request[
"SPHostUrl"
];



using

(
var

clientContext =
TokenHelper
.GetClientContextWithContextToken(hostWeb,
contextToken,
Request.Url.Authority))


{


clientContext.Load(clientContext.Web, web => web.Title);


clientContext.ExecuteQuery();



SiteTitle = clientContext.Web.Title;


}


}


}

}

In the web project go to the
default.aspx

file under
/pages
, replace the mark
-
up with the following:


<%
@

Page

Language
="C#"

AutoEventWireup
="true"

CodeBehind
="Default.aspx.cs"

Inherits
="SharePointRWeb.Pages.Default"

%>

<!
DOCTYPE

html
>

<
html

xmlns
="http://www.w3.org/1999/xhtml">

<
head
>


<
title
>
SignalR Sample
</
title
>

</
head
>

<!
--

The body is initally hidden. The onCssLoaded callback allows you to display the
content after the required resources for the chrome control have been loaded.
--
>

<
bo
dy

style
="
display
:
none
;
">


<!
--

Chrome control placeholder
--
>


<
div

id
="chrome_ctrl_placeholder"></
div
>


<!
--

The chrome control also makes the SharePoint


Website stylesheet available to your page
--
>


<
h1

id
="HostSiteTitle"

class
="m
s
-
accentText">
<%
=

this
.SiteTitle
%>
</
h1
>


<
div

id
="MainContent"></
div
>


<!
--
Script references.
--
>


<
script

src
="//ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js"></
script
>


<!
--
Reference the jQuery/JSON library.
--
>


<
script

src
="/Scripts/jquery
-
1.8.2.min.js"></
script
>


<
script

src
="/Scripts/json2.js"></
script
>


<!
--
Reference the chromeLoader helper.
--
>


<
script

src
="/Scripts/chromeLoader.js"></
script
>


<!
--
Reference the SignalR library.
--
>


<
script

src
="/Scripts/jquery.signalR
-
1.0.0.js"></
script
>


<!
--
Reference the autogenerated SignalR hub script.
--
>


<
script

src
="/signalr/hubs"></
script
>



<!
--
Add script to update the page and send messages.
--
>


<
script

type
="text/javascript">


$(
f
unction

() {


var

connection = $.hubConnection(
'/signalr/hubs'
);


proxy = connection.createHubProxy(
'SharePointRHub'
)


proxy.on(
'dataChanged'
,
function

(eventType) {


$(
'#MainContent'
).append(
'<li>Data change
d
-

'

+ eventType +
'</li>'
);


});


connection.start()


.done(
function

() {


//Subscribe to Announcements list


proxy.invoke(
'Subscribe'
,
"Announcements"
);


$(
'#
MainContent'
).append(
'<span>Now connected, connection ID='

+
connection.id +
'</span>'
);


})


.fail(
function

() {


$(
'#MainContent'
).append(
'<span>Could not Connect!</span>'
);


});


});


</
script
>

</
body
>

</
html
>


Go to the app project and edit the appmanifest, goto the genral tab and add the following to the
QueryString “
{StandardTokens
}&SPHostTitle={HostTitle}
” required for
branding.


in the web project add an
Images

folder



Add

and and image called “
AppIcon.png
” to the
Images

folder



Step 11: Create the list in SharePoint that the event receiver will be bound
to

Go to your SharePoint site create a list called
Announcements



Step 12: Deploy the App to Office 365

Now press F6

or
B
uild

Solution


And click
D
eploy


note:
If you receive an error here see troubleshooting as the bottom.


Trust

the app and now it is ready for use (details below)


Alternatively

You may want to deploy the app to the app catalog
(recommended for
production)
.

Publish the app

.






Now the app is published here is a step by step

guide to

how to deploy it.

Go to your
App Catalog

Site
(you may need to create one, tutorial not included here)

in site contents go to apps for SharePoint


Drag your
published app to the library to deploy it




Add an app

>
SharePointR





Trust

the app



Step 13: Run the App!

Open the announcements list side by side with the Page that will receive the SignalR

messages.
Add/Edit/Remove items from the list and watch the magic.




Appendix: Troubleshooting



W
hen publishing a SharePoint hosted application to a development site on Office 365 Preview,
running SharePoint 2013, you may receive the following error:

“Error occurred in deployment step ‘Install App for SharePoint’: Sideloading of apps is not enabled
on this site.

Solution: You need to enable the Sideloading feature on the site.

1.

Download and install the

SharePoint Online Management Shell

for PowerShell

2.

Download the script

Sideload.ps1

and execute it within the SharePoint Online Managemen
t Shell.

Note: This is not recommended for a production site.

Deploy applications using the App Catalog

on a
production site.

Source “
http://blog.lekman.com/2012/11/sharepoint
-
2013
-
sideloading
-
of
-
apps
-
is.html


I used the following code to enable the feature on my host site


using

(
var

context =
new

ClientContext
(Url))


{


//Pass the Credentials to SharePoint Online


context.Credentials =
new

SharePointOnlineCredentials
(UserName,
Password);



var

site = context.Site;


context.Load(site);



context.ExecuteQuery();



site.Features.Add(
new

Guid
(
"E374875E
-
06B6
-
11E0
-
B0FA
-
57F5DFD72085"
),
true
,
FeatureDefinitionScope
.Farm);


context.ExecuteQuery();

}