Pages

Monday, January 5, 2009

Add your own script in asp.net validation script when page is submitted

When we create a web page and use asp.net validator asp.net creates some javascript functions which is used to validate controls. Asp.net uses Page_ClientValidate() function to validate client side validators. Now if you want to do something before submitting this page (so if the page is valid to submit and all validator controls are vlaid) then you can do so by calling the Page_ClientValidate() method by yourself. Let assume the script below:

    <script type="text/javascript">

        function doSomethingBeforeSubmitting() {

            var isPageSubmitting = Page_ClientValidate();

            if (isPageSubmitting) {

            //write your code to do before page submitting.

            }

        }

    </script>

 

Now you can call this method on the client click event of an asp server side button as shown below:

<asp:Button ID="btnSubmit" runat="server" Text="Submit" OnClientClick="return doSomethingBeforeSubmitting();" />

 

May be till now to you have got nothing special. Let's make the things a bit interesting. Now lets say the page we are going to submit will take too long to submit. In that case to prohibit user to click on the submit button twice we need to disable the submit button when the page is going to be submit. Remember we don't need to disable the button if the page is not submitting because of asp.net validator control's validation failed. we can write this code as shown below:

 

    <script type="text/javascript">

        function doSomethingBeforeSubmitting() {

            var isPageSubmitting = Page_ClientValidate();

            if (isPageSubmitting) {

                var submitButton = document.getElementById('<%=btnSubmit.ClientID %>');

                submitButton.disabled = true;

            }

        }

    </script>

 

Now lets say after submitting the page the user is redirected to another page. If the user click on browser's back button to come back to this page then user may get the submit button disabled. To get rid of this problem you may think that we may write script on document's onload event to enable the button again. But when you come to page clicking browser's back button you may not get this event fired. In IE this event will be fired but in Firefox and others this event is not fired. So what you are going to do?

To get rid of this problem I had found a solution. I had written script on document's onunload event. I had enabled the button on this unload event. So before submitting the page the button was enabled again. So if user go back to the page using browser's back button he'll get the button enabled as I had already enabled the button before leaving the page. Here's the code block:

 

    <script type="text/javascript">

        window.onunload = "EnableSubmitButton";

        function EnableSubmitButton() {

            var submitButton = document.getElementById('<%=btnSubmit.ClientID %>');

            submitButton.disabled = false;       

        }

        function doSomethingBeforeSubmitting() {

            var isPageSubmitting = Page_ClientValidate();

            if (isPageSubmitting) {

                var submitButton = document.getElementById('<%=btnSubmit.ClientID %>');

                submitButton.disabled = true;

            }

        }

    </script>

 

Here in the above script I have registered EnabledSubmitButton method on window's onload event by the following:

window.onunload = "EnableSubmitButton";

 

So I did it in reverse way, rather than enabling the button on window's onload event, I enabled the button on button onunload event.

Sunday, December 14, 2008

Jeffrey Richter’s Power Threading Library

Recently I have viewed the video on Jeffrey Richter's Power Threading Library and it's amazing. From this video I have got the idea that Asynchronous programming is possible with Synchronous approach. Jeffrey's Power Threading Library provides AsyncEnumrrator class which can be used for leveraging Asynchronous models with Synchronous model. Channel 9 video can be found http://channel9.msdn.com/posts/Charles/Jeffrey-Richter-and-his-AsyncEnumerator/. Finally the idea is really really great! We can find the related information from Jeffrey's blog from http://wintellect.com/PowerThreading.aspx

Tuesday, November 11, 2008

Email marketing with ConstantContact

I had come to know few days ago about email marketing. One of the client (say MyClient) had a requirement to send mail to it's customers once a day. So what's the simple option? The simple option is to send mail to customers a day from MyClient. But if the number of recipients are thousands then MyClient will have to take load of sending so much mail. But if it would be possible to have another provider to whome MyClient provide it's client's list and the mail and then the provider would send mail as scheduled by MyClient then it should be easier for MyClient to manage. Email marketing is one with this purpose. Companies like EmailLabs, ConstantContact provide emial marketing. I had got a chance to work with ConstantContact API few days ago. But ConstantContact has no rich documentation of how to use its API with .NET. As I had figured some way to work with, I thought I need to share it with others. I'm going to provide three functions to add contact, remove contact and get the url of a list. FYI, till the day I have published this blog, ConstantContact API yet has no support for adding and updating mails.

==========================================================================================================

        /// <summary>

        /// Identify the given list's url from the constantcontact site

        /// </summary>

        /// <param name="listName"></param>

        /// <returns></returns>

        private string GetListUrl(string listName)

        {

            string apiUrl = "http://api.constantcontact.com/ws/customers";

            string userName = "user name";

            string password = "password";

            //to access api we need an api key to generate from constant contact site

            string apiKey = "yourapikey";

            //this is the namespace of returned url from constantcontact.

            //This name space is required for parsing the xml

            string xmlNamespace = @"http://www.w3.org/2005/Atom";

            //this is teh complete url to connect to

            string completeurl = apiUrl + string.Format("/{0}/lists", userName);

            Uri address = new Uri(completeurl);

            HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;

            //here the network credential will be 'apikey%username'

            request.Credentials = new NetworkCredential(string.Format("{0}%{1}", apiKey, userName), password);

            request.Method = "get";

            //request.contenttype = "application/x-www-form-urlencoded";

            HttpWebResponse response = request.GetResponse() as HttpWebResponse;

            using (response)

            {

                //get the response in reader

                StreamReader reader = new StreamReader(response.GetResponseStream());

                XmlDocument doc = new XmlDocument();

                //load the reader content in xml document

                doc.Load(reader);

 

                //prepare namespace manager here

                XmlNamespaceManager nsm = new XmlNamespaceManager(doc.NameTable);

                nsm.AddNamespace("cc", xmlNamespace);

                XmlNodeList nodes = doc.SelectNodes("//cc:entry", nsm);

                XmlNode targatedNode = null;

                foreach (XmlNode singelNode in nodes)

                {

                    XmlNode titleNode = singelNode.SelectSingleNode("cc:title", nsm);

                    //loop here to find the list name

                    if (titleNode.InnerText.ToLower() == listName.ToLower())

                    {

                        targatedNode = singelNode;

                        break;

                    }

                }

                //list name found so return here

                if (targatedNode != null)

                {

                    XmlNode idNode = targatedNode.SelectSingleNode("cc:id", nsm);

                    return idNode.InnerText;

                }

            }

            return string.Empty;

        } ==========================================================================================================

        /// <summary>

        /// Add all contacts of the given emails

        /// </summary>

        /// <param name="optMails"></param>

        public void AddContact(IList<string> emails)

        {

            try

            {

                string apiUrl = "http://api.constantcontact.com/ws/customers";

                string userName = "username";

                string password = "password";

                //to access the api u need to have an api key which u can generate from constant contact site

                string apiKey = "api key";

                string listName = "list name to which you want to add contacts";

                //get the list url

                string listUrl = GetListUrl(listName);

                string completeurl = apiUrl + string.Format("/{0}/activities", userName);

                Uri address = new Uri(completeurl);

                //create a web requst to be sent

                HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;

                //set the credential for the request. Here user name will be 'apikey%username'

                request.Credentials = new NetworkCredential(string.Format("{0}%{1}", apiKey, userName), password);

                request.Method = "post";

                request.ContentType = "application/x-www-form-urlencoded";

 

                //build data here.

                //get help from here for data format:

                //      http://developer.constantcontact.com/node/35#comment-14

                //      http://developer.constantcontact.com/doc/activities#UrlEncodedParameters

 

 

                StringBuilder sb = new StringBuilder();

                sb.Append("activityType=" + HttpUtility.UrlEncode("ADD_CONTACTS", Encoding.UTF8));

                sb.Append("&data=" + HttpUtility.UrlEncode("Email Address\n", Encoding.UTF8));

                foreach (string mail in emails)

                {

                        sb.Append(HttpUtility.UrlEncode(string.Format("{0}", mail), Encoding.UTF8));

                }

                sb.Append("&lists=" + HttpUtility.UrlDecode(listUrl, Encoding.UTF8));

                byte[] byteData = UTF8Encoding.UTF8.GetBytes(sb.ToString());

                request.ContentLength = byteData.Length;

                //write data to http stream

                using (Stream postStream = request.GetRequestStream())

                {

                    postStream.Write(byteData, 0, byteData.Length);

                }

                //read data here to ensure that the write was done successfully. If write is failed

                //then u'll get exception when u'll try to read it

                using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)

                {

                    // Get the response stream 

                    StreamReader reader = new StreamReader(response.GetResponseStream());

                    //this string can be used to know the status of the write operation

                    string st = reader.ReadToEnd();

                }

            }

            catch (Exception exp)

            {

                throw;

            }

        }

  ==========================================================================================================

        /// <summary>

        /// Remove a contact from constantcontact

        /// </summary>

        /// <param name="email"></param>

        public void RemoveContact(string email)

        {

            try

            {

                string apiUrl = "http://api.constantcontact.com/ws/customers";

                string userName = "username";

                string password = "password";

                //to access the api u need to have an api key which u can generate from constant contact site

                string apiKey = "api key";

                string listName = "list name to which you want to add contacts";

                //get the list url

                string listUrl = GetListUrl(listName);

                string completeurl = apiUrl + string.Format("/{0}/activities", userName);

                Uri address = new Uri(completeurl);

                //create a web requst to be sent

                HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;

                //set the credential for the request. Here user name will be 'apikey%username'

                request.Credentials = new NetworkCredential(string.Format("{0}%{1}", apiKey, userName), password);

                request.Method = "post";

                request.ContentType = "application/x-www-form-urlencoded";

 

                //build data here.

                //get help from here for data format:

                //      http://developer.constantcontact.com/node/35#comment-14

                //      http://developer.constantcontact.com/doc/activities#UrlEncodedParameters

 

                StringBuilder sb = new StringBuilder();

                sb.Append("activityType=" + HttpUtility.UrlEncode("REMOVE_CONTACTS_FROM_LISTS", Encoding.UTF8));

                sb.Append("&data=" + HttpUtility.UrlEncode("Email Address\n", Encoding.UTF8));

                sb.Append(HttpUtility.UrlEncode(string.Format("{0}", email), Encoding.UTF8));

                sb.Append("&lists=" + HttpUtility.UrlDecode(listUrl, Encoding.UTF8));

                byte[] byteData = UTF8Encoding.UTF8.GetBytes(sb.ToString());

                request.ContentLength = byteData.Length;

 

                //write data to stream

                using (Stream postStream = request.GetRequestStream())

                {

                    postStream.Write(byteData, 0, byteData.Length);

                }

                //read data here to ensure that the write was done successfully. If write is failed

                //then u'll get exception when u'll try to read it

                using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)

                {

                    // Get the response stream 

                    StreamReader reader = new StreamReader(response.GetResponseStream());

                    string st = reader.ReadToEnd();

                }

            }

            catch (Exception exp)

            {

                throw;

            }

        }

 

Friday, October 24, 2008

Provider Model (ASP.NET)

Provider model is just a way to allow user to contribute to API. For example you have a API with Class SecurityProvider:
public abstract class BaseSecurityProvider
{
    public bool Authenticate(string userName, string password);
}


Then you have defined an Sql2005SecurityProvider derived from BaseSecurityProvider as follows:
public class Sql2005SecurityProvider : BaseSecurityProvider
{
   public override bool Authenticate(string userName, string password)
    {
         //add logic here to authenticate against sql server 2005
     }
}


You have exposed your API with those Sql2005SecurityProvider so that user can authenticate against Sql server 2005. Now what if user wants to use Oracle or Active directory? May be one solution would be to implement another security provider such as OracleSecurityProvider. But it would be easier if we allow users to implement their own provider. To do so provider model is a best choice. To implement the provider model we need to refractor the BaseSecurityProvider abstract class. The modified code should look like as below:
    public abstract class BaseSecurityProvider
    {
        public bool Authenticate(string userName, string password);
        public static BaseSecurityProvider GetInstance()
        {
            string typeName = ConfigurationManager.AppSettings["TypeName"];
            string assemblyName = ConfigurationManger.AppSettings["AssemblyName"];
            BaseSecurityProvider securityProvider = System.Activator.CreateInstance(assemblyName, typeName);
        }
    }


Now users can implement a ActiveDirectory security provider class derived form BaseSecurityProvider and write necessary code in Authenticate method to authenticate against Active Directory:
public class ActiveDirectorySecurityProvider : BaseSecurityProvider
{
   public override bool Autheticate(stirng userName, string password)
   {
         //authenticate against AD.
    }
}

and then user can add the two entries in config file as
<AppSettings>
   <add key="AssemblyName" value................./>
   <add key="TypeName" value="ActiveDirectorySecurityProvider" />
</AppSettings>

Now to use your Active directory provider the code block will be
if (ActiveDirectorySecurityProvider .GetInstance().Authenticate(userName, password))
{
// authenticattion succeed
}

Now if a company say (Jaxara IT) supply the API with two class BaseSecurity provider and Sql2005SecurityProvider then another programmer of another company (say Orion Informatics Ltd.) can write a class inherited form BaseSecurityProvider to authenticate against another source.

For more information you can read Scott Mitchell's post here

Wednesday, September 17, 2008

Recursive FindControl

The following method can be used to find a control recursively:

private Control FindControlRecursive(Control root, string id) 
{
if (root.ID == id)
{
return root;
}

foreach (Control c in root.Controls)
{
Control t = FindControlRecursive(c, id);
if (t != null)
{
return t;
}
}

return null;
}

Monday, August 18, 2008

Thousand separator in SharePoint field item

I have a list with field ActivityYear of number type. When user put an number in this field and view the number in a list view then it shows with thousand separator. For example if user put 2008 then in the view of the list it shows as 2,008. we can create a calculated column with formula =TEXT(value,fromula) function. So all I did I put the formula for the calculated field as =Text(ActivityYear,"0000")

Sunday, August 3, 2008

CSS Reference Chart

I have got an important link regarding SharePoint CSS reference chart here:

SharePoint CSS Reference

Here is the link  to follow the sharepoint branding

http://www.cleverworkarounds.com/2007/10/08/sharepoint-branding-how-css-works-with-master-pages-part-1/