Magento on Rackspace Cloud Sites

After a little bit of work I was able to setup Magento 1.4 on Rackspace cloud sites. This new version runs quickly and correctly almost by default on Rackspace Cloud sites. I am not sure if this was the result of changes made to the Magento code or changes that Rackspace made to their infrastructure, but version 1.4 runs much better than previous versions on cloud sites. For the most part you simply need to follow the default instructions for setup and configuration of Magento on the Rackspace Cloud. These standard instructions are available both here on the Rackspace Cloud Sites wiki and here on the official Magento wiki.

There are two things that are non standard. The first, as mentioned on the Rackspace wiki, is that if you are using url rewrites you must set RewriteBase / in the .htaccess file. The second change is only needed if you are using ssl pages on your site. Rackspace handles requests through a gateway server that then forwards the request to the web server running your app. The key however, is that the request is only a secure request from the user to the gateway. The request from the Rackspace gateway to the Rackspace Cloud server running Magento is send unsecured. This is not the behavior that Magento expects. The result will be an infinite loop of redirects when you try to visit any https pages on your site. A minor change to the code will fix this problem.

Rackspace includes a value “HTTP_CLUSTER_HTTPS” with the request when the request is secure. Magento normally looks for the standard “HTTPS” value. We need only make a few changes to the file \app\code\core\Mage\Core\Model\Config.php to fix this issue. The correct way to modify Magento core files is to create a copy of the file and move it to the same place except in the \app\code\local\ directory. To make this change we will copy the Config.php file and move it to \app\code\local\Mage\Core\Model\Config.php. You will need to create these folders.

Change line 888 in the newly copied file from:

$secure = (!empty($_SERVER[‘HTTPS']) && ($_SERVER[‘HTTPS']!='off')) || $_SERVER['SERVER_PORT']=='443';

to:

$secure = (!empty($_SERVER['HTTP_CLUSTER_HTTPS']) && ($_SERVER['HTTP_CLUSTER_HTTPS']!='off')) || $_SERVER['SERVER_PORT']=='443';

Let me know if you have any problems or feedback.

New Facebook Connect in C#

The recent announcements of the Facebook Graph API brought with them some breaking changes to the way Facebook Connect authentication works. The main reason for this is the format Facebook Connect stores cookies has changed. In order to take advantage of these new features on ASP.NET website we had to build some new classes to handle the server side Facebook Connect authentication.

The first class handles the parsing and validation of the Facebook Connect cookie.

public class FacebookCookie {

        private FacebookCookie() {
        }

        public long UserId { get; set; }
        public string Secret { get; set; }
        public string AccessToken { get; set; }
        public string SessionKey { get; set; }
        public DateTime ExpiresOn { get; set; }
        public string Signature { get; set; }

        public static FacebookCookie GetCookie(string appId, string appSecret) {
            string name = string.Format("fbs_{0}", appId); // Cookie Name

            // Test if key exists
            if (HttpContext.Current == null
                || HttpContext.Current.Request == null
                || HttpContext.Current.Request.Cookies == null
                || !HttpContext.Current.Request.Cookies.AllKeys.Contains(name)) {
                return null;
            }
            var httpCookie = HttpContext.Current.Request.Cookies[name];
            return FacebookCookie.Parse(httpCookie.Value, appSecret);
        }

        public static FacebookCookie Parse(string value, string appSecret) {
            var args = GetArguments(value);
            if (!FacebookCookie.Validate(args, appSecret)) {
                throw new SecurityException("Invalid cookie.");
            }

            var cookie = new FacebookCookie();

            DateTime expires;
            DateTime.TryParse(args["expires"], out expires);
            cookie.ExpiresOn = expires;

            long userId;
            long.TryParse(args["uid"], out userId);
            cookie.UserId = userId;

            cookie.Secret = args["secret"];
            cookie.SessionKey = args["session_key"];
            cookie.Signature = args["sig"];
            cookie.AccessToken = args["access_token"];

            return cookie;
        }

        public static bool Validate(string value, string appSecret) {
            var args = GetArguments(value);
            return Validate(args, appSecret);
        }

        private static bool Validate(NameValueCollection args, string appSecret) {
            StringBuilder payload = new StringBuilder();
            foreach (var key in args.AllKeys) {
                if (key != "sig") {
                    payload.AppendFormat("{0}={1}", key, args[key]);
                }
            }
            payload.Append(appSecret);
            var md5 = System.Security.Cryptography.MD5CryptoServiceProvider.Create();
            var hash = md5.ComputeHash(Encoding.ASCII.GetBytes(payload.ToString()));
            StringBuilder signature = new StringBuilder();
            for (int i = 0; i < hash.Length; i++) {
                signature.Append(hash[i].ToString("X2"));
            }
            return args["sig"] == signature.ToString().ToLower();
        }

        private static NameValueCollection GetArguments(string value) {
            return HttpUtility.ParseQueryString(value.Replace("\"", string.Empty));
        }
    }

As you can see this class does quite a few things. The first is that it parses the cookie into name value pairs. Next, and most importantly, the class validates the cookie. This validation uses MD5 hashing to compare the contents of key appended to the app secret to the signature that comes in with the cookie. If these values match we know the key is valid.

The next class is a simple utility that is called directly from your ASP.NET page or ASP.NET MVC controllers. This class is the ConnectService.

 public class ConnectService : IConnectService {

        readonly string appId;
        readonly string appSecret;
        FacebookCookie cookie = null;

        public ConnectService() {
            appId = ConfigurationManager.AppSettings["FacebookAppId"];
            appSecret = ConfigurationManager.AppSettings["FacebookAppSecret"];
        }

        #region IFacebookConnectService Members

        public bool IsConnected() {
            if (cookie == null) {
                cookie = FacebookCookie.GetCookie(appId, appSecret);
            }
            return
                cookie != null &&
                cookie.UserId != 0 &&
                !string.IsNullOrEmpty(cookie.SessionKey);
        }

        public string SessionKey {
            get {
                if (cookie != null) {
                    return cookie.SessionKey;
                } else {
                    return null;
                }
            }
        }

        public long UserId {
            get {
                if (cookie != null) {
                    return cookie.UserId;
                } else {
                    return 0;
                }
            }
        }

        #endregion

    }

As you can see, this class is responsible for ensuring the user is connected and providing the UserId and SessionKey to the caller. Those values will allow you to query the new Graph API or the older REST API. You can use the Facebook Toolkit to query various data as well.

UPDATE: I have posted a new open source project on codeplex. This project should you get started using the new JavaScript SDK with a .Net application. The project is not yet ready for use in production, but I will be working on getting it ready asap. For now check out the code at: http://facebookgraphtoolkit.codeplex.com/

.Net APIs for 37Signals Products Open Sourced

Recently, I created a API wrapper for Basecamp. I built this to create a web application that interacts with basecamp (more on that shortly). Today, I decided that to open source the basecamp API and post it on Google Code. I have also started working on an API for Highrise and have plans for the other 37Signals products as well. So if you are a .Net developer and you are interested in integrating your product with 37Signals check this out. You can browse the source or download and build this project here. I haven’t quite finished up coding or testing yet, so be careful if you want to use this in a production environment.

Let me know if you have any questions or feedback.

Migrate from Mediacom Email to Gmail

I am writing this post out of sympathy. I don’t have Mediacom anything, I never will be a customer of that company again, and I have never used their email. However, I do live in an area where mediacom is the only cable company and as such I hear a lot of complaints about their email system. Also any quick search will show you how much people hate that company. So this post is here to help people solve at least part of the problem that comes along with being a mediacom customer. Please share this with anyone you know who has a @mchsi.com email address.

Before we begin, I would also suggest to you that after you migrate your email to gmail you look into alternative internet providers. The one I use here in Iowa City is Qwest DSL. Back a few year ago, medicom internet was deffinately fast than DSL (assuming you actually had an internet connection with mediacom), but Qwest has been making some serious upgrades to their network and in my experience my DSL is much faster than cable modem.

The first step is setting up your free Gmail account. This is very easy. To sign up for your account visit the Gmail Signup Page and fill in the required information. You will be asked for a secondary email address when you sign up. I would recommend using a work address, but you may also use your @mchsi.com email address.

After you have signed up for your account you will be directed to your new inbox. The Gmail inbox is very clean and easy to get used to. To begin the migration of your old email click on the settings link at the top of your inbox. (See picture below)

settings

Next click the “Accounts and Import” tab. Select “Always reply from default email address”. After that click the “Add POP3 email account” button.

accountsandimport

After you click the “Add POP3 email account” button a popup window will appear asking you for your email address. Input your @mchsi.com email address and click next.

emailaddress

Next enter your password in the dialog and then click “Add Account”.

password

That is everything you need to do in order to get Gmail set to download your mediacom email. You start seeing the emails that are being imported from your mediacom email appear in your gmail inbox within a few minutes. If you have a lot of email in your mediacom account this process may take some time.

That’s it. All your emails from your mediacom email will be in Gmail and any future email you receive to your @mchsi.com email address will arrive in Gmail. All email sent through your gmail account will use your new email address.

You can also setup gmail with other devices like smartphones and email software like outlook.

Setup Gmail on your phone: http://www.google.com/mobile/sync/
Setup Gmail with Outlook: http://mail.google.com/support/bin/topic.py?hl=en&topic=23333

Let me know if you have any problems or questions.

Google, you’re so good to us

It’s been a great day. I just got home after watching the Mumlers for the first time in the most hospitable dive bar in Missouri (it’s non smoking). I opened my email and learned that Google just slashed its additional online storage fees! This is great news! I was about to have to pay $50 for the third year in a row to support my Picasa addiction. Instead, for $20 I get 80 gbs of additional storage. gStorage

This is wonderful timing considering my increasing dependence on Google Docs and the growing draw of the new DROID. Or am I actually more lured by Verizon…nah, it’s probably my disdain for at&t. The point is that Android 2.0 may be tempting enough to finally cut the cord with my iPhone. Decisions, decisions…

In case you don’t have Google additional storage but you do have a Gmail account or a Picasa account, you really need to hop on the bandwagon. How can reject 20gb of additional storage for 5 bucks a year?! Time to bust out that digital camera and start sharing pictures as they’re meant to be shared — not as email attachments.

Google Apps Account Renaming Tool

Yesterday a client of Atlas Bay asked me if it was possible to rename some of their user accounts in Google Apps. After a bit of digging I found that it was possible, but only using the Google’s provisioning API. Unfortunately, the provisioning API is only available for Google Apps Premier Edition users. I decided that the easiest way to complete this renaming of the user accounts was to build a tool using Google’s .Net SDK. After a few minutes I developed a simple command application that will allow me to rename any user account in a Google Apps domain. Using the tools is simple, and I have made it available for anyone to use. Here is how you complete an account rename.

First, you need to turn on the provisioning API on your Google Apps account. To do this, click on the Users and groups tab in your domain administration site. Next, check the box called “Enable provisioning API.”ProvisionAPI

After the API is enabled you can use my tool to rename an account. If you are running Windows, Internet Explorer, and have .Net 3.5 installed simply click here to run the application. If you are on Firefox or another browser you can download and install the application here.

When the application opens you will be prompted to enter your domain, administrator email address, and password. As a note, this application is running on your computer and all data is being transmitted securely to Google so all your data should be safe.

firstscreen

After you have entered your administration credentials you will be prompted to enter the old username and new username. After you click enter, the request will be sent to rename the account. Be careful because there is no confirmation. However, if you make a mistake you can always just fix it with this tool.

After the account has been renamed you will receive a message that the operation completed successfully and asking you if you would like to rename another account using your same administrator credentials. If there is an error you will be told, however there is no detailed information about the cause of the error.

The only thing that is left is to turn off the provisioning API in your Google Apps administration site and you are done.

In case you would like to modify this program yourself here is the source code. You will need to download and reference the .Net library for the Google Data API here. You will have to reference Google.GData.Apps, Google.GData.Client, and Google.GData.Extensions in your project.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Google.GData.Apps;

namespace AtlasBay.GoogleAppsTools
{
 class Program
 {
 static void Main(string[] args)
 {
 Console.WriteLine("Domain:");
 string domain = Console.ReadLine();
 Console.WriteLine("Administrator Email:");
 string adminEmail = Console.ReadLine();
 Console.WriteLine("Administrator Password:");
 string adminPass = Console.ReadLine();
 RenameUser(domain, adminEmail, adminPass);
 }

 static void RenameUser(string domain, string adminEmail, string adminPassword)
 {
 Console.WriteLine("Old Username:");
 string oldUsername = Console.ReadLine();
 Console.WriteLine("New Username:");
 string newUsername = Console.ReadLine();
 try
 {
 AppsService service = new AppsService(domain, adminEmail, adminPassword);
 UserEntry entry = service.RetrieveUser(oldUsername);
 entry.Login.UserName = newUsername;
 service.UpdateUser(entry);
 Console.WriteLine("Rename Successful. Perform another rename? y/n");
 }
 catch
 {
 Console.WriteLine("An error occurred while performing the update. Try another? y/n");
 }
 var key = Console.ReadKey();
 if (key.Key == ConsoleKey.Y)
 {
 Console.WriteLine();
 RenameUser(domain, adminEmail, adminPassword);
 }
 }
 }
}

This application and code are provided as is. The application works on my machine, but you are using this at your own risk.

Radiohead strikes again

On March 2, 2008 I downloaded thirty six amazing tracks for $5 (nin’s ghosts i-iv: still available for $5 on the nin site). A few months earlier I downloaded Radiohead’s In Rainbows and Saul Williams’ The Inevitable Rise and Liberation of Niggy Tardust for free. Musicians were finally winning, I thought to myself. Unfortunately that was short lived. The online distribution model did not change the tide of the music world. So much for thinking that artists would no longer burn and package a ton of albums that would then be thrown into armies of trucks and shipped to the far corners of the world. So much for a future with one less genre of giant cardboard cutouts telling us what to buy.

How times had changed.

For years I was set on not buying media from iTunes. I had all the CDs I thought I wanted and didn’t think it was worth dishing out $1 a song if I didnt get the media in the mail. Over time I did buy a few songs and episodes that I had to have it at that moment.

In 2008, a few months after getting my Zune, I broke down. My roommates’ convinced me that it was pretty pointless to not have the Zune service since they also had Zunes. They couldn’t forward great songs without going to a third party service like Grooveshark, which of course, they were unwilling to do. We tried to share songs over the Zune’s wifi, but since I didn’t subscribe to the service, I could never get the songs they sent. Looking back on it today, I don’t know how I lived without it. I ‘rent’ over half of my music collection for a few bucks a month and also get 10 free downloads. Well worth it.

Today I listen to Porcupine Tree and Pink Floyd on my Zune and my Karma Police Station on Pandora on my iPhone and can’t remember the last time I bought an album. There are a couple albums I am going to buy on the Amazon Marketplace (Metallica’s Black Album and DJ Dangermouse’s Grey Album) as soon as I get around to it.

A few hours ago Radiohead released a new song online: Harry Patch (in memory of). Even though the single song they released is a little pricey, the proceeds are for a cause. Maybe if this song takes off more musicians will think twice before sending another dozen tracks to the plastic factory.

Update: Girl Talk released his latest album following the In Rainbows Method – pay what you want to download. http://bit.ly/13zmaN

RescueTime Version 2.0

About a month ago I realized that my RescueTime dashboard was no longer listing any of the time I spent on either my desktop or laptop. Tony Wright immediately suggested that I had changed my password online but had not updated it on the client app, thereby leaving the log of the work tracked sitting on my computer. Since the client app only has an online dashboard, I was sure it was something wrong with the tracking software, not the reporting software. The solution to my problem was as simple as logging in with my correct password and non of my time worked was lost.

Tony also told me that a couple months ago RescueTime released version 2, complete with tray icon notification and more advanced reporting tools. If RescueTime is as useful to you as it is for Nate and me, you should really download the newest version at RescueTime.com. If you haven’t used RescueTime, I really suggest downloading it and checking it out…now to work on a product review, to highlight the awesomeness of this app. Thanks Tony for creating such great app!

Open Letter to Napa Non-Profits

Unfortunately last year I didn’t hear about Craigslist’s Bootcamp for non-profits until it passed. Here is the letter I just sent out to all the Non-Profits and Non-Profit service companies I have in my rolodex (err, Gmail history — I’ve never owned nor intend to own a rolodex):

Hi Napa Non Profits,

I meant to send this announcement out earlier. Below is an email reminder to sign up for Craigslist’s Boot Camp — a series of workshops designed to help non profits bay area non profits reach their audiences. The workshops cover a range of topics from strategic planning to locating opportunities in this economy and social entrepreneurship  (you can see all 24 workshops here: link). I think this is a great opportunity for any Napa Non Profit. Please forward this to anyone else you think may be interested.
For more tips on how to make the most of your resources or improve your non profit, you can follow us at: www.atlasbay.com or our twitter account.

I meant to send this announcement out earlier. Below is an email reminder to sign up for Craigslist’s Boot Camp — a series of workshops designed to help bay area non profits reach their audiences. The workshops cover a range of topics from strategic planning to locating opportunities in this economy and social entrepreneurship  (you can see all 24 workshops here: link). I think this is a great opportunity for any Napa Non Profit. Please forward this to anyone else you think may be interested.

For more tips on how to make the most of your resources or improve your non profit, you can follow us at: www.atlasbay.com or our twitter account.

Alejandro Oyarzabal

Excerpt from Craiglsit Boot Camp email:

BOOT CAMP IS ONLY FOUR DAYS AWAY - REGISTER NOW!

Breaking news: Key team members from AllforGood.org (an open source application created by volunteers in the tech world that allows you to find and share volunteer activities) will be doing a special session:

Smarter Service in a Web 2.0 World: Introducing All for Good
Presenters: Adam Sah, Jonathan Greenblatt, and Susan Nesbitt

Learn about how your organization can use All for Good, a new open source platform that helps distribute your volunteer opportunities across the web and on social networks. All for Good engineers and co-founders will provide an overview of the platform and related Apps, walk through steps for you to get started today, and give a sneak peek at upcoming features.

So join us, Craig Newmark, Arianna Huffington, Ami Dar, Michael Krasny, and thousands of others this Saturday as we join forces to build stronger and healthier communities.

See you at Boot Camp!

-The Craigslist Foundation Boot Camp Team

For questions or help with registration, please email:
bootcamp@craigslistfoundation.org.

Google Apps Sync for Outlook

Today Google issued a heavy blow in the fight for small business, non-profit, and education email systems. Google announced that they will soon be releasing a tool that will allow Google Apps Premier and Education users to synchronize all their Outlook data instantly and quickly with their Google Apps account. This new tool will behave very similar to how Outlook interacts with Microsoft Exchange server and will feel no different for users.

As somebody who has setup numerous deployments of Exchange and Google Apps I can tell you that this is the one thing my customers have been waiting for. Many organizations have been using Exchange Server since its early days. All of their employees are using Outlook to manage their email, calendar, tasks, and contacts and for these organizations training all their employees to use Gmail would have simply been too difficult and too great of cost. However, this new tool allows organizations to seamlessly transition to Google Apps without the difficulty of removing Outlook.

Below is a demo of the new Outlook Sync tool.

Microsoft really needs to step up their game if they want to continue to succeed in this market. I am not saying Exchange Server is going to simply vanish, but fewer smaller organizations are going to be considering products like Small Business Server in the future and will instead be relying on cloud file storage and services like Google Apps and Salesforce.com to power their businesses.