Wednesday, November 6, 2013

E-commerce integration: Part 2 - Dealing with the PayPal nested form problem

This is a series of blog posts covering the e-commerce features of Gallery Server Pro.
Part 1: Add ‘Add to cart’ and ‘View cart’ buttons to your gallery
Part 2: Dealing with the PayPal nested form problem
Part 3: Working with multiple prices and options
Part 4: Using multiple ‘Add to cart’ buttons on a page
Part 5: Selling access to media content
Part 6: Adding e-commerce integration without the Enterprise Edition

Dealing with the PayPal nested form problem

In Part 1 we used the built in e-commerce templates provided in the Enterprise Edition to enable shopping cart functionality in a gallery. In that post we didn’t look too closely at the structure of the PayPal code in the templates. If we did, we would have noticed that the HTML button code provided by PayPal differs in a few key respects from the button code used in the template. For example, here is the code PayPal provides for a simple button:
<form target="paypal" action="https://www.paypal.com/cgi-bin/webscr" method="post">
 <input type="hidden" name="cmd" value="_s-xclick">
 <input type="hidden" name="hosted_button_id" value="JP2UFSSRLBSM8">
 <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_cart_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
 <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form>

It’s essentially an HTML form with a few input tags and an image. The HTML specs say forms cannot be nested within another form, yet that’s exactly what would happen if you pasted this code into your UI template. That’s because every ASP.NET Web Forms app contains a form element that encompasses the entire page. It’s there to help manage view state and provide other functions.

Since we can’t have nested forms, we must manually tweak the PayPal code to get rid of its form tag. Then we add a bit of JavaScript to the UI template to manually submit the form when the ‘add to cart’ or ‘view cart’ buttons are clicked. For example, we can turn the above HTML into this that is suitable for a UI template:

<input type='hidden' name='cmd' value='_s-xclick'>
<input type='hidden' name='hosted_button_id' value='JP2UFSSRLBSM8'>
<input type='hidden' name='item_name' value='Photograph - {{:MediaItem.Title}} (Item # {{:MediaItem.Id}})'>
<input id='{{:Settings.ClientId}}_addToCart' type='image' src='https://www.paypalobjects.com/en_US/i/btn/btn_cart_LG.gif' border='0' name='addToCart' alt='PayPal - The safer, easier way to pay online!' style='padding:5px;'>
<span style='display:inline-block;vertical-align:top;margin-top:10px;'>$1.00</span>
<img alt='' border='0' src='https://www.paypalobjects.com/en_US/i/scr/pixel.gif' width='1' height='1'>

Notice we also add an input tag that defines the item name. PayPal uses this element as the item’s description in the checkout process. Then we add this JavaScript to the script tab of the UI template:

var bindAddToCartEvent = function() {
 $('#{{:Settings.ClientId}}_addToCart').click(function() {
  var f = $('form')[0];
  f.action = 'https://www.paypal.com/cgi-bin/webscr';
  f.submit();
  return false;
 });
};

$('#{{:Settings.MediaClientId}}').on('next.{{:Settings.ClientId}} previous.{{:Settings.ClientId}}', function() {
 bindAddToCartEvent();
});

bindAddToCartEvent();

The script registers a click event handler for the ‘add to cart’ button that updates the ASP.NET form element to point to the PayPal URL and then submits it. The page may have additional form elements such as viewstate that end up getting sent, but PayPal ignores any tags it doesn’t recognize, so it works just fine.

The script also adds an event handler that fires each time the user clicks the next or previous button. The handler re-wires the add to cart click handler. Without this step, the add to cart button would work only when the page first loads, and not after clicking next or previous.

So why is this important? If you didn’t know this gotcha with nested forms, you might be tempted to copy embed code from PayPal into your template. But now you know the issue and how to deal with it. This will help you understand the more complicated e-commerce integration examples we’ll dig into in subsequent posts.

Tuesday, November 5, 2013

E-commerce integration: Part 1 - Add ‘Add to cart’ and ‘View cart’ buttons to your gallery

Last month we looked at how easy it is to integrate social media into your gallery. Today we’ll start a series of blog posts that discuss converting a gallery into an e-commerce site where you can sell physical items or access to media content.

Part 1: Add ‘Add to cart’ and ‘View cart’ buttons to your gallery
Part 2: Dealing with the PayPal nested form problem
Part 3: Working with multiple prices and options
Part 4: Using multiple ‘Add to cart’ buttons on a page
Part 5: Selling access to media content
Part 6: Adding e-commerce integration without the Enterprise Edition

In Part 1, let’s look at how you can monetize the items in your gallery by adding e-commerce integration. Users can add items to a cart, view the cart, and check out through a secure payment provider such as PayPal. You get notified of the purchase (typically an email, text, or API callback), which you can then act on to complete the transaction.

Demo e-commerce site

To get a feel for what you can do with your gallery, we set up a demo e-commerce site for you to play with:

sc1

In this demo, which simulates a professional photographer selling prints, customers can choose from several options such as type of print (framed or unframed), the size, and more. When they click the add to cart button, they see a view of their shopping cart, where they can change quantities, remove items, or start the checkout process:

sc2

The financial transaction is handled by PayPal. You don’t have to worry about SSL certificates, credit card numbers or other personal customer data. When the transaction is complete, PayPal notifies you with the relevant details.

This demo was created without editing any of the C# source code in the gallery. Only the UI templates were modified and a few settings in the site admin area. All you need are some basic HTML and JavaScript skills.

If you are a .NET developer or are able to hire one, you can download the source code and implement advanced e-commerce functionality. The sky is the limit.

And if you want to use a payment provider other than PayPal, that is fully supported.

Demo: Start selling your items online

If you have the Enterprise Edition, adding a shopping cart is easy because it includes UI templates pre-configured for e-commerce. This demo will use those templates to quickly set up your gallery. If you are using the GPL Free or GPL Pro editions you can still add a shopping cart—more on that in a subsequent post.

  1. Be sure you have the Enterprise Edition by checking the Site Settings – General page as a site administrator.

    sc3

  2. Go to the UI Templates page. Select RightPane from the gallery item dropdown and ‘Default with PayPal Add To Cart Widget’ from the name dropdown. We want this template to take precedent over the default one, so click the Target Albums tab and select the root album. Then click Save.

    sc4

  3. Now select the Default template, uncheck the albums, and save.

    sc5

  4. Browse to one of your media objects and look in the right pane. There is a fully functioning add to cart button and a price showing $1.00.

    sc6

If you click the button, the current media object is added to your cart and you are redirected to PayPal where you can review your purchase and optionally check out.

sc7

But you’re not done. The default PayPal templates that ship with Gallery Server Pro point to a PayPal account owned by Tech Info Systems, the maker of Gallery Server Pro. If a customer completes the checkout process, you won’t get the money—we do. This is probably not the behavior you want, so read on.

Link the button to your PayPal account

To connect the gallery purchases to your own PayPal account, you must update the hosted button ID that is present in the UI template. You can see it on the HTML tab when looking at the right pane template:

sc8

To get your own button ID, first log in to your PayPal account and create a button. Here I’ve created a button named Photograph with a price of $19 and shipping cost of $5.

sc8

When you save the button, PayPal displays the embed code. When your button is simple, having just a single price as seen in the above screenshot, all you need from the embed code is the hosted button ID:

sc9

Copy the button ID over the existing one in the UI template. Notice the UI template also hard codes the price at $1, so update it to reflect the price you specified in the PayPal button.

sc10

At this point you have a fully functioning e-commerce site linked to your PayPal account. Let’s review what you did:

  • Turned on the PayPal template and disabled the default one for the right pane
  • Changed the hosted_button_id value to point to your PayPal button
  • Changed the price in the template to match the actual price

Pretty easy, right? Let’s look at adding some more functionality.

Adding a ‘View cart’ button

A standard piece of functionality in any e-commerce site is the view cart button. The Enterprise Edition includes a template for the header section that shows this button.

sc11

  1. Go to the UI Templates page. Select Header from the gallery item dropdown and ‘Default with PayPal View Cart Widget’ from the name dropdown. We want this template to take precedent over the default one, so click the Target Albums tab and select the root album. Then click Save.

    sc12

  2. Now select the Default template, uncheck the albums, and save.

    sc13

  3. Now a ‘View cart’ button appears in the top right of every page as shown in the screen shot earlier. But as with the add to cart widget, the view cart button is linked to the Tech Info Systems PayPal account. We need to update it to point to your PayPal account. Log in to PayPal and view the code for one of your buttons. At the bottom of the page is a ‘Create a View Cart button’ link. Click it.

    sc14

  4. A few options will appear for customizing the button. When finished, click Create Button.

    sc15

  5. The embed code appears. If you used the default options when creating the button, all you need is the input tag with name=”encrypted”. Copy it to the clipboard.

    sc16

  6. Return to the header UI template and replace the input tag with the one you copied to your clipboard. Save.

    sc17

Tip: If you customized the button in PayPal, you may need to edit the other HTML elements in the UI template to reflect your customized button.

That’s it! Your view cart button now points to your PayPal account.

Congratulations. Your gallery is now an e-commerce site with ‘add to cart’ and ‘view cart’ buttons with payment processing handled by PayPal. In the next few installments we’ll look at more advanced e-commerce scenarios.

Wednesday, October 23, 2013

Administrator’s Guide updated with new integration demos

Integrating Gallery Server Pro into an existing website can be done in a number of different ways, each having their advantages and disadvantages. Whatever your preference, we want to make the process easy and painless.

With that in mind, I rewrote and updated the integration section in the Admin Guide. It discusses the four main techniques:

  • Add a gallery to an existing ASP.NET website—compilation not required
  • Add a gallery to an existing ASP.NET website—compilation required
  • Add the gallery source code to an existing ASP.NET website
  • Add a gallery to an existing site using an iframe

There are also How-To sections with step by step instructions for three of these techniques. (The instructions for adding the source code to a site is similar to the compilation-required technique, so it didn’t need its own example.) The examples use the just-released Visual Studio 2013 but Visual Studio 2012 can also be used.

Download the Administrator’s Guide

Monday, October 21, 2013

Using Entity Framework Code First Migrations to auto-create and auto-update an application

Last week I came across a tough programming challenge that I couldn’t find solved anywhere on the internet. It took several hours to figure it out, so I thought I’d write it up here to help others.
Gallery Server Pro uses Entity Framework (EF) Code First Migrations to create the database and tables and seed it with initial configuration data. We had these requirements it had to handle:
  • Automatically create the database, objects and initial data the first time the application starts or any time it is missing.
  • If the database exists but doesn't yet have any gallery-related tables, create them and seed them with data.
  • If an earlier version of the gallery data schema is present, automatically upgrade it to the current version. It must support upgrades from any previous version of Gallery Server Pro going back to 3.0.0.

These requirements must be simultaneously met without requiring changes to web.config or any other user input.

The first attempt

The Entity Framework provides two classes that help with database creation and upgrades:
  • CreateDatabaseIfNotExists
  • MigrateDatabaseToLatestVersion
They both do what their names imply—create the database if it doesn’t exist and upgrade the schema to the latest version. Since I want both of these behaviors, I tried invoking both of them during application startup:
private static void InitializeDataStore()
{
  // Create the database and tables if it doesn't exist.
  System.Data.Entity.Database.SetInitializer(new Data.Migrations.GalleryDbInitializer());
  (new GalleryDb()).Database.Initialize(true);

  // Verify the database has the minimum default records and the latest data schema.
  System.Data.Entity.Database.SetInitializer(new System.Data.Entity.MigrateDatabaseToLatestVersion<GalleryDb, 
System.Data.Entity.Migrations.DbMigrationsConfiguration<GalleryDb>>());
  (new GalleryDb()).Database.Initialize(true);
}

public class GalleryDbInitializer : CreateDatabaseIfNotExists<GalleryDb>
{
  protected override void Seed(GalleryDb ctx)
  {
    SeedController.InsertSeedData(ctx);
  }
}

This seemed to work at first. When the database was missing, it was automatically created and seeded. When a migration was added and applied to an instance running an older version, the Up() method was called as expected. It worked so well this is the code that is included in Gallery Server Pro 3.0 – 3.0.3. These versions had code in the Up() methods that updated values in the tables to change default settings or fix bugs in the UI templates. But we didn’t alter the structure of the database.

That changed with 3.1.0. With this version, for the first time we had to alter the schema of the database by adding a column to one of the tables. I followed the normal steps one takes with EF Code First Migrations:
  1. Add the new property to the Code First entity class:
    [Table("Metadata", Schema = "gsp")]
    public class MetadataDto
    {
      [MaxLength(1000)]
      public virtual string RawValue
      {
        get;
        set;
      }
    
      // ...Other columns omitted for clarity
    }

  2. Use the Package Manager Console to execute ‘Add-Migration v3.1.0’.

  3. Verify the new migration class includes code to add the new column:
    namespace GalleryServerPro.Data.Migrations
    {
      using System.Data.Entity.Migrations;
        
      public partial class v310 : DbMigration
      {
        public override void Up()
        {
          AddColumn("gsp.Metadata", "RawValue", c => c.String(maxLength: 1000));
        }
            
        public override void Down()
        {
          DropColumn("gsp.Metadata", "RawValue");
        }
      }
    }

Then, rather than execute Update-Database in the Package Manager Console, I refreshed the web page in the browser. If the MigrateDatabaseToLatestVersion initializer was working correctly, it would notice the database needed upgrading and automatically add the new column. Instead, I received this message:

The model backing the 'GalleryDb' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269)
EF1

The error was occurring just after specifying the CreateDatabaseIfNotExists initializer and before we even had a change to attach the MigrateDatabaseToLatestVersion initializer.

The final solution

At this point I will spare you the hours of investigation and trial and error I went through and just skip to the final solution. In the end, the solution involves four key details:
  1. Stop using the CreateDatabaseIfNotExists initializer.

  2. Explicitly specify the database schema in the initial migration. Previously I let EF reverse engineer the data schema from the Code First entity classes, leaving the Up() method empty except for a few custom indexes. Now we need the initial migration to specify the schema that exists at that version (before any schema changes that are introduced in later migrations). Essentially that means adding a bunch of CreateTable() function calls. For example:
    public partial class v300 : DbMigration
    {
      public override void Up()
      {
        CreateTable(
          "gsp.Album",
          c => new
          {
            AlbumId = c.Int(nullable: false, identity: true),
            FKGalleryId = c.Int(nullable: false),
            FKAlbumParentId = c.Int(),
            DirectoryName = c.String(nullable: false, maxLength: 255),
            ThumbnailMediaObjectId = c.Int(nullable: false),
            SortByMetaName = c.Int(nullable: false),
            SortAscending = c.Boolean(nullable: false),
            Seq = c.Int(nullable: false),
            DateStart = c.DateTime(),
            DateEnd = c.DateTime(),
            DateAdded = c.DateTime(nullable: false),
            CreatedBy = c.String(nullable: false, maxLength: 256),
            LastModifiedBy = c.String(nullable: false, maxLength: 256),
            DateLastModified = c.DateTime(nullable: false),
            OwnedBy = c.String(nullable: false, maxLength: 256),
            OwnerRoleName = c.String(nullable: false, maxLength: 256),
            IsPrivate = c.Boolean(nullable: false),
          })
            .PrimaryKey(t => t.AlbumId)
            .ForeignKey("gsp.Gallery", t => t.FKGalleryId, cascadeDelete: true)
            .ForeignKey("gsp.Album", t => t.FKAlbumParentId)
            .Index(t => t.FKGalleryId)
            .Index(t => t.FKAlbumParentId);
    
        // ... Rest of tables omitted for clarity
      }
    }

  3. Replace the Initialize call with a call to DbMigrator.Update(). The new InitializeDataStore function looks like this:
    private static void InitializeDataStore()
    {
      System.Data.Entity.Database.SetInitializer(new System.Data.Entity.MigrateDatabaseToLatestVersion<GalleryDb, GalleryDbMigrationConfiguration>());
    
      var configuration = new GalleryDbMigrationConfiguration();
      var migrator = new System.Data.Entity.Migrations.DbMigrator(configuration);
      if (migrator.GetPendingMigrations().Any())
      {
        migrator.Update();
      }
    }

    public sealed class GalleryDbMigrationConfiguration : DbMigrationsConfiguration
    {
      protected override void Seed(GalleryDb ctx)
      {
        MigrateController.ApplyDbUpdates();
      }
    }

  4. Move record updates from the Up() methods of migrations and the initial data seeding to GalleryDbMigrationConfiguration.Seed(). The Seed() method is called after all migrations are complete, which is the first time you can successfully use the Code First entity classes to query and update the database. Any earlier—such as during each migration’s Up() method—and you’ll get an exception if you reference an entity class whose properties don’t match the table’s definition.
With this configuration, the database is
  • automatically created and seeded whenever it is missing
  • updated to the latest version when an older data schema is detected
  • backwards compatible with a database created with the original EF architecture
The next version of Gallery Server Pro will include these changes, so when it’s released you can download the source to see it in action. You can also read more about how Code First is used and other technical aspects in the Code Project article Gallery Server Pro - An ASP.NET Gallery for Sharing Photos, Video, Audio and Other Media. Hopefully this will save some of you the grief I went through. Cheers!

Microsoft patch available for IIS 7 and 7.5 bug

[Update Oct 31, 2013]: A user pointed out—and I was able to confirm—that SP1 for Windows Server 2008 R2 fixes this issue, so you don’t need the hotfix if you apply SP1. I don’t know whether service packs for other OS’s include the fix.

Gallery Server Pro uses Web.API for many kinds of AJAX callbacks when users interact with the gallery. For example, when you click the area around a thumbnail image, the gallery makes an AJAX call to the server to retrieve the meta items by issuing a GET request similar to this:

http://site.com/gs/api/mediaitems/617/meta

As you can see, the URL doesn’t contain a file extension. That is how Web.API is designed and it’s nice and clean. However, a bug in IIS 7 and IIS 7.5 causes extensionless URLs to fail in certain circumstances.

See if you are affected

There are a few ways to identify if you are affected by this bug. The easiest is to navigate to one of the albums in your browser and click the border area immediately around one of the thumbnail images. A wait icon appears in the right column, but it never disappears and the right pane is never updated. If you see this behavior and the web server is running IIS 7.0 or 7.5, you are likely being affected by the bug.

ExtensionlessUrlFix

Another easy way to identify the bug is to try synchronizing the gallery (choose Synchronize from the Actions menu). You’ll see the message “Error starting task – An error occurred on the server. Check the gallery’s event log for details.”

ExtensionlessUrlFix2

When you check the event log, you see this error:

Error: Object reference not set to an instance of an object.

Url: http://localhost/gsp303/api/task/startsync
Timestamp: 10/21/2013 4:13:39 PM
Exception Type: System.NullReferenceException
Message: Object reference not set to an instance of an object.
Source: GalleryServerPro.Web
Target Site: Boolean get_IsAuthenticated()
Stack Trace:
at GalleryServerPro.Web.Utils.get_IsAuthenticated()
at GalleryServerPro.Web.Api.TaskController.StartSync(SyncOptions syncOptions)
App Event ID: 13
Gallery ID: 1
HTTP User Agent:
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36

Apply the fix

A hotfix is available from Microsoft that fixes the issue. It installs quickly but be aware it may require a restart. Once the fix is applied, the Web.API calls work and your gallery is back in business.

Monday, October 14, 2013

Administrator’s Guide Updated for 3.0

Today we released an updated version of the Gallery Server Pro Administrator’s Guide. The screen shots have been updated and there are new sections covering UI Templates, Media Templates, and other new features. In addition, existing chapters were revised to provide more clarity and additional details.

AdminGuideWith many software products, the documentation is an afterthought, having minimal or outdated content, if there’s any at all. Gallery Server Pro provides best-of-breed documentation, with deep technical content, tons of screenshots, and How-To sections covering several common tasks.

Many of you won’t need to use the guide beyond the initial installation considering that the user interface is intuitive and easy to use, with plenty of in-application tooltips to guide you along the way. But the Administrators Guide is there for when you need it.

Thursday, October 3, 2013

Add a comment engine to your gallery in less than a minute

The new UI Templates feature in Gallery Server Pro 3 lets you modify the look and behavior of a gallery using only HTML and JavaScript skills. This opens up a wide variety of opportunities to customize the gallery to your requirements. One example is the e-commerce demo site, which was created by changing a few settings and playing with the UI templates.

A frequent request is to allow comments on media objects. We may someday add this as native functionality, but there is an excellent alternative today that may be even better depending on your scenario. Facebook provides a comment widget that provides full commenting ability, even if you don’t have a Facebook account. When enabled, every media object has its own comment thread. Here is what it looks like (live demo here):

facebook1

Enterprise Edition makes it easy

The Enterprise Edition of Gallery Server Pro includes two Facebook templates – one for the comment engine and one for ‘liking’ media objects. If you are using the GPL Free or GPL Pro editions you can still add commenting – skip to that section below.

  1. Be sure you have the Enterprise Edition by checking the Site Settings – General page as a site administrator:

    facebook2

  2. Go to the UI Templates page. Select MediaObject from the gallery item dropdown and ‘Default with Facebook Comments Widget’ from the name dropdown.

    facebook3

  3. This template is identical to the default template except it contains some extra HTML and JavaScript for the comment engine. You don’t have to do anything with it – it’s already good to go. All you need to do is enable this template and disable the default one. Do this by clicking the Target Albums tab and selecting the albums you want the comment engine on. Then click Save.

    facebook4

  4. Now select the Default template, uncheck the albums, and save.

    facebook5

That’s it! Browse to a media object and notice the Facebook comment widget is activated. If you have a persistent Facebook cookie in your browser, it will automatically recognize you:

facebook6

Notice the checkbox for posting to Facebook. When selected, the comment will appear on your wall. To prevent this, uncheck it.

If you are not logged in to Facebook, you are given an opportunity to log in to Facebook or several other websites:

facebook7

One caveat is that your gallery must be publicly accessible to Facebook. If Facebook can’t see the URL, you’ll see a message like this:

facebook8

Facebook must be able to see the URL so it can properly associate the comments to correct media objects.

Add Facebook comment engine to GPL Free and GPL Pro editions

The Facebook-enabled UI template is not included in the GPL Free and GPL Pro editions, but those versions fully support changes to the UI templates, so if you do a little digging you can add the HTML and JavaScript yourself. I suggest starting at the facebook developers page, where you can get the necessary snippets of code to add to the UI template.

There is one hint I will give that will be immensely helpful. After you add the embed code from Facebook to your template, you may notice the commenting works on the first media object but when you click next or previous, it fails to work on the remaining objects in the album. That’s because those objects are loaded via JavaScript and the Facebook parser only runs on page load. To force the Facebook API to recognize new media objects as you browse them, add this to the end of the JavaScript in your UI template:

$('#{{:Settings.MediaClientId}}').on('next.{{:Settings.ClientId}} previous.{{:Settings.ClientId}}', function() {
if (typeof (FB) != 'undefined') FB.XFBML.parse();
});

Summary

And there you have it. Adding a robust commenting engine to your gallery takes just a few clicks and no .NET or C# skills, nor do you have to recompile or deploy any code.

The Enterprise Edition also includes templates for ‘liking’ media objects as well as PayPal shopping cart functionality. I’ll dig into the PayPal templates in a future post.

Cheers!