Progress so far

I have started working on my own business in December 2008. Here’s the progress so far.

Idea

Idea is very simple:  web gallery with shopping cart for wedding photographers. For more details see my older post.

Pricing

Fotrel is a web application. My clients use it as a service. The whole software-as-a-service model has a great appeal to me. I can make money while I sleep. There is no pain of acquiring new clients.

I’ve set the price to $95 per month right from the beginning. I didn’t want my product to look cheap. On the other hand it is flat price. I don’t like the idea of charging commission for this kind of service. For some reason it feels like putting my hands in client’s pocket. After all, my expenses are exactly the same regardless of how many orders my client gets.

Then, after reading Jay Abraham’s book I got an idea: charge $95 per month only if photographer has sales in that month. This creates a safety net for my clients: if my product doesn’t bring profit to them at least they are not losing money on it. This should be especially appealing to small (one-person) studios. Also, when you have sales paying $95 feels like giving away part of the profit, not parting with your own money. So far people like this idea. It certainly makes it easier to say yes rather than no.

Clients

Not everyone pays me each month because of the pricing structure. So it is somewhat hard to tell if someone is my customer. A photographer could sign up for my service just to try it out and then never come back. I define my client as someone who has uploaded at least one wedding or portrait gallery in past 3 months.

At the moment I have 15 clients.

Profits

Profits chart

On average, I get $300 per month.

Expenses

You might be wondering why I have those red months with negative profit.

  • I hired web designer twice.
  • I sponsored few AIPP mentoring sessions.

Hosting is the biggest part of my expenses at the moment – about $150 per month. I’m using Amazon EC2 and Softsys Hosting.

Conclusion

Overall, I’m happy with my progress. For now, my focus is on preventing those red bars from appearing again.

P.S. You can take a look at my product here: Fotrel.

Detaching Far Manager from long-running process on Windows 7

Occasionally I launch a program from Far Manager that takes very long to complete. Normally you can use Ctrl+Alt+Tab to “detach” Far from a running process. Technical note #27 explains this well:

If a long-running process (for example, archiving) was run in a FAR console, and for some reasons this very instance of FAR is needed (an editor in the background) or it is undesirable to run a new instance ofFAR, pressing this key combination will create a new console for FAR where it will continue running as if the process has already ended, and the process will continue working in the old console.

Unfortunately this doesn’t work on Windows 7. Instead of detaching you get Windows task switcher:

Windows 7 task switcher

Interestingly the switcher is in “sticky” mode. It doesn’t disappear when you release keys. You can use arrow keys to select a window.

The fix is easy:

  1. Start regedit, open HKCU\Software\far2\System key.
  2. Create ConsoleDetachKey string value, set it to some keyboard shortcut. I use CtrlAltX.
  3. Restart Far Manager

To test it try executing this command from Far:

ping google.com -t

Press Ctrl+Alt+X to get back to Far without stopping pings.

Converting .Net DateTime to JavaScript Date

JavaScript Date constructor accepts number of milliseconds since Unix epoch (1 January 1970 00:00:00 UTC). Here’s C# extension method that converts .Net DateTime object to JavaScript date:

public static class DateTimeJavaScript
{
   private static readonly long DatetimeMinTimeTicks =
      (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).Ticks;

   public static long ToJavaScriptMilliseconds(this DateTime dt)
   {
      return (long)((dt.ToUniversalTime().Ticks - DatetimeMinTimeTicks) / 10000);
   }
}

Usage:

var dt = new Date(<%= DateTime.Today.ToJavaScriptMilliseconds() %>);
alert(dt);

Reference

Java System.currentTimeMillis() equivalent in C#

How to enable detailed error information for IIS and ASP.NET

By default, IIS and ASP.NET hide detailed error information to prevent revealing sensitive information about your web application:
IIS generic error

ASP.NET generic error

Sometimes you need to see error details (think shared hosting). Add these entries to your web.config file to disable generic errors:

<configuration>
  <system.web>
    <customErrors mode="Off" />
  </system.web>

  <system.webServer>
    <httpErrors errorMode="Detailed" />
  </system.webServer>
</configuration>

Resources

How to safely remove USB flash drive with Far Manager

Do you have one of these:
USB flash drive
When you finish copying files from and to your USB flash drive you need to safely remove it, otherwise you risk losing data. Normally you do this by clicking icon with green arrow in system tray:

You can do the same with Far Manager. Open drive menu (Alt+F1 or Alt+F2), select your flash drive and press Shift+Del. You should get this confirmation:

Select “Remove” and you’re done. Note that the key combo (Shift+Del) is different from ejecting CD/DVD (Del).

jQuery BBQ plugin and Internet Explorer 7

Remember the problem with jQuery BBQ plugin in Internet Explorer 7? It’s not really a problem, just a minor glitch but let’s fix it anyway.

Steps to reproduce:

  1. Open BBQ demo in Internet Explorer
  2. Click Next button few times
  3. Open history menu

Observe:

No page titles, just URLs. Definitely it would be nice to see photo numbers there.

First, let’s see how Asual jQuery Address fix this problem:

_title = _d.title = value;
if (_juststart && _frame && _frame.contentWindow && 
    _frame.contentWindow.document) {
    _frame.contentWindow.document.title = value;
    _juststart = FALSE;
}

Aha! For IE7 you need to set title of the hidden iframe in addition to setting document title. So this is how you set page title:

document.title = "New title";
var iframe = $("iframe:hidden");
if (iframe.length > 0 && iframe[0].contentWindow && iframe[0].contentWindow.document)
  iframe[0].contentWindow.document.title = "New title";

Updated demo should work correctly in Internet Explorer 7.

Using jQuery BBQ plugin for photo gallery

Click here to view demo

Let’s take previous demo and use jQuery BBQ plugin instead. Our goal is Ajax photo gallery that supports browser’s back button. We will display numbers instead of actual photos for now.

$(function () 
{
  $(window).bind('hashchange', function(e) {
    var i = parseInt(e.fragment, 10);
    if (!isNaN(i))
      switchTo(i);
  });
  $(window).trigger('hashchange');
});

function switchTo(i)
{
  if ($("#number").text() == i)
    return;
  $("#number").text(i);
  document.title = i;
  if (i > 1)
    $("#lnk_prev").attr("href", "#" + (i - 1)).removeClass("disabled");
  else
    $("#lnk_prev").removeAttr("href").addClass("disabled");
  if (i < 20)
    $("#lnk_next").attr("href", "#" + (i + 1)).removeClass("disabled");
  else
    $("#lnk_next").removeAttr("href").addClass("disabled");
}

Instead of handling click event we modify href attribute of the links. BBQ will fire hashchange event whenever we "navigate" to a new page. No need to prevent default processing – simple!

One gotcha to watch out for is changing page title. If you take naive approach and simply use document.title (like I did) to set page title you won't see your titles in the history menu in Internet Explorer 7:

Compare with Internet Explorer 8:

Using Asual jQuery Address plugin for photo gallery

Click here to view demo

Let’s say you want to build photo gallery. Something like this:

You want to open next and previous photos without page reload, using Ajax. One problem with this is that browser’s back button stops working. Fortunately, it’s easy to fix with jQuery Address plugin from Asual. I tried using this plugin and it worked very well. I’ve put together a simple demo.

For simplicity, let’s display just a number instead of actual photo:

Every time you click right arrow the number increases; left arrow decrements the number by one. Simple! Back and forward buttons should work. Also, it would be nice to have different titles in the history:

The code:

$(function () 
{
  $.address.strict(false);
  $.address.externalChange(function(e)
  {
    switchTo(e.value);
  });

  $("#lnk_prev").click(function(e)
  {
    e.preventDefault();
    var i = $("#number").text() * 1 - 1;
    switchTo(i);
    $.address.value(i);
  });

  $("#lnk_next").click(function(e)
  {
    e.preventDefault();
    var i = $("#number").text() * 1 + 1;
    switchTo(i);
    $.address.value(i);
  });
});

function switchTo(i)
{
  $("#number").text(i);
  $.address.title(i);
}

The magic happens in click handler. $.address.value method changes page address and adds browser history point. You need to cancel navigation by calling e.preventDefault() — otherwise browser would follow the link and two history entries would get created.

Note that you need to disable plugin’s strict option, otherwise it would add slash symbol immediately after hash symbol, like this: #/11.

Enhancement to launching PowerShell scripts from Far Manager

The method of launching PowerShell scripts from Far Manager I have described previously has 2 important drawbacks:

  1. You can’t pass arguments to the script, and
  2. You can’t launch script in a separate window

Here’s how to fix this:

  1. Start regedit, open HKCR\Microsoft.PowerShellScript.1\shell\Open\command key
  2. Set it to
    "C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe" "-file" "%1" %*

Now you can press Shift+Enter to start PowerShell script in a new window.

As a bonus, you can now double-click .ps1 files in Windows Explorer to launch them. Why is this not a default behaviour? Most likely Microsoft decided to be extra cautious here.

Make sure you have Powershell version 2.0 or higher for this to work. How to check Powershell version.

How to redirect output of console program to a file in PowerShell

At some stage I decided I need backup plan for my virtual server. After some intensive web surfing I found brilliant command line tool – s3.exe. The next step was to schedule to run it periodically, every night, when everybody is sleeping.

Night

I decided to use PowerShell. It’s new, it’s powerful, it comes pre-installed with Windows Server 2008. PowerShell is very popular among system administrators.

Also, I got an idea to save output of backup application to log file so that I could see results later. And this is where things didn’t work as I expected. Here’s how to redirect console program output to a file:

s3.exe > log.txt

If you run this you will be surprised to find that log.txt is empty. As it turns out s3.exe, being a good Windows citizen, writes errors to STDERR stream. To redirect error output to the same file you need to add magic string 2>&1:

s3.exe 2>&1 > log.txt

Two new problems appear. First, errors appear as exceptions in log file:

s3.exe : s3.exe version 1.6 - check for updates at http://s3.codeplex.com
At PS Logging.ps1:4 char:43
+ s3.exe <<<<  2>&1 > log.txt
    + CategoryInfo          : NotSpecified: (s3.exe version ...s3.codeplex.com
   :String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

PowerShell helpfully wraps STDERR output into exceptions. This is nice but I don’t need it! All I want is to see how a program worked by examining log file. Do this to unwrap exceptions and show plain text:

s3.exe 2>&1 | foreach-object {$_.ToString()} | Out-File log.txt

And second, errors are out of order with normal output. They could appear before or after normal lines emitted by a program, making it hard to diagnose problem. To confirm that, I created a simple console program:

static void Main(string[] args)
{
   for (int i = 0; i < 10; i++)
   {
      Console.WriteLine("Hello");
   }
   Console.Error.WriteLine("Bah bah");
   for (int i = 0; i < 10; i++)
   {
      Console.WriteLine("2");
   }
}

If you run this program from PowerShell and redirect output to a file, error message (bah bah) could appear anywhere in the log file:

Hello
Bah bah
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
2
2
2
2
2
2
2
2
2
2

The solution I came up with is to let old reliable cmd.exe to do the work:

cmd /c s3.exe `>log.txt 2`>`&1

Note backsticks – I use them to prevent PowerShell from parsing redirect operators.

Links

PowerShell ABC’s – O is for Output
How to capture exe output to a PowerShell variable
Another approach to unwrap exceptions: use add-content cmdlet.