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!

2 comments:

  1. Anuja , is there a way to do this on premise , instead of off prem

    ReplyDelete
  2. hi

    I have one question for you......

    I am calling a site wf from a list wf , The list wf gets cancelled on call of site workflow

    the site wf works if run independently

    I get the list not exists error

    The task lists and history are associated and exists

    can you please get me the solution for this

    Thanks,
    Suraj

    ReplyDelete