06 May 2009
Lately I’ve been working on my side project called Twime. So as part of that project, I wanted to add the ability to parse URLs, usernames and hashtags from the user’s Twitter timeline. Here’s how I went about doing that:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text.RegularExpressions;
public static class HTMLParser
{
public static string Link(this string s, string url)
{
return string.Format("<a href=\"{0}\" target=\"_blank\">{1}</a>", url, s);
}
public static string ParseURL(this string s)
{
return Regex.Replace(s, @"(http(s)?://)?([\w-]+\.)+[\w-]+(/\S\w[\w- ;,./?%&=]\S*)?", new MatchEvaluator(HTMLParser.URL));
}
public static string ParseUsername(this string s)
{
return Regex.Replace(s, "(@)((?:[A-Za-z0-9-_]*))", new MatchEvaluator(HTMLParser.Username));
}
public static string ParseHashtag(this string s)
{
return Regex.Replace(s, "(#)((?:[A-Za-z0-9-_]*))", new MatchEvaluator(HTMLParser.Hashtag));
}
private static string Hashtag(Match m)
{
string x = m.ToString();
string tag = x.Replace("#", "%23");
return x.Link("http://search.twitter.com/search?q=" + tag);
}
private static string Username(Match m)
{
string x = m.ToString();
string username = x.Replace("@", "");
return x.Link("http://twitter.com/" + username);
}
private static string URL(Match m)
{
string x = m.ToString();
return x.Link(x);
}
}
So as you can see I’m using the new Extension Methods feature in C# 3.0.
Now I can simply just call the extension methods like this:
string tweet = "Just blogged about how to parse HTML from the @twitter timeline - http://jes.al/2009/05/how-to-parse-twitter-usernames-hashtags-and-urls-in-c-30/ #programming";
Response.Write(tweet.ParseURL().ParseUsername().ParseHashtag());
and the result should looks something like this:
Just blogged about how to parse html from the @twitter timeline - http://jes.al/2009/05/how-to-parse-twitter-usernames-hashtags-and-urls-in-c-30/ #programming
Just be sure to call ParseURL method before ParseUsername and ParseHashtag. The other two methods will add URLs to the usernames and hashtags and you don’t want ParseURL to confuse those links with the original links present in the text.
This was inspired by Simon Whatley’s post about doing something similar using prototyping with JavaScript.
20 Feb 2009
Before using this code, all you need to do is register for an UPS OnLine Tools account and then apply for a XML Access key using your developer key which you will receive after creating an account. After you get your access key, just plug your username, password and the key into the highlighted line below and you should be good to go!
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;
/// <summary>
/// Summary description for ShippingCalculator
/// </summary>
namespace MyCo.BusinessLogic
{
public static class ShippingCalculator
{
public static decimal GetShippingCost(string shipFromZipCode, string shipToZipCode, string serviceRateCode, string packageWeight)
{
decimal shippingCost = -1;
string URL = "https://www.ups.com/ups.app/xml/Rate";
WebRequest objRequest = WebRequest.Create(URL);
objRequest.Method = "POST";
objRequest.ContentType = "application/x-www-form-urlencoded";
using (StreamWriter writer = new StreamWriter(objRequest.GetRequestStream()))
{
writer.Write(GetAuthXML("accesskey", "username", "password"));
writer.Write(GetRequestXML(shipFromZipCode, shipToZipCode, serviceRateCode, packageWeight));
writer.Flush();
writer.Close();
}
objRequest.Timeout = 5000;
try
{
using (WebResponse objResponse = objRequest.GetResponse())
{
XmlDocument xmlResponse = new XmlDocument();
xmlResponse.Load(objResponse.GetResponseStream());
int responseStatusCode = int.Parse(xmlResponse.SelectSingleNode("//RatingServiceSelectionResponse/Response/ResponseStatusCode").InnerText);
if (responseStatusCode == 1)
{
XmlNodeList xmlNodeList = xmlResponse.SelectNodes(string.Format("/RatingServiceSelectionResponse/RatedShipment/TotalCharges[../Service/Code={0}]/MonetaryValue", serviceRateCode));
foreach (XmlElement xmlElement in xmlNodeList)
{
shippingCost = decimal.Parse(xmlElement.InnerText);
}
}
}
}
catch (Exception ex)
{
//Log exception
}
return shippingCost;
}
private static string GetAuthXML(string accessLicenseNumber, string userID, string password)
{
StringBuilder xmlAuthTemplate = new StringBuilder();
xmlAuthTemplate.Append("<?xml version="1.0"?>");
xmlAuthTemplate.Append("<AccessRequest xml:lang="en-US">");
xmlAuthTemplate.Append(string.Format("<AccessLicenseNumber>{0}</AccessLicenseNumber>", accessLicenseNumber));
xmlAuthTemplate.Append(string.Format("<UserId>{0}</UserId>", userID));
xmlAuthTemplate.Append(string.Format("<Password>{0}</Password>", password));
xmlAuthTemplate.Append("</AccessRequest>");
xmlAuthTemplate.Append("<?xml version="1.0"?>");
return xmlAuthTemplate.ToString();
}
private static string GetRequestXML(string shipFromZipCode, string shipToZipCode, string serviceRateCode, string packageWeight)
{
StringBuilder xmlRequestTemplate = new StringBuilder();
xmlRequestTemplate.Append("<RatingServiceSelectionRequest xml:lang="en-US">");
xmlRequestTemplate.Append("<Request>");
xmlRequestTemplate.Append("<TransactionReference>");
xmlRequestTemplate.Append("<CustomerContext>Rating and Service</CustomerContext>");
xmlRequestTemplate.Append("<XpciVersion>1.0001</XpciVersion>");
xmlRequestTemplate.Append("</TransactionReference>");
xmlRequestTemplate.Append("<RequestAction>Rate</RequestAction>");
xmlRequestTemplate.Append("<RequestOption>shop</RequestOption>");
xmlRequestTemplate.Append("</Request>");
xmlRequestTemplate.Append("<PickupType>");
xmlRequestTemplate.Append("<Code>01</Code>");
xmlRequestTemplate.Append("</PickupType>");
xmlRequestTemplate.Append("<Shipment>");
xmlRequestTemplate.Append("<Shipper>");
xmlRequestTemplate.Append("<Address>");
xmlRequestTemplate.Append(string.Format("<PostalCode>{0}</PostalCode>", shipFromZipCode));
xmlRequestTemplate.Append("</Address>");
xmlRequestTemplate.Append("</Shipper>");
xmlRequestTemplate.Append("<ShipTo>");
xmlRequestTemplate.Append("<Address>");
xmlRequestTemplate.Append(string.Format("<PostalCode>{0}</PostalCode>", shipToZipCode));
xmlRequestTemplate.Append("</Address>");
xmlRequestTemplate.Append("</ShipTo>");
xmlRequestTemplate.Append("<Service>");
xmlRequestTemplate.Append(string.Format("<Code>{0}</Code>", serviceRateCode));
xmlRequestTemplate.Append("</Service>");
xmlRequestTemplate.Append("<Package>");
xmlRequestTemplate.Append("<PackagingType>");
xmlRequestTemplate.Append("<Code>02</Code>");
xmlRequestTemplate.Append("<Description>Package</Description>");
xmlRequestTemplate.Append("</PackagingType>");
xmlRequestTemplate.Append("<Description>Rate Shopping</Description>");
xmlRequestTemplate.Append("<PackageWeight>");
xmlRequestTemplate.Append(string.Format("<Weight>{0}</Weight>", packageWeight));
xmlRequestTemplate.Append("</PackageWeight>");
xmlRequestTemplate.Append("</Package>");
xmlRequestTemplate.Append("<ShipmentServiceOptions/>");
xmlRequestTemplate.Append("</Shipment>");
xmlRequestTemplate.Append("</RatingServiceSelectionRequest>");
xmlRequestTemplate.Append("</RatingServiceSelectionRequest>");
return xmlRequestTemplate.ToString();
}
}
}
25 Oct 2008
Recently I discovered a better way to format my email messages in ASP.NET using the MailDefinition object. It lets you to use an email template and define tokens which you want to replace in it. This helps keep the presentation and business layers clean & seperate and lets the designers go in and edit the email templates without having to navigate the StringBuilder jungle.
Here’s how its done.
private void SendEmail(long customerID)
{
Customer customer = CustomerData.GetCustomer(customerID);
MailDefinition mailDefinition = new MailDefinition();
mailDefinition.BodyFileName = "~/Email-Templates/Order-Confirmation.html";
mailDefinition.From = "[email protected]";
//Create a key-value collection of all the tokens you want to replace in your template...
ListDictionary ldReplacements = new ListDictionary();
ldReplacements.Add("<%FirstName%>", customer.FirstName);
ldReplacements.Add("<%LastName%>", customer.LastName);
ldReplacements.Add("<%Address1%>", customer.Address1);
ldReplacements.Add("<%Address2%>", customer.Address2);
ldReplacements.Add("<%City%>", customer.City);
ldReplacements.Add("<%State%>", customer.State);
ldReplacements.Add("<%Zip%>", customer.Zip);
string mailTo = string.Format("{0} {1} <{2}>", customer.FirstName, customer.LastName, customer.EmailAddress);
MailMessage mailMessage = mailDefinition.CreateMailMessage(mailTo, ldReplacements, this);
mailMessage.From = new MailAddress("[email protected]", "My Site");
mailMessage.IsBodyHtml = true;
mailMessage.Subject = "Order Confirmation";
SmtpClient smtpClient = new SmtpClient(ConfigurationManager.AppSettings["SMTPServer"].ToString(), 25);
smtpClient.Send(mailMessage);
}
Your email template could be of any extension (txt, html, etc) as long as its in a text format. I personally like to keep it in HTML format so that we can preview the email template in a browser. Basically it’ll looks something like this -
Hello <%FirstName%> <%LastName%>,
Thank you for creating an account with us. Here are your details:
<%Address1%>,
<%Address2%>
<%City%>, <%State%> <%Zip%>
Thank You,
My Site
10 Oct 2008
Generating PDF forms in .NET has always been somewhat messy and complicated. I’ve seen people convert raw HTML to PDF or adding and formatting form elements to a PDF doc in code behind, etc. All those methods are highly time consuming and tricky. Easiest way to create PDF Forms is by using iTextSharp (open source) and Adobe LiveCycle Designer.
You can just create your custom form template using LiveCycle and then data bind the form fields using iTextSharp like this:
User user = UserData.GetUserByID(userID);
string randomFileName = Helpers.GetRandomFileName();
string formTemplate = Server.MapPath("~/FormTemplate.pdf");
string formOutput = Server.MapPath(string.Format("~/downloads/Forms/Form-{0}.pdf", randomFileName));
PdfReader reader = new PdfReader(formTemplate);
PdfStamper stamper = new PdfStamper(reader, new System.IO.FileStream(formOutput, System.IO.FileMode.Create));
AcroFields fields = stamper.AcroFields;
// set form fields
fields.SetField("Date", DateTime.Now.ToShortDateString());
fields.SetField("FirstName", user.FirstName);
fields.SetField("LastName", user.LastName);
fields.SetField("Address1", user.Address1);
fields.SetField("Address2", user.Address2);
fields.SetField("City", user.City);
fields.SetField("State", user.State);
fields.SetField("Zip", user.Zip);
fields.SetField("Email", user.Email);
fields.SetField("Phone", user.Phone);
// set document info
System.Collections.Hashtable info = new System.Collections.Hashtable();
info["Title"] = "User Information Form";
info["Author"] = "My Client";
info["Creator"] = "My Company";
stamper.MoreInfo = info;
// flatten form fields and close document
stamper.FormFlattening = true;
stamper.Close();
04 Oct 2008
Here’s a basic ADO.NET data access template with a SqlCacheDependency. Just wanted to post this for my future reference.
public static DataSet GetData(long itemID)
{
DataSet ds = new DataSet();
string cacheKey = Helpers.GetCacheKey(itemID); //Get a cache key unique to this method.
if (HttpContext.Current.Cache[cacheKey] != null)
{
ds = (DataSet)HttpContext.Current.Cache[cacheKey];
}
else
{
using (SqlConnection sqlConnection = new SqlConnection(WebConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
{
using (SqlCommand sqlCommand = new SqlCommand())
{
sqlCommand.Connection = sqlConnection;
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.CommandText = "sp_Items_GetItemByID";
sqlCommand.Parameters.Add("@itemID", SqlDbType.BigInt).Value = itemID;
sqlConnection.Open();
using (SqlDataAdapter da = new SqlDataAdapter(sqlCommand))
{
da.Fill(ds);
}
}
}
SqlCacheDependency sqlCacheDependency = new System.Web.Caching.SqlCacheDependency(WebConfigurationManager.AppSettings["DatabaseName"], "Items");
HttpContext.Current.Cache.Insert(cacheKey, ds, sqlCacheDependency, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration);
}
return ds;
}
Note that I’ve employed most of the recommended best practices like caching, “using” keywords, stored procedures and connection string inside web.config.