Sunday, June 23, 2013

Provider Hosted App - Azure and Read, Write to SharePoint List with real example

Hi,

After my first blog on simple app,
http://anujabhojani.blogspot.in/2013/06/step-by-step-sharepoint-2013-provider.html

Now will go in detail about how to read and write SharePoint list with Provider Hosted App.

Here to cover all the scenario implementing it with real example by creatingvTrello App.

http://www.trello.com is basically a task management tool. I am developing app which will have following functionality,
1. App will take user token (oauth) from Trello to read all the cards  for current logged in user.
2. Trello will return token, which our app will store in one SharePoint list so next time when user visit the app, will get the token from list and directly get all the cards from trello.

Now, lets start and build this app.

1. Having Default.aspx in my solution, adding following code in page_load to get the access token


  1. public partial class Default : System.Web.UI.Page
  2. {
  3. public static string userToken = string.Empty;
  4. Uri appWeb;
  5. string myAccessToken;
  6. ClientContext clientContext;
  7. protected void Page_Load(object sender, EventArgs e)
  8. {
  9. if (!IsPostBack)
  10. {
  11. TokenHelper.TrustAllCertificates();
  12. var contextToken = TokenHelper.GetContextTokenFromRequest(Page.Request);
  13. HttpCookie cookie = new HttpCookie("AccessToken");
  14. SharePointContextToken spContextToken = TokenHelper.ReadAndValidateContextToken(contextToken, Request.Url.Authority);
  15. appWeb = new Uri(Page.Request["SPHostUrl"]);
  16. myAccessToken = TokenHelper.GetAccessToken(spContextToken, appWeb.Authority).AccessToken;
  17. cookie.Value = myAccessToken;
  18. Response.Cookies.Add(cookie);
  19. ReadListItems(appWeb.ToString(), myAccessToken);
  20. }
  21. }

2.  Here in SharPoint site I have created one list named "usertoken", with UserName and Token, In ReadListItems will check the Trello token is present for current logged in user or not, if it will be there will get it directly and display the Trello cards assigned to Logged in user.
For Reading List item using REST API to get list items. 

  1. private void ReadListItems(string webUrl, string myAccessToken)
  2. {
  3. string oDataUrl = "/_api/Web/lists/getbytitle('UserToken')/items?$select=Token,UserName";
  4. var requestUrl = new Uri(string.Format("{0}{1}", webUrl, oDataUrl));
  5. HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(requestUrl);
  6. Response.Write(webUrl.ToString() + oDataUrl);
  7. request.Method = "GET";
  8. request.Accept = "application/atom+xml";
  9. request.ContentType = "application/atom+xml;type=entry";
  10. request.Headers.Add("Authorization", "Bearer " + myAccessToken);
  11. HttpWebResponse response = (HttpWebResponse)request.GetResponse();
  12. XDocument oDataXML = XDocument.Load(response.GetResponseStream(), LoadOptions.None);
  13. XNamespace atom = "http://www.w3.org/2005/Atom";
  14. XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
  15. XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
  16. List<XElement> entries = oDataXML.Descendants(atom + "entry")
  17. .Elements(atom + "content")
  18. .Elements(m + "properties")
  19. .ToList();
  20. var entryFieldValues = from entry in entries
  21. select new
  22. {
  23. Token = entry.Element(d + "Token").Value,
  24. UserName = entry.Element(d + "UserName").Value
  25. };
  26. var itemCOllection = entryFieldValues.ToList();
  27. clientContext = TokenHelper.GetClientContextWithAccessToken(webUrl, myAccessToken);
  28. Web web = clientContext.Web;
  29. clientContext.Load(web);
  30. clientContext.ExecuteQuery();
  31. clientContext.Load(web.CurrentUser);
  32. clientContext.ExecuteQuery();
  33. var currentUser = clientContext.Web.CurrentUser.Title;
  34. for (int i = 0; i < itemCOllection.Count; i++)
  35. {
  36. Response.Write(itemCOllection[i].Token);
  37. if (itemCOllection[i].UserName == currentUser & itemCOllection[i].Token != string.Empty)
  38. {
  39. userToken = itemCOllection[i].Token.ToString();
  40. }
  41. }
  42. if (userToken == string.Empty)
  43. {
  44. Response.Redirect("https://trello.com/1/authorize?key=fd43232051021f33680eb2c6cf3125b3&name=TrellSharepoint&expiration=never&response_type=token&return_url=<SiteName>/Home.aspx?FromTrello=yes&Web=" + appWeb);
  45. }
  46. else
  47. {
  48. Response.Redirect("<SiteName>/Home.aspx?FromTrello=No&token=" + userToken);
  49. }
  50. }
3. Now as you see, we are moving to Home.aspx with Trello userToken if it will be there in list.
If not present in list will redirect to trello site and get authentication token from there,
In home.aspx we have to add script to get UserToken from trello return url.
 function GetUserToken() {
            var token;
            var url = document.location;
            var strippedUrl = url.toString().split("#");
            if (strippedUrl.length > 1) {
                self.location = strippedUrl[0] + "?" + strippedUrl[1];
            }
        }
will call this function on Body onload.

4. For reading trello cards, I have used Chello.dll which is .net wrapper for trello development.
Add reference of chello in solution.
  1. protected void Page_Load(object sender, EventArgs e)
  2. {
  3. if (Request.QueryString != null && Request.QueryString.Count > 0 && Request.QueryString.AllKeys.Contains("token"))
  4. {
  5. ChelloClient chelloClient = new ChelloClient(System.Configuration.ConfigurationManager.AppSettings["TrelloAppKey"], Request.QueryString["token"]);  
  6. IEnumerable<Board> boards = chelloClient.Boards.ForUser("my");
  7. boardGrid.DataSource = boards;
  8. boardGrid.DataBind(); 
  9. }
  10. if (Request.QueryString.AllKeys.Contains("FromTrello"))
  11. {
  12. var IsRedirectfromTrello = Request.QueryString["FromTrello"];
  13. if (IsRedirectfromTrello == "yes")
  14. {
  15. AddToken();
  16. }
  17. }
  18. }

This will display the boards available for current logged in user, And as you see I have took grid to bind it.  
  1. <ItemTemplate>
  2. <tr style="height:50px;">
  3. <td>
  4. <asp:LinkButton runat="server" ID="linkBoardName" Text="" CommandName="borad"><%#Eval("Name") %></asp:LinkButton></td>
  5. </tr>
  6. </ItemTemplate>
         

This will display the boards available for current logged in user, And as you see I have took grid to bind it.  
  1. <ItemTemplate>
  2. <tr style="height:50px;">
  3. <td>
  4. <asp:LinkButton runat="server" ID="linkBoardName" Text="" CommandName="borad"><%#Eval("Name") %></asp:LinkButton></td>
  5. </tr>
  6. </ItemTemplate>
            
5. Here in grid we are binding boards name into link button, and on button click having following code
  1. protected void boardGrid_ItemCommand(object sender, ListViewCommandEventArgs e)
  2. {
  3. if (e.CommandName == "borad")
  4. {
  5. LinkButton btn = e.CommandSource as LinkButton;
  6. ChelloClient chelloClient = new ChelloClient(System.Configuration.ConfigurationManager.AppSettings["TrelloAppKey"], Request.QueryString["token"]);
  7. IEnumerable<Chello.Core.List> list = chelloClient.Lists.ForBoard("513584cbf885e0711c0007fe");
  8. IEnumerable<Card> cards = chelloClient.Cards.ForBoard("513584cbf885e0711c0007fe");
  9. myListView.DataSource = list.Take(3);
  10. myListView.DataBind();
  11. ListView1.DataSource = cards.Where(t => t.IdList == list.First().Id);
  12. ListView1.DataBind();
  13. boardGrid.Visible = false;
  14. }
  15. } 
6. As in code I have took Listview to bind cards, Now its time to write Trello token back to list,
So next time when user come will not redirect to Trello site for getting user token. I am writing to SharePoint list by ClientContext to cover both the scenarios:
In page_load we have call AddToken(); 
  1. private void AddToken()
  2. {
  3. var appWeb = new Uri("<Siteurl>");
  4. HttpCookie cookie = new HttpCookie("AcessToken");
  5. cookie = Request.Cookies["AccessToken"];
  6. var myAccessToken = cookie.Value;  
  7. ClientContext clientContext = TokenHelper.GetClientContextWithAccessToken(appWeb.ToString(), myAccessToken);
  8. Web web = clientContext.Web;
  9. clientContext.Load(web);
  10. clientContext.ExecuteQuery();
  11. clientContext.Load(web.CurrentUser);
  12. clientContext.ExecuteQuery();
  13. var currentUser = clientContext.Web.CurrentUser.Title;
  14. Microsoft.SharePoint.Client.List oList = web.Lists.GetByTitle("UserToken");
  15. ListItemCreationInformation itemCreation = new ListItemCreationInformation();
  16. Microsoft.SharePoint.Client.ListItem listItem = oList.AddItem(itemCreation);
  17. listItem["UserName"] = currentUser;
  18. listItem["Token"] = Request.QueryString["token"];
  19. listItem.Update();
  20. clientContext.ExecuteQuery();
  21. }
We are done! Just publish the solutions and will get trello cards on our Azure hosted app.
While publishing I came across few bugs which resolved and described in following blogs which might be helpful :

http://anujabhojani.blogspot.in/2013/06/error-web-deployment-task-failed.html

http://anujabhojani.blogspot.in/2013/06/the-parameter-token-cannot-be-null-or.html

Happy Coding!

Wednesday, June 19, 2013

Step By Step- SharePoint 2013 Provider Hosted App - Azure Hosting

Here In this blog on SharePoint 2013 Provider hosted app will try to cover each and every steps to create provider hosted app.

1. Open Visual Studio 2012.



2. Click on New Project -> Select SharePoint template 


3. Visual studio will prompt to add Office 365 SharePoint Site you are using and select the Provider-hosted from dropdown list.



4.  On selection of Provide Hosted, click Next you will be prompted with following screen.
Select use a client secret option.



5. Now, Click on Finish. visual studio will take some time to prepare solution.



6. Solution has been created with two projects, looks like below image,


7.  App project will be deployed to SharePoint. And AppWeb project will be .Net Web Application. 


8. Now lets Deploy Provider-Hosted App to SharePoint.
 here, I am adding some sample test text in default.aspx as per below code,

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Test</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
   
<h2>First Azure Page.</h2>     </div>
    </form>
</body>
</html>

9.  Check the code behind of default.aspx contains code to get Accesstoken from TokenHelper.cs

10. Now,


11. Copy the App Id to AppManifest.xml file in your Visual Studio project. will look like below, you need to modify it by right click and view code.

<?xml version="1.0" encoding="utf-8" ?>

  <Properties>
    <Title>TestApp</Title>
    <StartPage>
http://anuja-bhojani.azurewebsites.net/Pages/Default.aspx?{StandardTokens}</StartPage>
  </Properties>
  <AppPrincipal>
    <RemoteWebApplication ClientId="f3bdddd6-65a1-5g89-acd5-655dddddd94c" />
  </AppPrincipal>
</App>

12. We have to add this to Web.config also.

<appSettings> 
  <add key="ClientId" value="f3bdddd6-65a1-5g89-acd5-655dddddd94c" />
 <add key="ClientSecret" value="damw+Svgx3K999556744332999zmmGmI1C6HKWWKbGU=" />
 
</appSettings>

13. Last step, need to give permission on App, Due to that we can interact with SharePoint. Please look the image below.


14. We are good to go now, Right click on app project -> Publish

15. Select Publish Profile from drop down, if not yet downloaded, click on import and from azure site download it.



16. Click Next, you will get automatically feel up ClientID and Secret, Just press Finish.


17. You will see Page where you need to click on "Trust App". Will redirect to azure site where will get our test message.

We are done with simple provider hosted app!

For the advance operation list read and write operation:
http://anujabhojani.blogspot.in/2013/06/provider-hosted-app-azure-and-read.html

Wednesday, June 12, 2013

Error: Web deployment task failed. .. User_UNAUTHORIZED - Provider Hosted App

During development of provider hosted app, while deployment I was greeted with error :

Web deployment task failed. (Connected to the remote computer ("waws-prod-hk1-001.publish.azurewebsites.windows.net") using the Web Management Service, but could not authorize. Make sure that you are using the correct user name and password, that the site you are connecting to exists, and that the credentials represent a user who has permissions to access the site.  Learn more at: http://go.microsoft.com/fwlink/?LinkId=221672#ERROR_USER_UNAUTHORIZED.)

After some research came to know need to download the Publish Profile from site and attached new publish profile, helps me to solve this issue.

It was due to changes in site happens are not detected in old publish profile, which leads to this error.

Hope this fix will save you time!


Tuesday, June 11, 2013

The option for the SharePoint 2013 workflow platform is not available because the workflow service is not configured on the server

Hi there,

I have configured SharePoint 2013 and Workflow Manager.
Created new Site Collection. However I was not able to create SharePoint 2013 workflows.
Option for"SharePoint 2013 Workflow" (but SharePoint 2010 workflows works).

Issue was, The site collection was created using blank template, which doesn't include all necessary dependency features for creating workflows.

Fix: we need to enable hidden feature WorkflowServiceStore using PowerShell.

Enable-SPFeature -Identity WorkflowServiceStore –Url $yourUrl

And finally I got option in SharePoint Designer:




Saturday, June 8, 2013

The parameter 'token' cannot be a null or empty string - Provider Hosted App SharePoint 2013

Hi,

Today while my research on SharePoint Provider hosted app with Azure, after deployment I was greeted with "The parameter 'token' cannot be a null or empty string ".
I was almost crazy behind finding the cause. However finally I found some points which leads me to resolve this error.

  • The ClientID in your AppManifest.xml and in your Web.Config should be same.
  • In AppManifest.xml file domain reference to Azure should be HTTPS rather than HTTP.
  • Should have proper permission on web,list,site collection according to your app.
  •   
    Happy coding!