Sunday, May 2, 2010

How to Turn on IIS7 Compression

One thing you can do to speed up your site is by turning on compression on your web server. This approach is highly recommended by Yahoo in their website performance analysis tool YSLOW as well as Google via PageSpeed.

In essence, compression helps in reducing data payload transmitted between the web server and the client browser. With machines (both the server and client machine) that are relatively powerful nowadays to handle compression easily - this than becomes a very viable and efficient solution to reduce network bottlenecks, thus increasing the perceived response to the browsing experience.

Here is how to do it in IIS7:
  1. Open IIS Manager and In "Features" view, double-click Compression. 
  2. Choose one or both of the following: 

    • Enable dynamic content compression to configure IIS to compress dynamic content. 
    • Enable static content compression to configure IIS to compress static content. 

  3. Open the configuration file at "C:\Windows\System32\inetsrv\config\applicationhost.config" and make sure your compression settings are correct or if you want to add/remove any compression settings.
Mine looks like this:
 
<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">
    <scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" />
    <dynamicTypes>
        <add mimeType="text/*" enabled="true" />
        <add mimeType="message/*" enabled="true" />
        <add mimeType="application/x-javascript" enabled="true" />
        <add mimeType="*/*" enabled="false" />
    </dynamicTypes>
    <staticTypes>
        <add mimeType="text/*" enabled="true" />
        <add mimeType="message/*" enabled="true" />
        <add mimeType="application/javascript" enabled="true" />
        <add mimeType="*/*" enabled="false" />
    </staticTypes>
</httpCompression>
That's it! Easy!

Now one caveat is that it seems that IIS7 does not compress javascript file immediately, so it may need a few hits before you can detect it using fiddler or firebug etc. I waited for about 2-3 days before eventually YSLow and PageSpeed both detect that all my javascript files are compressed.
-- read more and comment ...

Monday, March 15, 2010

Browsing securely using an SSH tunnel

Browsing on an internet cafe or a open/free wireless hot spot is scary sometimes. Some places are secured enough (like airport), but some are pretty questionable. You're not sure who owns the router, whether they put security on it, packet tracer, or a phising software etc ... Some times you also want to avoid the proxy that tracks your cookie, traffic, website blocker, etc etc. Regardless of the reasons, you may want to be able to browse securely - and a way to do it is by tunneling it via SSH.

So how does this work? Basically you want to setup an SSH server and redirect all of your browser traffic via this SSH server using an SSH client on your computer (instead of directly via http into the internet). This SSH tunnel is secured, so no one can peek into your traffic, unlike the regular http traffic. So here is how I did mine using Bitvise WinSSHD & Tunnelier. You can download both (free) from Bitvise website.

Installing WinSSHD (the SSH server)

  1. You will need a computer that is always on or with wake-up on LAN enabled. I use my media center machine for this. 
  2. Download WinSSHD (from here) and install it. Just follow the default instruction on the screen and you should be ok. If you are not an administrator on your machine then you will need to install this as an administrator. There is a more detailed User's Guide by Bitvise here.

Configuring WinSSHD

  1. Once installed, the WinSSHD control panel should open with the "Easy WinSSHD Settings" window open.
  2. Under "Server Settings", you can customize the listening ports etc if you want - or just leave then with the default settings and click "Next" and go to "Windows Account".
  3. Under "Windows Account", you can allow or disallow Windows Account to login/connect to your SSH server. The default setting is to allow. I disallow this for security reason and created a locked down virtual users instead. You can read more about virtual vs Windows account here.
  4. Under "Virtual Users", you can add virtual users. Click "Add" and assign account name and customize the permissions for that user, then click "OK". Once the user shows up in the list, select the user row, and click the pencil icon under "Virtual account password" column and the click the "Manage" button, then set the password for that user.
  5. Once the "Easy WinSSHD Settings" window is gone, now you are looking at the WinSSHD control panel.
  6. Now click "Edit advanced settings" and go to "Windows Firewall" area. Make sure that both drop downs are set to "Open ports to any computer" - so you can connect from outside your network/internet. You can read about opening up WinSSHD to the internet here.
  7. Make sure the service is running. If not, start it. There should be a link in the control panel to start or stop the WinSSHD service.

Installing & Configuring Tunnelier

  1. Download Tunnerlier (from here) and install it. Just follow the default instruction on the screen and you should be ok. If you are not an administrator on your machine then you will need to install this as an administrator. There is a more detailed User's Guide by Bitvise here.
  2. Once installed, try connecting to your WinSSHD from within your network by entering the IP/host name, port, username, and password in the login area of Tunnelier and click "Login".
  3. You should see the status of connection and data transmission in the right text area.
  4. Now once it is setup within the network, before you can try it from the outside of your network, you will need to configure port forwarding in your router.

Configuring Router Port Forwarding

  1. A router usually has a port-forwarding tool built-in. I am not sure how each router does their setting specifically, but usually there are several simple steps to register the port-forwarding. Go to portforward.com/ for the specific instructions on how to do it for your router.
  2. Make sure your box that runs the SSH server is running on a static IP address within the local network. You can do this via the router's DHCP or using the TCP/IP setting in your computer.

Registering (dynamic) DNS Name

  1. If you are running your SSH server from home, most likely you have a dynamic IP address. So, what you want to do here is register a dynamic DNS name, so that when your IP change, it is be also updated and tied to your DNS name dynamically. In the end, all you need to remember is your DNS name. The way this works is that dynamic DNS registration company has a small software in your box that will send an updated IP address to your dynamic DNS registry so it continuously updating your DNS name registration with your current IP address.
  2. There are a lot of dynamic DNS provider out there in the internet - make your own pick here. I use No-IP. Usually you just need to register/create an account on their site, pick your DNS name and download the updater client software and install it on your box and you are set!

Trying It From Outside Your Network

  1. To do this obviously you will need to be connecting to the internet from outside your LAN
  2. Open up Tunnelier
  3. Instead of entering the internal IP address, enter the dynamic DNS name you have registered and leave everything else the same and then click "Login"
  4. If the port-forwarding and Windows Firewall and the account settings are set up correctly, you should be connected to your SSH server.

Setting Up Your Web Browser & Tunnelier for Secure Browsing

  1. Open up Tunnelier and go to "Services" tab. Make sure "SOCKS/HTTP Proxy Forwarding" is enabled. Put "127.0.0.1" for "Listen Interface", pick an unused port number (like 8081) enter it in "Listen Port" field, leave the "Server Bind Interface" to be all zeros. Basically, follow these settings in Tunnelier.
  2. Now in your browser, follow these settings: Firefox, Internet Explorer
  3. Now you are set!
-- read more and comment ...

Wednesday, March 10, 2010

AJAX Animation using jQuery

During an AJAX call from a website, usually the screen won't blink - because page is not refreshing. This is neat because it gives the impression to the user that their interaction with the site to be seamless and uninterrupted.

But sometimes, you do want to give a visual clue to the user that an action is happening and you want them to know about it. Maybe by giving them an hourglass or a "loading ..." clue etc. So how do you do this easily? This post is about using jQuery to provide visual clue to the user on AJAX requests.

First, obviously you will need to reference jQuery library. I usually use the one hosted in Google:
 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
Add the HTML to display our visual clue. In this example I am displaying a black bar on the top of the browser, about 25px in height that has a "Loading ..." text in it. So when an AJAX call is happening, this black bar will be displayed until the call is done.
 <div style="z-index:101; width:100%; height:25px; position:fixed; left:0px; top: 0px;">
<div id="loadingbar" style="height:25px;display:none; margin-left:auto; margin-right:auto; background-color:#000000; color:#fff">
  Loading ...</div>
<div class="stretch"> </div>
</div>
Now we add the javascript to show and hide the bar, plus I also like to gray out the background a little bit.
 $(document).ready(function() {
    jQuery().ajaxStart(function() {
        $("body").fadeTo("fast", 0.70);
        $("#loadingbar").show("fast");
    });
    jQuery().ajaxStop(function() {
        $("body").fadeTo("fast", 1.0);
        $("#loadingbar").hide("fast");
    });
});

Done. Now anytime there is an AJAX call in your page, the background will be grayed out a little bit and a black bar will show up on the top. To increase/decrease the opacity of the background during the graying out, you can adjust the value on the "fadeTo" call. Currently it is set to "0.70" or 70%. If you want less transparency (or more grayed out), then set it lower (between 0.00 to 1.0), and set it higher for less transparency.
-- read more and comment ...

Sunday, February 21, 2010

Food Deals, Tips, and Tricks

In this post, I thought I share some of the food deals, tips, and tricks that I found to be beneficial for me - and of course these food joints are the ones I frequent. I am pretty sure there are more out there - if you know those deals, tips, and tricks, let me know in the comment section and I will try them and add them into this post with credit to you.

Deals:
Restaurants.com: Several times a year, Restaurants.com has excellent deals with their discount code. I bought several coupon for $25 off for certain restaurants for only $3 per coupon, some for $4 (depending on the deals). Be on the look out at fatwallet.com or deals.woot.com for coupon code.

Roosters: on Olentangy River Rd chain, they have appetizers deal on Tuesdays. So each Tuesday they will give you a discount on one selected appetizers. For example, last time I went there, it was $1.07 for an order cheese sticks (has 5 sticks in it).

Qdoba: On Tuesday nights, kids eat free at Qdoba. If you are a Qdoba card holder, get double points on Friday. If you go to the one in 5th ave, they give free chips if you come after 8pm - for any day. Students can have a $5 meal - any day.

City Barbeque: Buck-a-bone Tuesdays. So instead of paying $17.99 for a full slab - pay only ~$12. Some stores have $4 off a sampler Mondays.

Applebee's: 1/2 Price Appetizers with any beverage purchase from 10 p.m. to close. At the bar from 11 a.m. to 4 p.m. (dine-in only. excludes sampler).

Tips & Tricks:
Chipotle/Qdoba:

  • For "more" portion in your fajita/burrito without bursting your tortilla, as for bol/naked with tortilla on the side - no extra charge!
  • Chipotle will make quesadilla for you - even though it is not on the menu
  • Order a Quesorito in Chipotle - a hybrid of quesadilla and burrito.
  • You can get half/half (i.e. chicken and steak) for your meat selection - and pay only for the more expensive price
  • You can get "double meat" and pay extra $2 (nore really sure about the price - but should be close)
  • At Chipotle, if you are ordering salad, you can add fajita onions/peppers with no extra charge
BD's Mongolian Barbeque:
  • Instead of paying the full buffet price ($13.99 I think), you can elect to go once ($8.99 - LUNCH only). For most women, this is usually enough, if not - or if you are a man - read the next point.
  • Now, there is a trick on how to arrange your ingredients in your bowl so that you can have plenty (I am mean PLENTY) of food by just going once. The key is to put your vegetables on the bottom and the meat on top - so that the vegetables will get pushed to the bottom by the meat therefore allowing more room. In detail, follow these steps:
    1. If you do not want vegetables, skip to step 2. Get your empty bowl and go to the vegetable section right away, skip the meat for now. Get your vegetables, try to arrange them neatly and allowing an empty opening in the middle.
    2. If you do not want seafood, skip to step 3. Go to the seafood section. If you have vegetables, put your seafood in the opening in the middle of you bowl. If you do not have veggies, arrange them however you want, but try to be neat.
    3. If you do not want carbs, skip to step 4. Go to the carbs section. Get your noodle/pasta and try to arrange them neatly. These "arrange neatly" thing is important but don't be OCD about it. Just make sure they are packed and not overflowing.
    4. Now go the spices section, get the "cooking" ingredients. What I call the "cooking" ingredients are the items that you want to be cooked along from the beginning with your veggies, meat, seafood. For the ingredients that will go later, I call them "sauce" ingredients. So in this step, go get your "cooking" ingredients; usually something like garlic, ginger, some spices, etc - depending on your style and recipe. Don't want to go crazy here, since you can always add them later in the "sauce" - I usually just get minced garlic in this step.
    5. Go back to the meat section. Whatever you do, put beef/steak last - since it is usually sliced thinly - so you use is as a "roof" to hold everything together. Get your meat - in the order or smallest to largest - then put beef strips last.
    6. Now, assemble your sauce. Depending on how much you got to pile on your bowl, you may require more than 1 mini-sauce-bowl to hold your sauce - which is alright. Put your food bowl and on the sauce counter and assemble your sauce, spices, and condiments. 
    7. CRUCIAL STEP - if the cooking area is crowded, make sure you look and pay attention to where do you think you will be lining up at - and go there ONLY WITH YOUR FOOD BOWL, leaving the sauce bowl(s) behind on the sauce counter. If you are not on the cooking counter directly (someone is in front of you with their food being cooked), put your food bowl on the counter and then go back to the sauce counter to get your sauce. If necessary, go get egg(s) as well and go back to your spot in the cooking line and put your sauce bowl(s) and egg(s) on the cooking counter.
    8. Now wait for your turn ...
    9. Pay attention to your food being cooked and tell the cook about your preferences (sauce now/sauce later, egg now/later, etc). When it's done cooking, make sure the cook put your food in the yellow bowl, NOT the blue bowl. Blue bowl is for repeats, so you will be charged full buffet instead of the single. If for some reason, you got a blue bowl, go back to your seat and tell your server about it - that you only went once with 1 ingredients bowl and don't want to be charged full buffet (this only ever happened to me once).
Roosters:
  • You can combined up to 3 wings sauces without any extra charge. So if you want your wings to be in "chipotle" an "garlic" - just say "chipotle garlic" on your sauce selection when you order.


For some more food items that are not in the menu:

-- read more and comment ...

Wednesday, February 17, 2010

Using ASP.NET Chart Controls in ASP.NET MVC

In the past, I blogged about using Google Visualization to render charts with ASP.NET MVC here. It's pretty easy, it works, and fast. What if you do not want to use Google Visualization / javascripts to make your charts? Well, Microsoft released ASP.NET Charting Controls late 2008 and this blog post will discuss several options you can have in using it with ASP.NET MVC.
There are several ways you can do this (that I have tried/used):

  1. Generating and rendering the chart in the controller and returning it as a FileResult using MemoryStream
  2. Generating the chart in the controller and put it in the view data and let the aspx/ascx renders it using the webform control
There are advantages and disadvantages for both approaches. Using the MemoryStream approach works everytime, but it does not render the mapping, so it does not attach url, tooltip, etc on the legend and the chart by default. You have to do those things manually.

Now using the webform control gives you all the bells and whistles (url, tooltip, label, etc) - no extra work needed. BUT - in my experience, it does not work if you run your MVC project under a virtual path. For example, if you run your project under root, such as http://localhost:2000/Home/Index - it will work. But if you then go to the Project Property window in the solution explorer in VS 2008, go the the "Web" tab, and set a virtual path, and build and run (i.e. http://localhost:2000/Chart/Home/Index) - it will break. When it runs, it will give you "Failed to map the path '/ChartImg.axd'". I am still unable to solve this problem yet - you can see my StackOverflow question here.

Before I show you my code, I have to give credit to Mike Ceranski - he blogged about how to use the webform control in rendering ASP.NET chart - in which his approach become the one I used in my projects and become a partial subject of this blog post.

You will need to download a couple of things to get started:

First, you will need to prepare your web.config by adding these lines:
 

    ....
    <appsettings>
        <add key="RenderMode" value="MEMORYSTREAM" />
        <add key="ChartImageHandler" value="privateImages=false;storage=file;timeout=20;deleteAfterServicing=false;url=/App_Data/;WebDevServerUseConfigSettings=true;"/>
    </appSettings>
    ....
    <system.web>
        <httphandlers>
            <add verb="*" path="ChartImg.axd" type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler, System.Web.DataVisualization, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/>
        </httpHandlers>
    </system.web>
    ....
In your development environment, when running using VS 2008 by hitting F5, the images will always be rendered in memory regardless what you put in the appSettings, unless you are including the "WebDevServerUseConfigSettings=true;" in there.

You can also read more about the possible enumerations and values for the appSettings here.

Now, the HTML:
 
<%         
    if (ConfigurationManager.AppSettings["RenderMode"] == "MEMORYSTREAM") {
        Response.Write("");
    } else {
        myChart.Controls.Add(ViewData["Chart"] as Chart);
    }
%>


The controller code:
 
public ActionResult Index() {
    ViewData["Message"] = "Welcome to ASP.NET MVC!";

    if (ConfigurationManager.AppSettings["RenderMode"] != "MEMORYSTREAM") {
        Chart myChart = CreateChart();
        ViewData["Chart"] = myChart;
    }
    return View();
}

public ActionResult Chart() {
    using (var ms = new MemoryStream()) {
        Chart myChart = CreateChart();
        myChart.SaveImage(ms, ChartImageFormat.Png);
        ms.Seek(0, SeekOrigin.Begin);
        return File(ms.ToArray(), "image/png", "mychart.png");
    }
}

private Chart CreateChart() {
    Chart chart = new Chart();
    chart.Width = 350;
    chart.Height = 300;
    chart.Attributes.Add("align", "left");

    chart.Titles.Add("MY CHART"); // Display a Title  
    chart.ChartAreas.Add(new ChartArea());

    chart.Series.Add(new Series());

    chart.Legends.Add(new Legend("MY CHART"));
    chart.Legends[0].TableStyle = LegendTableStyle.Auto;
    chart.Legends[0].Docking = Docking.Bottom;
    chart.Series[0].ChartType = SeriesChartType.Pie;

    for (int i = 0; i < 10; i++) {
        string x = "MEMBER " + (i + 1).ToString();
        decimal y = ChartTest.Models.Utility.RandomNumber(1, 100);
        int memberId = i;
        int point = chart.Series[0].Points.AddXY(x, y);
        DataPoint dPoint = chart.Series[0].Points[point];
        dPoint.Url = "/Member/Detail/" + memberId.ToString();
        dPoint.ToolTip = x + ": #VALY";
        dPoint.LegendText = "#VALX: #VALY";
        dPoint.LegendUrl = "/Member/Detail/" + memberId.ToString();
        dPoint.LegendToolTip = "Click to view " + x + "'s information...";
    }

    chart.Series[0].Legend = "MY CHART";
    return chart;
}
Now, depending on how your "RenderMode" setting is in the web.config, it will either render the chart using approach #1 (memory stream) or #2 (using webform control).

Additional readings/resources:
-- read more and comment ...

Tuesday, February 9, 2010

GeoDocs Reborn, version 8.0 Released!

After a long overdue, GeoDocs 8 was released by AWH on December 2009 (this blog post is late). Last month, GeoDocs revamped its website using the new version, new look and feel, and faster!

In a collaboration with Nationwide Children's Hospital, they became the first client to upgrade to the new version.

GeoDocs was also recognized as a semi-finalist for the 2009 TechColumbus Innovation Awards! GeoDocs was nominated in the category of 'Outstanding Product, Fewer than 50 Employees'.

So what makes GeoDocs 8 to be better than its predecessor?


There are a lot of reasons why GeoDocs 8 is better, as far as the technical aspects - here are some of them:
  • Running on .NET framework 3.5 (GD 7 was running on .NET 1.1)
  • The UI has been rebuilt from scratch using ASP.NET MVC
  • SEO friendly URL
  • AJAX integration with jQuery and jQuery UI
  • Better and cleaner integration with Google Mini/Search Appliance
  • Cleaner code base that enhance development experience
Now if you are a user, here are the benefits of GD 8:
  • Much, much, much faster - and can be faster still depending on you configuration
  • AJAX means better user experience:

    • Less screen refresh to load/reload data
    • Faster feedback in returning on-demand data
    • Nice and unobtrusive animations/cues for user actions
    • Faster and friendlier editing panel (Web View Editor)
    • More robust WYSIWYG editor
  • SEO friendly URL, means nice looking URL and easily crawled by search engine
  • If you are developing your own custom templates/look & feel - it will be much easier
This does not mean that we are removing all the good features and functionality that GD 7 has, GD 8 still has them, but better! GeoDocs since version 7 has boasted robust feature such as:
  • Manage multiple website authors, provide workflow and security
  • Help you manage your graphical brand and optimize navigation and layout
  • Provide secure, granular access and information to defined users
  • Customizable content types that will suit your needs
  • Excellent and popular modules such as Form Builder & Calendar will continue to be supported and enhanced
GeoDocs is a product created and maintained by the Allen, Williams & Hughes Company, or AWH.

You can follow GeoDocs on twitter and on facebook.
-- read more and comment ...

Monday, February 8, 2010

Best Bang for The Buck!

One of the restaurant that I probably most frequently visited in the last 2 years is Tai's Asian Bistro. It's a small place on Lane Ave, close to OSU West Campus (click here for map). How frequent did I go? At least once a week, more likely twice a week and sometimes more. I think remember that there were times when I actually went everyday of the week, excluding weekends.

So, why? Is is that good? Cheap? Economical? Fit my taste? What are the reasons that so compelling for me to keep coming back?

Well, the blog title pretty much sums them up, but here are the details about why Tai's Asian Bistro is the best bang for the buck for me (some may not apply for you):

  • The food themselves are excellent. If you are a fan of spicy food, you need to try their "Hot Pepper Chicken". This used to be Mark Pi's best, but somehow Tai's makes it better. Read for some of my recommendations.
  • It's relatively cheap (between $6 to $8)
  • The portion is HUGE, I meant heaping amount of food. For most people, this can mean lunch and dinner (for under $10).
  • The food is also of good quality. The breaded chicken is breaded and fried fresh daily - not from frozen, the ingredients are fresh, egg rolls are fried on demand, etc.
  • The place is clean, including the kitchen. You can see the kitchen right there and watch your food being cooked.
  • The service is excellent. Food being cooked super-fast and if you are dining in, it will delivered to your seat. All the servers are helpful and friendly.
  • For carry-out, they give you this plastic tub that is awesome and reusable. So instead of styrofoam that barely holds your food and melted easily, they use good quality container.
  • If you are a student, free pop! They also serve beer if you want.
  • Did I mention they are generous with their portion?
Tai's has a small selection in their menu, but for each one, they do it extremely well. I have pretty much tried every single item in their menu and in the order of how much I like them here are the top ten items to order at Tai's Asian Bistro:
  1. Hot Pepper Chicken (very spicy)
  2. Beef with Wild Mushroom
  3. Singaporean Rice Noodle (a bit spicy)
  4. Cantonese Chow Fun
  5. Dan Dan Noodle
  6. Pad Thai
  7. Tai's Asian Chicken
  8. Double Pan Friend Noodle
  9. General Tso's Chicken
  10. Rice Noodle Salad

-- read more and comment ...

Friday, February 5, 2010

Implementing jQuery FullCalendar PlugIn with ASP.NET MVC

I have been using jQuery for most of my javascript work, so when I need to display a calendar related data in a "month view", I did a search for jQuery plug-ins that will work with my existing project (and extensible to be used for future projects as well). My current projects are built using ASP.NET MVC, so working with jQuery and its plug-ins just makes sense.

After a careful research, trial & error, I finally picked "FullCalendar" jQuery plug-in. You can download it here. It says in its website as:

"FullCalendar is a jQuery plugin that provides a full-sized, drag & drop calendar like the one below. It uses AJAX to fetch events on-the-fly for each month and is easily configured to use your own feed format (an extension is provided for Google Calendar). It is visually customizable and exposes hooks for user-triggered events (like clicking or dragging an event)"

So first thing first, let's just try to get the calendar to display. In one of my view, let's call it "MonthView.aspx", I have this javascript (jQuery):
 $(document).ready(function() {
   $('#calendar').fullCalendar();
}
Somewhere in the HTML:
 <div id='calendar'></div>
Set the controller code to display the view:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult MonthView() {
   return View();
}
At this point, we should be able to display the empty calendar by going to http://<site>/<controller_name>/MonthView.

So now, we are going to modify this empty calendar to display the data from somewhere (db/xml/etc) via AJAX. For this, I created a class called "CalendarEvent" which structure is mimicking the JSON object model described in here; such as this:
 public class CalendarEvent {
   public string id { get; set; }
   public string title { get; set; }
   public string date { get; set; }
   public string start { get; set; }
   public string end { get; set; }
   public string url { get; set; }
}
Then in the controller code:
 
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult CalendarData() {
   DateTime start = new DateTime(1970, 1, 1);
   DateTime end = new DateTime(1970, 1, 1);

   start = start.AddSeconds(double.Parse(Request["start"]));
   end = end.AddSeconds(double.Parse(Request["end"]));

   // call middle tier/orm/whatever to get data from source
   List list = SearchForEvents(start, end);
   
   return Json(list);
}

private List SearchForEvents(DateTime start, DateTime end) {
   // code to get data here

}
Now, we need to modify the jQuery to call the action in our controller to return data:
 
$(document).ready(function() {
   $('#calendar').fullCalendar({
      events: '<%= Url.Action("CalendarData", "MyController") %>'
   });
}
Voila!

-- read more and comment ...

Wednesday, January 20, 2010

My Blog Shows Up in Google But Not in Bing - Why?

When I started blogging, all my traffic pretty much were from direct, meaning someone knows the URL to my blog and type it in the location bar. Most likely those people are myself. As time goes by, I added more content, post links in twitter, stackoverflow.com, facebook, etc, my blog starts to pick up some traffic, both from referral (someone clicks a link to get to my blog) or from search engine (mostly Google). Now, my site traffic source is about 80% from search engine results. I use Google Analytics to track my traffic.

So, when Microsoft released Bing, I tried it and I like it. I was considering switching totally to Bing as my main search engine, but one thing that saddened me was that Bing did not produce results for my blog, even when I entered partial URL of my blog address, or the title of my blog, or any keywords in it. Basically I came to the conclusion that since Blogger or Blogspot is owned by Google, they must be crawling it automatically, maybe somewhat prioritize it in their ranking. But, Bing does not crawl Blogspot - which includes my blog.

So how can we fix this?




Turns out, there is a "Bing Webmaster Center" page (here), where you can submit your site to be crawl by Bing (assuming it has not been crawled). Voila! So I went in there, click "Sign in to use the tools" button in the middle of the page. Then it asked me to sign in with my Windows Live ID (this can be your hotmail account, LIVE account, MSN account). Once logged in, I clicked on the "Add a site" button, enter my blog address in the "Web address:" textbox and click "Submit".

Then the webmaster tool will present an option to authenticate your site. You can either place an XML file in your site or add a meta tag in the homepage of your site. I selected to use the second option (add metatag) since my blog is hosted in Blogger/Blogspot and I cannot add a file - but adding a metatag just means I have to modify the template of my blog. So I went to my blog layout administration screen, copied and pasted the metatag from Bing Webmaster page and saved the template.

Now back in the Bing Webmaster page, I clicked "Return to site list". In there I can see all the sites that I have registered with Bing to be crawled and I can check the status as well by clicking on the link for each site. Wait for several days for crawling and everything is all set! Pretty easy!

Bing
Bing Webmaster Center
Google Analytics
-- read more and comment ...

Tuesday, January 19, 2010

AWH Has Job Openings

Need a job? Well, AWH - where I am currently employed - has several openings for .NET developers. Why work for AWH? Well, I have been working there for 10+ years - that ought to tell you something, huh? It's an excellent place to work, with good and vibrant team members, excellent leadership, relatively flat organization, cutting edge technology, and more importantly, we're growing!

So, what are you waiting for? You can see our openings and submit your resume here.

-- read more and comment ...

Cheap Mini Photo Portable Studio

I had several hours of free time during the Christmas holiday and I grabbed my camera and experimented on how to make a small item shot that looks decent, with a small/no budget. This exercise is pretty useful in real life if you are someone who put a lot of stuff on Ebay or online listing (ie. craigslist, etc).

The goal is to be portable, cheap, and quick - but also produces nicely lit object. I want a fairly soft light to create a nice even light. So, I am thinking either bounced light or diffused light. I went with diffused light, since it is easier to control the power that way.

So here are items that I used, which most if not all you can find in your house or buy cheaply:

  • Parchment paper or wax paper. This is for the diffuser.
  • Open cardboard box for creating a boxed controlled environment, cut out to attach the parchment paper (see setup shot for an example).
  • A black or dark surface. Or glass/reflective surface will also work.
  • A piece of blank white paper/carton to be a reflector.

I happen to have a black dining table that is a fairly smooth surface - so I used that as my surface. I put my box above the object, with another cut-out hole on the top of the box to let the light go through.

I setup my flash about 2-3 ft above the box facing down into the box. Attach the parchment paper on the cut-out hole to diffuse the light. Place the paper reflector inside the box on the other side of where the flash is.

Setup up the max sync speed on your camera and adjust your aperture to get a complete darkness exposure - this is for making sure that your only source light is your flash, not the dining room lamp, the sun, etc etc. Now turn on your flash and adjust the power of your flash to get a good exposure. Tweak the distance between your flash and the box as necessary, or add another layer of parchment paper if needed.

Here is my setup shot:




Learn How To Light via Strobist
Overpowering The Ambient

-- read more and comment ...

Monday, January 4, 2010

Some funny stuff on the web ...

There are some things on the web that you just have to bookmark ... and share. Here are some that I found in 2009 that I think are funny (or outrageous) enough to bookmark and share:

http://thereifixedit.com
How to fix the problem in our lives with extra-ordinary creativity, extremely cheap, works, and at the end of the day you can say "There! I fixed it!"

http://www.notalwaysright.com
For the record: Customer is NOT Always Right!

http://www.dontevenreply.com
Have you ever posted something in Craigslist? .... And got replies from hell? Apparently a lot of people have.

http://www.peopleofwalmart.com
Walmart is really my favorite place ... this site is a testimony of it.

http://thedailywtf.com/
Yes, they update it daily, because WTF moments happen daily!

http://xkcd.com/
The MODERN Far Side!

http://awkwardfamilyphotos.com/
The URL says it all ... awkward indeed!

I will add some more as I remember them ... so far, that's all.

-- read more and comment ...

Monday, December 14, 2009

Poop Ethiquete at Work

This post is NOT originally mine - I got it from the internet like here and here.

We've all been there but don't like to admit it. We've all kicked back in our cubicles and suddenly felt something brewing down below. As much as we try to convince ourselves otherwise, the WORK POOP is inevitable.

For those who hate pooping at work, following is the Survival Guide for taking a dump at work.

CROP DUSTING
When farting, you walk briskly around the office so the smell is not in your area and everyone else gets a whiff but does not know where it came from. Be careful when you do this. Do not stop until the full fart has been expelled. Walk an extra 30 feet to make sure the smell has left your pants.

FLY BY
The act of scouting out a bathroom before pooping. Walk in and check for other poopers. If there are others in the bathroom, leave and come back again. Be careful not to become a FREQUENT FLYER. People may become suspicious if they catch you constantly going into the bathroom.

ESCAPEE
A fart that slips out while taking a leak at the urinal or forcing a poop in a stall. This is usually accompanied by a sudden wave of embarrassment. If you release an escapee, do not acknowledge it. Pretend it did not happen. If you are standing next to the farter in the urinal, pretend you did not hear it. No one likes an escapee. It is uncomfortable for all involved. Making a joke or laughing makes both parties feel uneasy.

JAILBREAK
When forcing a poop, several farts slip out at a machine gun pace. This is usually a side effect of diarrhea or a hangover. If this should happen, do not panic. Remain in the stall until everyone has left the bathroom to spare everyone the awkwardness of what just occurred.

COURTESY FLUSH
The act of flushing the toilet the instant the poop hits the water. This reduces the amount of air time the poop has to stink up the bathroom. This can help you avoid being caught doing the WALK OF SHAME.

WALK OF SHAME
Walking from the stall, to the sink, to the door after you have just stunk up the bathroom. This can be a very uncomfortable moment if someone walks in and busts you. As with farts, it is best to pretend that the smell does not exist. Can be avoided with the use of the COURTESY FLUSH.

OUT OF THE CLOSET POOPER
A colleague who poops at work and is damn proud of it. You will often see an Out Of The Closet Pooper enter the bathroom with a newspaper or magazine under their arm. Always look around the office for the Out Of The Closet Pooper before entering the bathroom.

THE POOPING FRIENDS NETWORK (P.F.N)
A group of co-workers who band together to ensure emergency pooping goes off without incident. This group can help you to monitor the where about of Out Of The Closet Poopers, and identify SAFE HAVENS.

SAFE HAVENS
A seldom used bathroom somewhere in the building where you can least expect visitors. Try floors that are predominantly of the opposite sex. This will reduce the odds of a pooper of your sex entering the bathroom.

TURD BURGLAR
Someone who does not realize that you are in the stall and tries to force the door open. This is one of the most shocking and vulnerable moments that can occur when taking a poop at work. If this occurs, remain in the stall until the Turd Burglar leaves. This way you will avoid all uncomfortable eye contact.

CAMO-COUGH
A phony cough that alerts all new entrants into the bathroom that you are in a stall. This can be used to cover-up a WATERMELON, or to alert potential Turd Burglars. Very effective when used in conjunction with an ASTAIRE.

ASTAIRE
A subtle toe-tap that is used to alert potential Turd Burglars that you are occupying a stall. This will remove all doubt that the stall is occupied. If you hear an Astaire, leave the bathroom immediately so the pooper can poop in peace.

WATERMELON
A poop that creates a loud splash when hitting the toilet water. This is also an embarrassing incident. If you feel a Watermelon coming on, create a diversion. See CAMO-COUGH.

HAVANA OMELET
A case of diarrhea that creates a series of loud splashes in the toilet water. Often accompanied by an Escapee. Try using a Camo-Cough with an ASTAIRE.

UNCLE TED
A bathroom user who seems to linger around forever. Could spend extended lengths of time in front of the mirror or sitting on the pot. An Uncle Ted makes it difficult to relax while on the crapper, as you should always wait to poop when the bathroom is empty. This benefits you as well as the other bathroom attendees.

-- read more and comment ...

Friday, December 4, 2009

Ooma Review

In August, I bought a phone gadget called Ooma. You can go to their website to get a full exposure of what it is etc here. For me, I am using it for money saving cost for international calls between my parents and my family in US. We talk pretty frequently and I have been paying around $20-ish per month for long-distance international calls on my land line. So in a year, I amp paying around $300 for international calls and that's just me. If my mom calls me from Indonesia, I don't pay anything but she pays her international charge from her phone carrier.

Prior to this, I was considering other options such as Skype, Vonage (or any other VoIP solutions), MSN/YM web-cam chats, etc - but nothing seems to work as good as Ooma as a whole experience.

So, if you are calling internationally a lot and want to save some money, you may want to consider Ooma. Read my experience with it and why is it my solution of choice after the jump.

REQUIREMENT
You will need a broadband connection for Ooma to be able to works as expected. The connection speed where I hooked up my Ooma with is pretty modest (768dl and 256ul), but so far it works pretty well as soon as the internet is not used up for heavy downloading.

COST
Ooma will cost you around $200 up-front. Unlike Vonage or other VoIP solutions, which are using subscription model, Ooma actually sells you a device AND you do NOT need to pay anything else. You have the option to add more advance options stuff if you want (and you do have to pay for them) - but I don't need them - so I only pay $200 for the device and that's it. So right of the bat, Ooma is a CHEAPER solution in the long run compared to Vonage ($15 / mo).

INTERNATIONAL CALLS
But Ooma's website says it will charge you if you are calling internationally? Yes, that is right. What I did:

  1. Registered my Ooma under my name
  2. Gave it a local number (so area code is the same as my home phone, my cell, my wife's cell, and my office number)
  3. Setup the device at my home (very easy) and test it for several hours (or days if you want)
  4. Send the device to my parents and tell them how to hook it up with their broadband internet router
  5. Plug a cordless phone to the device.
This setup enables me to call my parents anytime without incurring international calling cost. If I call them from my home phone, it is FREE. If I call from my cell phone, I only lose my cell-minutes. If I call from the office phone, it's FREE. And ... they also can call me anytime for FREE.

    PHONE / AUDIO EXPERIENCE
    My experience with Ooma as a far as audio quality has been great. Only a few times that my mom would call me and the sound quality were like broken up - those were caused by my dad/brother downloading MS updates/photos/music from the internet. Once they stopped the download and recalled, quality back to clear and no delay. It is basically as good as calling from a land-line to another land-line locally; clear, no delay, and full duplex quality.

    WEB-CAM/SKYPE?
    Webcam/Skype kind of requires you to sit in front of a computer. To hook it up to a regular phone, you have to buy an extra device to connect and then maintaining the software, etc. I am ok with all that, but I don't think my parents is quite a tech-savvy to able to handle all that. Ooma is a stand-alone hardware, plug and play. No software, no computer, just plug into the router, connect your own phone, and voila!

    All in all, after 4 months of usage - I am very satisfied with Ooma and recommend you to use it if you have the need for frequent long distance calling like me.
    -- read more and comment ...

    Thursday, September 10, 2009

    SQL Shortcut Tip

    In creating a SQL select statement to query some records from a table, it is pretty common to include WHERE clause - where you are limiting your selections by criterias specified.

    Here is an example:

    SELECT *
    FROM Products
    WHERE CategoryID = 5
    The SQL statement above will return all Product records that are in Category 5.

    Now, sometimes we want to use the condition only if it meets certain condition (like if the condition value is not empty or null).

    So you SQL statement may look like this (assuming this is wrapped in a stored procedure) - where you want the stored procedure to return all Products if Category is not filtered:
    ALTER PROCEDURE [dbo].[GetProducts] 
    @CategoryID int = NULL 
    AS
    
    IF (@CategoryID IS NULL)
    BEGIN   
    SELECT *   
    FROM dbo.Products
    END
    ELSE
    BEGIN   
    SELECT *   
    FROM dbo.Products   
    WHERE CategoryID = @CategoryID 
    END  
    Looks rather long isn't it? In LINQ this is a bit shorter, but still quite repetitive:
    var data;
    if (categoryId == 0)
       data = from row in db.Productselse
       data = from row in db.Products where CategoryID == categoryId 
    So how can we short-cut this into shorter statements that is elegant and still do the job well?
    Like this:
    ALTER PROCEDURE [dbo].[GetProducts]
      @CategoryID int = NULL
    AS   
    
    SELECT *   
    FROM dbo.Products   
    WHERE (@CategoryID is NULL OR @CategoryID = 0) OR CategoryID = @CategoryID 
    In LINQ:
    var data = from row in db.Products
               where (CategoryID == categoryId  || categoryId == 0)
    By including the possibility of null or zero (or any default condition) in the WHERE clause means that the condition will be computed as well to produce the results. Got it?
    -- read more and comment ...