Saturday, 28 May 2011

Combining Silverlight and Google Contact & Client API on Windows Azure


 The scope of this article is to access Google APIs through Silverlight application on Windows Azure.

 Integration
 The documentation consists of the following parts:
     1.  Access Google APIs in Silverlight Application
-       Write a web service to access Google APIs.
-       Consume service in Silverlight application.
     2.      Deploy application on Windows Azure
     3.      Troubleshooting

How I want it to work?
Silverlight does not work directly with the Google API. For this, we need to add a web service that will communicate with the Google API. Access this service from Silverlight.

The inner workings
Create Silverlight Web project. We have to create IList<> of objects that Silverlight can recognize, to bind data dynamically to Silverlight Controls. So now add classes to represent Calendar Events and Contact entries.  For this, right click the Silverlight Web Project and click Add New Item.  Add a class representing the Calendar Event and Contact with the following properties:
Figure 1: Write classes for event and Contact entity that Silverlight can recognize.

Now create Web service to access Google APIs:
1.    For this, right click the Silverlight Web Project and select Add New Item.  Next, select Web Service.  This will generate an asmx file.  Each operation in this method needs to be tagged with a [WebMethod] attribute.  Note that this would also have worked with a WCF service.  Add references to the Google Contact and Calendar APIs (Please refer to the previous article).  Now write methods to retrieve all calendar events &contacts and,to add calendar events.
Figure 2: Write function in Web service to access Google APIs
2.    Build the Web Service Project.
3.    Next, right-click the Silverlight project and click on Add ServiceReference.  Click on the Discover button.  Here you should be able to see the list of methods that were exposed in the service.  Enter a suitable name for the proxy and click the OK button.


Figure 3: Add service reference

4.    Next, in MainPage.xaml.cs, create an instance of the Service Reference as such:
CalendarProxy.GoogleAPIServiceSoapClient proxy = newGoogleAPIServiceSoapClient();
5.    In the click events of the three buttons, call the web methods through the proxy instance.  In the completed event handler of the methods, bind the lists to the datagrids.

Figure 4: Consume web service to get all contacts details

Figure 5: Consume web service to get event

Figure 6: Consume web service to add events

6. Build the solution, hit F5 and check whether the Silverlight application is able to access the Google API

Migrate the Silverlight Application to Azure




Figure 7: Add existing web project

4.    Right click on the solution and again select Add Existing Project.  Browse to the folder where the Silverlight application that was created in step 4.1. 

5.    Now select the Silverlight project and select the csproj for that project.  Click on OK.


Figure 8: Select Silverlight project

6.    Your solution structure should look like the one below:

Figure 9: Azure project Solution structure

7.    Under the Azure Project, right click Roles node and select Add, select Web Role Project in solution.  Select the Web project that was added in the previous step and click OK.

8.    Build the project and hit F5.  The compute emulator should start running and you should be able to see the Silverlight control running on your test page on the devfabric.

Figure 10: Application running in devfabric

Troubleshooting

 

Exception 1: Silverlight on Azure devFabric gives cross domain exception
Exception Message:
An error occurred while trying to make a request to URI 'http://localhost:5311/Service.asmx'. This could be due to attempting to access a service in a cross-domain way without a proper cross-domain policy in place, or a policy that is unsuitable for SOAP services. You may need to contact the owner of the service to publish a cross-domain policy file and to ensure it allows SOAP-related HTTP headers to be sent. This error may also be caused by using internal types in the web service proxy without using the InternalsVisibleToAttribute attribute. Please see the inner exception for more details.

Resolution:
Open ServiceReferences.ClientConfig. You'll notice the endpoint address is something like http://localhost:12345/... Please change it to http://127.0.0.1:81/... Note if port 81 is already occupied, when launching the web role in Development Fabric, port 82 will be used. So please change the port number appropriately.

Posted this on MSDN forum, the reference for which is:
http://social.msdn.microsoft.com/Forums/en-US/windowsazuredevelopment/thread/9fd8432e-ab14-44f6-823a-7e609c3ec655


Exception 2: Silverlight hosted on Azure portal cannot access Web Service

Exception Message:
Service not found.

Resolution:
This exception occurs because the service is hosted on the azure servers, however, the ServiceReference.clientconfig still points to the url of the compute emulator.  The workaround for this is to programmatically configure your service client to read the URL from wherever the application is deployed.  Note that you cannot use relative endpoints in the ServiceReference.clientconfig.  Then, instead of instantiating the proxy class with the default constructor, use the parameterized constructor which takes the endpoint address and the binding type.
 
Following is the code sample:
Replace CalendarProxy.GoogleAPIServiceSoapClient proxy=new GoogleAPIServiceSoapClient();

With

CalendarProxy.GoogleAPIServiceSoapClient proxy=GetServiceClient();
Where GetServiceClient() is defined as


The reference for this is:













Select partial ROW of GridView by using JavaScript and select a column of table by using JQUERY


Select a part of ROW of Grid View by using JavaScript

The problem statement was to select only some part of a row on selection of check box.


I have added client side code while creating rows of grid view. Please refer screenshot below where
m_rowIndex is declared as

privateintm_rowIndex = 0;


This index will identify row uniquely.


Here I have a custom control for creating such kind of grid view, as my page was going to contain any variable number of gridviews.

So to identify selected gridview, I am passing anidentifier for each gridview while creating them.


Now to select partial row, write code on client side in JavaScript:

<%@ControlLanguage="C#"AutoEventWireup="true"CodeBehind="CustomGrd.ascx.cs"Inherits="FTC.CustomGrd"%>
<scriptlanguage="javascript"type="text/javascript">
vargridViewCtlId = 'grdCustom';
vargridViewCtl = null;
varcurSelRow = null;

functiongetGridViewControl() {
if (null == gridViewCtl) {
gridViewCtl = getElementsByClassName('CustomGrid', null, null);
        }
    }

functiongetElementsByClassName(searchClass, domNode, tagName) {
if (domNode == null) domNode = document;
if (tagName == null) tagName = '*';
var el = new Array();
var tags = domNode.getElementsByTagName(tagName);
vartcl = " " + searchClass + " ";
for (i = 0, j = 0; i <tags.length; i++) {
var test = " " + tags[i].className + " ";
if (test.indexOf(tcl) != -1)
el[j++] = tags[i];
        }
return el;
    }

functiononGridViewRowSelected(rowIdx, grdIdx) {
varselGrid = getSelectedGrid(grdIdx);
varselRow = selGrid.rows[rowIdx];
if (curSelRow != null) {
curSelRow.cells[1].bgColor = '#ffffff';
curSelRow.cells[2].bgColor = '#ffffff';
        }

if (null != selRow) {
curSelRow = selRow;
curSelRow.cells[1].bgColor = '#5CB3FF';
curSelRow.cells[2].bgColor = '#5CB3FF';
checkSelectedRow(selGrid, rowIdx);
        }
    }

functioncheckSelectedRow(selGrid, rowIdx) {
for (i = 1; i <selGrid.rows.length; i++) {
if (i == rowIdx) {
selGrid.rows[i].cells[2].children[0].checked = true;
            }
else {
selGrid.rows[i].cells[2].children[0].checked = false;
            }
        }
    }

functiongetSelectedGrid(grdIdx) {
getGridViewControl();
if (null != gridViewCtl) {
returngridViewCtl[grdIdx]
        }
returnnull;
    }

</script>
<asp:GridViewID="grdCustom"runat="server"AutoGenerateColumns="False"GridLines="Both"
OnRowCreated="grdCustom_RowCreated"ShowHeader="false"OnDataBound="grdCustom_DataBound"
BackColor="White"Width="150px"CssClass="CustomGrid">
<Columns>
</Columns>
</asp:GridView>


Select a column of table by using JQUERY

The problem statement was to select a complete column on selection of any cell present in that column.  This can be done by using JQuery.

Created a gridview having following properties:

Now to select a complete column, add path for JQuery. Once that is done, use following JQuery code to select complete column:




Friday, 27 May 2011

Access Google public Calender and contacts of a g-mail account on Windows Azure


Google exposes Google API to access details of Google public calendar and also to access contacts for a g-mail account.  The scope of my investigation is restricted to APIs Google.Contacts and Google.GData.Calendar. 
The problem statement is:
The manager should be able to add meeting request to this subordinates. The manager will be the organizer of meeting, should be able to add event into calendar event attendees.  If there are some clashes it should be notified by application.
The scope is divided into  
  •  Make calendar as public
  •  Give permission to organizer to add events 
  • To get Calendar events
  • To add calendar event
  • To get all contacts
  • To add contact

APIs can be downloaded from location:
http://google-gdata.googlecode.com/svn/docs/index.html



Make Calendar as Public

Go to Calendar -><Name of calendar> calendar -> Calendar settings (Please see figure 1) -> Check “Make this Calendar as Public” (Please see Figure 2)




Figure 1: Calendar settings




Figure 2: Make calendar as public

Give permission to organizer to add events
The organizer of meeting should be given a particular permission to add event into calendar event attendees.  Please see figure 3.

Figure 3: To give permission to organizer to add event into calendar of Attendees.

To access Calendar
The public calendar can be access though Google Data APIs by giving a sharing link. This link is available in calendar setting. Please see figure mentioned below: 

Figure 4: Get link to access calendar 

There are 3 types of link that are provided:
1.       XML
2.       ICAL
3.       HTML
To access calendar, link that is provided by XML button should be used.


To access events by Google APIs
    To add/ remove/ view events of attendees, project should have reference of
usingGoogle.GData.Calendar;
using Google.GData.Client;
using Google.GData.Extensions;
     
     To access contacts from Google APIs
     To access contacts though google APIs, project should have reference of
usingGoogle.GData.Contacts;
using Google.GData.Client;
using Google.GData.Extensions;


   Code Snippets:
     a.   To get Calendar events


CalendarServiceoSrv = newCalendarService("GoogleAPIs_Cal_V1");
ArrayListentryList = newArrayList(50);

EventQueryoQuery = newEventQuery();
oQuery.Uri = newUri(
"https: <Calendar URI> @group.calendar.google.com/private/full.");
if (strUserName != null&&strUserName.Length> 0)
                {
oSrv.setUserCredentials(strUserName, strPwd);
                }
oQuery.StartTime = DateTime.Now.AddHours(-11);
oQuery.EndDate = DateTime.Now.AddDays(1);
EventFeedcalFeed = oSrv.Query(oQuery) asEventFeed;
ArrayList dates = newArrayList(50);
while (calFeed != null&&calFeed.Entries.Count> 0)
                {
// look for the one with dinner time...
foreach (Google.GData.Calendar.EventEntry entry incalFeed.Entries)
                    {
dtRow["EventTitle"] = entry.Title.Text;

foreach (Wherelocinentry.Locations)
strLoc += loc.ValueString + ";";

foreach (AtomPersonlocinentry.Authors)
strAuth += loc.Name + ";";

this.entryList.Add(entry);
if (entry.Times.Count> 0)
                        {
foreach (When w inentry.Times)
                            {
dtRow["StartTime"] = w.StartTime;
dtRow["EndTime"] = w.EndTime;
dates.Add(w.StartTime);
                            }
                        }
                    }
// just query the same query again.
if (calFeed.NextChunk != null)
                    {
oQuery.Uri = newUri(calFeed.NextChunk);
calFeed = oSrv.Query(oQuery) asEventFeed;
                    }
else
calFeed = null;
                }

b. Add Calendar event


Google.GData.Calendar.EventEntry entry = newGoogle.GData.Calendar.EventEntry();

entry.Title = newAtomTextConstruct(
AtomTextConstructElementType.Title,
                                    title);
entry.Authors.Add(newAtomPerson(AtomPersonType.Author, author));
entry.Published = DateTime.Now;
entry.Updated = DateTime.Now;

WherenewPlace = newWhere();
newPlace.ValueString = place;
entry.Locations.Add(newPlace);

WhennewTime = newWhen();

newTime.StartTime = startTime;
newTime.EndTime = endTime;
newTime.AllDay = fAllDay;
entry.Times.Add(newTime);

Google.GData.Calendar.EventEntry test = feed.Insert(entry) asGoogle.GData.Calendar.EventEntry;

c. Get Contact


RequestSettingsrs = newRequestSettings("GoogleAPIs_Cal_V1", strUserName, strPwd);
rs.AutoPaging = true;
ContactsRequestcr = newContactsRequest(rs);

Feed<Contact> f = cr.GetContacts();
IEnumerable<Contact> list = f.Entries;

foreach (Contact entry inf.Entries)
                {
if (entry.Name != null)
                    {
string strContactName = entry.ContactEntry.Name.FullName;
ExtensionCollection<PhoneNumber> phones = entry.ContactEntry.Phonenumbers;
ExtensionCollection<EMail>emAils = entry.ContactEntry.Emails;
                    }
                }
      d. Add Contact
     
R  RequestSettingsrs = new RequestSettings("GoogleAPIs_Cal_V1", strUserName, strPwd);
   rs.AutoPaging = true;
   ContactsRequestcr = newContactsRequest(rs);


   ContactnewEntry = newContact();
   Namename = newName();
   name.FullName = "Matt John";

   EMailprimaryEmail = newEMail("liz@gmail.com");
   newEntry.Emails.Add(primaryEmail);

   PhoneNumberphoneNumber = newPhoneNumber("555-555-5555");
   newEntry.Phonenumbers.Add(phoneNumber);

   StructuredPostalAddresspostalAddress = new
   StructuredPostalAddress();
   postalAddress.Country = "United States";
   postalAddress.Primary = true;
   newEntry.PostalAddresses.Add(postalAddress);

   newEntry.Content = "Contact information for Liz";
   Feed<Contact> fc = cr.GetContacts();
   ContactinsertedEntry = cr.Insert(fc, newEntry);


a.    To get through proxy

     In .NET, any HTTP connection object can have proxy as set as a property.  It can  be handled though code as below:

<Snippet>
GDataRequestFactoryrequestFactory = (GDataRequestFactory)oSrv.RequestFactory;
IWebProxyiProxy = WebRequest.DefaultWebProxy;
WebProxymyProxy = new WebProxy(iProxy.GetProxy(oQuery.Uri));
myProxy.Credentials = CredentialCache.DefaultCredentials;
myProxy.UseDefaultCredentials = true;
requestFactory.Proxy = myProxy;
oSrv.RequestFactory = requestFactory;
</snippet>

However after running application it will throw an error as:

        Error Code: 407 Proxy Authentication Required. The ISA Server requires authorization to fulfill the request. Access to the Web Proxy filter is denied. (12209)

Solution:This can be resolved by setting default proxy in web.config file.

To access public calendar, a link should be provided to calendar service. If this provided link is in format of ICAL, it will throw as error:

Google.GData.Client.ClientFeedException was caught
 Message=Parsing failed
 Source=Google.GData.Client

Solution:Use XML format link to access calendar.


c.   Insertion of contact

An error might be thrown when new contact entry is inserted

Error Code: 407 Proxy Authentications Required. The ISA Server requires authorization to fulfill the request. Access to the Web Proxy filter is denied.
Message = "Cannot update a read-only feed"

Code generating error:
Uri feedUri = new Uri(strCalendarURI);
Contact createdEntry = cr.Insert(feedUri, newEntry);

Solution:
This can be resolved by adding contact as:
Feed<Contact> fc = cr.GetContacts();
ContactinsertedEntry = cr.Insert(fc, newEntry);

d.    Insertion of Events

While linking to calendar we use XML link. 
E.g.

If while adding new entry of events is into public calendar of attendees, we use same link it will throw an error

“Feed is read-only”

To add specific user's primary calendar: This can be resolved by using link
Using this link, event entry will be added to default calendar or to a specific user's primary calendar. It will not be added into public calendar of someone else.

To add public calendar: 
To insert data into public calendar of someone 1, we should use link like

e.   Deployed application on Azure

The deployed application will throw an error as mentioned below:

<Snippet>
Google.GData.Client.GDataRequestException: Execution of request failed: http://www.google.com/m8/feeds/contacts/default/full --->System.Net.WebException: Unable to connect to the remote server --->System.Net.Sockets.SocketException:
A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 209.85.231.104:443
  </Snippet>

Solution: Host web role in HWC (Hostable-Web-Core) mode. To use hwc, open csdef file, remove <sites> section.

Posted the problem on forum:

Reference: