Tuesday, November 19, 2013

Gallery Server Pro 3.1.0 Released

Today we released 3.1.0, containing a number of significant new features along with a few bug fixes. The features are:

  • Improved quality of web-optimized videos
  • Images generated up to 3 times faster
  • Auto-rotation of images and videos
  • Synchronization up to 300% faster and uses less server memory
  • Easier Active Directory setup and integration
  • Improved performance for large numbers of user accounts
  • On-demand rotation of videos
  • Support for 3GP videos

This release is a free upgrade to 3.0 license holders. Upgrading is easy – just copy the files from the upgrade package over your existing web application. Installations and upgrades for other scenarios are in the Administrator’s Guide.

Video Improvements

When the Gallery Server Pro Binary Pack is installed, web-optimized versions of video files are created when you upload a file or the synchronization determines that an optimized video is needed. GSP 3.0.X created H.264 MP4 files that were highly compressed, resulting in small files that played quickly in all modern browsers.

However, I have come to believe they are *too* optimized and the loss in video quality was too much of a tradeoff. Version 3.1 changes this by creating videos with a higher quality, albeit with slightly higher file sizes as a result.

Specifically, what we changed in 3.1 was the FFmpeg arguments for the All video => .MP4 conversion process. You can manage these settings on the Video & Audio page in the site admin area.

In 3.0.X, the FFmpeg arguments were this:

-y -i "{SourceFilePath}" -vf "scale=min(iw*min(640/iw\,480/ih)\,iw):min(ih*min(640/iw\,480/ih)\,ih)" -b:v 384k -vcodec libx264 -flags +loop+mv4 -cmp 256 -partitions +parti4x4+parti8x8+partp4x4+partp8x8 -subq 6 -trellis 0 -refs 5 -bf 0 -coder 0 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -qmin 10 -qmax 51 -qdiff 4 -ac 1 -ar 16000 -r 13 -ab 32000 -movflags +faststart "{DestinationFilePath}"

In 3.1, it is now this:

-y -i "{SourceFilePath}" -vf "scale=min(iw*min(640/iw\,480/ih)\,iw):min(ih*min(640/iw\,480/ih)\,ih){AutoRotateFilter}" -vcodec libx264 -movflags +faststart -metadata:s:v:0 rotate=0 "{DestinationFilePath}"

Notice that a lot of the optimization settings have been removed, allowing FFmpeg to use default settings in many areas. There are also two other changes:

  • New {AutoRotateFilter} replacement parameter—This string is replaced with a rotation filter that automatically rotates the video to the correct orientation based on the orientation flag that may be present in the original video file. This feature requires a recent version of FFmpeg so if you haven’t updated the binary pack lately, be sure to do so.
  • Removal of orientation flag—The string “-metadata:s:v:0 rotate=0” tells FFmpeg not to include the orientation flag in the web-optimized video. Without this, the file would have the same rotation flag as the original file even though it is now rotated.

To generate a new set of web-optimized videos based on the new setting, run a synchronization with the option Rebuild optimized versions selected.

If you want to continue generating the highly compressed videos like it was done in 3.0.X, change the FFmpeg arguments for the All video => .MP4 setting back to its original value, except with the new settings included, like this:

-y -i "{SourceFilePath}" -vf "scale=min(iw*min(640/iw\,480/ih)\,iw):min(ih*min(640/iw\,480/ih)\,ih){AutoRotateFilter}" -b:v 384k -vcodec libx264 -flags +loop+mv4 -cmp 256 -partitions +parti4x4+parti8x8+partp4x4+partp8x8 -subq 6 -trellis 0 -refs 5 -bf 0 -coder 0 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -qmin 10 -qmax 51 -qdiff 4 -ac 1 -ar 16000 -r 13 -ab 32000 -movflags +faststart -metadata:s:v:0 rotate=0 "{DestinationFilePath}"

Up to 3X faster image generation

Versions of GSP prior to 3.1 used the GDI+ library in the .NET Framework to generate the thumbnail and optimized images for most image types. (TIFF being one exception where ImageMagick is used instead.) This has worked reliably but the algorithms are pretty slow.

Starting with this release, GSP now uses the WPF classes in the .NET Framework. They are much faster and provide the same or better quality as the old technique.

The WPF classes require a higher level of permissions than the old GDI+ classes. The code automatically detects the current trust level of the application. When the app is running in full trust, the WPF classes are used. Anything less and it falls back to the GDI+ classes. Thus those users who are running in full trust get the benefits while we still support those of you running in reduced trust scenarios.

Auto-rotation of images and video

A frequent request has been the detection of orientation in photos and videos and then auto-rotating them as need to make them right side up. It’s been discussed in the forums here and here and on the internet here. Initially I was hesitant to add this feature due to the terribly inconsistent way it’s been implemented in applications. My biggest worry was adding a feature that ended up rotating files that shouldn’t be rotated, essentially corrupting the media library. However, I got a good reminder of how useful this feature would be when I started uploading images and videos from my Samsung Galaxy S4 and discovered I had to tilt my head to see half the items.

As a result, we spent some time deliberating how best to implement this feature, and I think we’ve come up with a robust solution. For starters, when you upload or synchronize a photo or video that has orientation metadata embedded in it, the gallery extracts that information and stores it in the metadata table. You can see it in the new orientation meta item:

image_orientation

Second, when the gallery creates the thumbnail and optimized files, it uses the orientation flag to determine if a rotated version of the original should be created. The original file is not rotated or modified in any way during this process.

In the above example, the original photo is rotated 90 degrees counter clockwise, as seen here in Paint.NET:

rotated_image

But when we add it to the gallery, GSP detects the orientation flag in the metadata and uses it to create the thumbnail and optimized images rotated 90 degrees clockwise. Again, the original file is unchanged, which you can see if you use the Download/Share button to download the original image from the gallery.

But what happens if you rotate a photo or video from within GSP? This is a case where the original file *is* modified. For example, if we go to the rotate page for the above photo, leave the default selected ‘UP’ icon at the top of the image, and then click rotate, guess what happens?

rotate1

Since the original image is rotated 90 degrees CCW, GSP will rotate it 90 degrees CW so that it is oriented the same way as the thumbnail and optimized image. Also, to prevent issues in the future, the orientation flag in the original file is removed. If we left it in, we would experience potentially confusing behavior in other apps and in GSP, since we’d have a right side up photo whose metadata indicates it’s rotated to the left.

After the rotation, the image still appears right side up. But notice the orientation meta item is no longer present:

rotate2

If we open the original file in Paint.NET, we see it is rotated 90 degrees clockwise from its original orientation:

rotate3

We used a photo as our example here, but videos behave the same way. When the binary pack is installed, GSP uses FFmpeg to detect the orientation and then creates the thumbnail and optimized files with the required amount of rotation. Earlier in this post we looked at the new {AutoRotateFilter} parameter in the FFmpeg arguments setting. That is key to this feature. The original video is unmodified until you manually invoke a rotation, in which case the original is recreated with the desired rotation and any embedded orientation flag is removed.

What if you don’t want auto-rotation? For videos, all you need to do is remove the {AutoRotateFilter} parameter from the encoder settings. For images, the behavior is hard-coded and cannot be turned off. I considered adding a setting to disable the feature, but decided to wait to see if it’s really necessary. If you are affected by this and don’t want the auto-rotation feature, contact me and describe your situation.

Applying auto-rotate to your existing media objects

If you upgraded to 3.1 and want your existing media objects to be auto-rotated, follow a two-step process:

1. On the Metadata page, click the rebuild button next to the orientation meta item:

rotate4

2. Run a synchronize with the ‘Overwrite thumbnail images’ and ‘Overwrite optimized versions’ options selected.

You may want to do a test in one album before recursively regenerating items for all your objects. If any existing images or videos have actual orientations that conflict with the orientation meta item, you will end up with files created with the wrong rotation. If you discover this to be the case, I recommend cleaning up your original files by rotating them with a 3rd party app or using a tool to remove the orientation meta item.

Synchronization up to 300% faster and uses less server memory

The synchronization algorithm was modified to be faster and use less server memory. The primary change that caused it be faster is the switch to using WPF to create thumbnail and optimized images as we described earlier, but the other important change is that the synchronization no longer loads all media objects into memory at the beginning of a sync. Instead, media objects are loaded on an as-needed basis as each album is processed.

In a test with photos that averaged 18 MB each, the synchronization process finished 300% faster in 3.1.0 than in 3.0.3! That was the best case scenario, as the performance benefits are greatest for large images. In another test where the images averaged 2.9 MB, the speed increase was a still respectable 43%.

SQL CE users will notice a smaller performance benefit since much of a synchronization is spent on database access, but the speed increase should still be noticeable.

Some web hosting providers automatically recycle the application pool when certain memory limits are reached. In some cases the limits are—in my opinion—ridiculously low (ahem...Arvixe Personal Class has a 250 MB limit).

These changes to the sync process should help reduce app recycling due to hitting memory limits. However, be aware that the sync process is inherently a memory-intensive operation and if you have trouble running the gallery on a cheap plan, you may need to upgrade.

Removed feature: Support for preserving media object record when original file has been moved to another directory

To support the performance and memory improvements of the synchronization process, one feature that has existed since the beginning of time had to be removed. Prior to 3.1, if you used Windows Explorer or some other tool to move an original media file from one directory to another, the synchronization process was able to detect the move and update the original media object record to indicate its new album location. It was able to do this by creating a hash key of each file and storing it in the HashKey column of the MediaObject table.

However, this technique had two problems. One, it required loading the hash keys of all media objects in memory at the start of each sync. For large galleries, this came at a performance cost and sometimes triggered an app recycle even before the sync could get underway. Two, the hash algorithm was imperfect because it was based on the file name and its creation timestamp, two values that might be duplicated in other media files. This caused an issue during synchronization for some users.

Because of these two issues, this feature was removed in 3.1.0, along with the HashKey column in the MediaObject table. Now if you manually move a file to another directory, the synchronization process will delete the old record in the MediaObject table and then create a new one. Any titles, captions, tags, or other data associated with the original record is lost.

Easier Active Directory setup and integration

One of the pain points in GSP has been the hoops one must jump through to integrate the gallery’s membership with Active Directory. With 3.1.0, the process has been greatly simplified. Now you no longer have to trick the gallery into running under .NET 2.0 just so you can use IIS Manager’s .NET Users applet to set up the admin account. Instead, you choose one of your AD accounts and specify the username and password in the install.txt file in the App_Data directory:

ad

During installation, GSP detects the account info and automatically configures it as an administrator in the gallery.

You also no longer have to specify an AD account in web.config with update permission to Active Directory. Instead, the gallery need only be running under an identity with read permission to AD.

More information about AD integration is in the Administrator’s Guide.

Improved performance for large numbers of user accounts

The Manage Users page has been modified to perform better when there are large numbers of user accounts. This is especially helpful for Active Directory integrations where there are often tens of thousands of users. Notice a new textbox at the top that lets you search for a user:

mu

The textbox has auto-complete functionality so when you type a few characters, a list of matching accounts appears.

When the gallery contains more than 1,000 accounts, the Manage Users page no longer renders all those accounts to the screen. Now they are hidden and you use the find user textbox to find the account you want to manage:

mu2

Support for 3GP videos

This was an easy feature to add but one that should help anyone with 3GP videos. These are treated the same as MP4 videos by the gallery, meaning that they are rendered in an HTML5 video element in browsers that support it and with Flash in the rest of the browsers.

Tuesday, November 12, 2013

E-commerce integration: Part 6 - Adding e-commerce integration without the Enterprise Edition

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

E-commerce integration without the Enterprise Edition

The Enterprise Edition makes it easy to monetize your gallery by providing ready-to-use PayPal templates. But the GPL Free and GPL Pro editions fully support changes to the UI templates, so with a little bit of work you can have the same level of e-commerce integration provided in the templates.

Everything you need to add a shopping cart to your site, including several advanced scenarios, has been covered in this series of blog posts. Even if you don’t have the Enterprise Edition, the screenshots and sample code can be copied into your own templates.

So curl up with your tablet on the couch and dig into the blog posts, check out the Admin Guide, study up on jsRender syntax, and scan the PayPal documentation.

Monday, November 11, 2013

E-commerce integration: Part 5 - Selling access to media content

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

Selling access to media content

So far we’ve looked at setting up an e-commerce gallery to sell physical objects like tee shirts and coffee mugs. In this post we’ll look at configuring a gallery to sell access to media content like video or audio files.

There are a number of ways to do this. Which one is best depends on your business requirements and skills set. We’ll look at two approaches.

Technique #1: Duplicate the library

In this technique we create two sets of media objects—one that allows customers to browse the thumbnails and select items for purchase and another that lets paying customers view their purchases. We’ll keep things simple and create an example where we are selling access to a single video.

  1. Create two albums in the gallery. In each one upload the video you want to sell access to.

    sc224

  2. Open the album named Video and make it private by clicking the lock icon in the top left. This hides the album from anonymous users. Only customers who purchased access to the video will be able to access it—more on that later.

  3. Go to the UI Templates page, create a copy of the default media object template, and name it Video Store. Assign it to the album named Video Store and copy the following HTML into the HTML tab:

    <div class="gsp_mvMediaView"><div class='gsp_mvMediaHeader'>
     <div class='gsp_mvMediaHeaderRow'>
      {{if Settings.ShowMediaObjectNavigation}}
       <div class='gsp_mvMediaHeaderCell gsp_mvPrevCell' style='width:100%;'>
        <a href='{{: ~prevUrl() }}'><img src='{{:App.SkinPath}}/images/arrow-left-l.png' class='gsp_mvPrevBtn' alt='{{:Resource.MoPrev}}' title='{{:Resource.MoPrev}}' /></a>
       </div>
      {{/if}}
      {{if Settings.ShowMediaObjectNavigation}}
       <div class='gsp_mvMediaHeaderCell gsp_mvNextCell'>
        <a href='{{: ~nextUrl() }}'><img src='{{:App.SkinPath}}/images/arrow-right-l.png' class='gsp_mvNextBtn' alt='{{:Resource.MoNext}}' title='{{:Resource.MoNext}}' /></a>
       </div>
      {{/if}}
     </div>
    </div>
    
    <div class='gsp_moContainer'>
     {{:MediaItem.Views[0].HtmlOutput}}</div>
     {{if Settings.ShowMediaObjectTitle}}
      <div class='gsp_mediaObjectTitle'>{{:MediaItem.Title}}</div>
     {{/if}}
    </div>
    </div>

    sc234

  4. Update the right pane and header templates to include the add to cart button and view cart button as described in this post. Make sure the templates are applied only to the Video Store album. When you are done the Video Store album will have full e-commerce functionality. Users won’t be able to watch the videos, but they will be able to add them to their shopping cart and complete the checkout process.

    sc244

  5. When a user completes a purchase, PayPal sends you a notification with the details of the purchase. Your job is to give the customer access to the second copy of the video hidden in the album named Video. Create a role that has view permission to this album and configure a user to have access to it. Then give your customer the username and password for this account. When they log in they can now view the video:

    sc254

Technique #2: Authentication sniffing in template

This technique uses a single set of media objects and controls access to them by checking whether the current user is logged on. Anonymous users see the thumbnail image while logged on users can view and play video and audio files. This technique works well when customers pay a set fee for access to all the media content in the gallery.

  1. Add a ‘Buy Now’ button somewhere in the gallery or in a companion website. The idea is that you will give users access to the media content when they complete the checkout process.

  2. Go to the UI Templates page, make a copy of the media object template, name it Video Store, and look for this text:

    <div class='gsp_moContainer'>
    {{:MediaItem.Views[MediaItem.ViewIndex].HtmlOutput}}</div>
  3. Replace it with this:

    <div class='gsp_moContainer'>
    {{if User.IsAuthenticated}}
     {{:MediaItem.Views[MediaItem.ViewIndex].HtmlOutput}}
    {{else}}
     {{:MediaItem.Views[0].HtmlOutput}}
    {{/if}}
    </div>
  4. When a customer purchases access to the gallery, provide them a logon account with view permission to the gallery. They will be able to view the media objects but anonymous users can see only the thumbnail image.

TIPS
  • To prevent users from downloading media files, be sure to disable the download/share button on the Gallery Control Settings page.

  • Remember that Gallery Server Pro does not offer full Digital Rights Management (DRM) protection. If users have the ability to view a video or audio file, they can also download it. Disabling the download button is only a minor deterrence to a motivated individual.

  • The first technique favors ease of implementation over security. For a more secure solution, upload screenshots of the video files in the Video Store album rather than the actual videos.

  • If the thumbnail images are too small, change their size on the Media Objects – General page. Be sure to synchronize the gallery with the ‘Overwrite thumbnails’ option selected to force the creation of new thumbnail images.

Friday, November 8, 2013

E-commerce integration: Part 4 -Using multiple ‘Add to cart’ buttons on a page

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

Working with multiple ‘Add to cart’ buttons on a page

As we move toward more complicated scenarios, you may find you want multiple ‘add to cart’ buttons in your gallery. The Gallery Server Pro E-Commerce demo shows this scenario:

sc205

This requires setting up two buttons in PayPal, each with the desired prices and options. Then you copy the embed code for each button into the UI template, tweaking it to work around the forms issue we discussed earlier and assigning the title and description:

sc214

Like with the other solutions, we need some JavaScript to handle the form submission when the user clicks one of the add to cart buttons. However, we have the added complication of two sets of form elements—one for each button. If we just submit the form, PayPal will receive data for two buttons and won’t know which one is the real one. So our script removes unnecessary form elements before submitted it to PayPal:

var bindAddToCartEvent = function() {
 $('.btnAddToCart').click(function(e) {
  $('#viewcart input[type=hidden]').remove();
  var $el;
  if (this.id == 'addPrintToCart')
    $el = $('#framedPrintContainer');
  else
    $el = $('#printContainer');
    
  $el.css({
    "width": $el.css('width'),
    "height": $el.css('height')
    })
    .find('*').remove().end().addClass('gsp_wait_center');

  $(e.target).after("");

  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();

There is also a bit of CSS work to prevent the UI from jumping around when the form elements are removed. The end result is that when one of the add to cart buttons is clicked, only the relevant data is sent to PayPal.

We need a similar solution for handling the view cart button in the header. The HTML is unchanged from our earlier examples, but the JavaScript removes the form tags for both add to cart buttons in the click event handler:

$('#{{:Settings.ClientId}}_viewCart').click(function() {
 var f = $('form')[0];
  var $el = $('#framedPrintContainer, #printContainer');

  $el.css({
    "width": $el.css('width'),
    "height": $el.css('height')
    })
    .find('*').remove().end().addClass('gsp_wait_center');

 f.action = 'https://www.paypal.com/cgi-bin/webscr';
 f.submit();
 return false;
});

And there we have it. An e-commerce gallery with multiple ‘add to cart’ buttons on the page.

Thursday, November 7, 2013

E-commerce integration: Part 3 - Working with multiple prices and options

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

Working with multiple prices and options

Different prices for different items

So far we’ve assumed all your items are a single price. But what if you have different prices for each item? Ultimately, the details depend on your payment provider. Here I’ll focus on PayPal.

PayPal requires a set price for each button. If you have a bunch of tee shirts at $20 and coffee mugs at $10, you must create two buttons in PayPal. Then, in your UI template, you have two options:

  • Create one template for each button. Keep all your $20 items in one album and your $10 items in another album. Assign the UI template having the $20 button to the album containing the $20 items. Likewise for the $10 button.
  • Use one UI template for both buttons. Edit the template’s HTML and JavaScript so that each item gets the correct hosted_button_id value. Exactly how you’d do this depends on your situation, but one way is to use an {{if}} conditional to check one of the media object’s properties and use that to decide which hosted_button_id to output.

For advanced scenarios, like when you have dozens of different prices, PayPal offers advanced shopping cart scenarios with their API. Check out the PayPal Developer site for details.

Different prices and options for each item

Another scenario is when each item has multiple prices and options. For example, a photographer might want to offer different sizes and paper styles:

sc184

PayPal lets you define these options when you set up the button. For example, here is the above button as defined in PayPal:

sc194

Based on those settings, PayPal generates this code for your website:

<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="HPEYNB2JDUFRA"> 
    <table> 
    <tr><td><input type="hidden" name="on0" value="Print Size">Print Size</td></tr>
<tr><td>
<select name="os0">
     <option value="Small (8.1 in. x 8 in.)">Small (8.1 in. x 8 in.) $37.00 USD</option>
     <option value="Medium (16.2 in. x 16 in.)">Medium (16.2 in. x 16 in.) $52.00 USD</option>
     <option value="Large (24.3 in. x 24 in.)">Large (24.3 in. x 24 in.) $119.00 USD</option> 
</select> </td></tr> 
<tr><td><input type="hidden" name="on1" value="Frame Style">Frame Style</td></tr>
<tr><td>
<select name="os1">
     <option value="Matte Black">Matte Black </option>
     <option value="Butterscotch Distressed Maple">Butterscotch Distressed Maple </option>
     <option value="ECOCARE 2-inch Black">ECOCARE 2-inch Black </option>
     <option value="Arqadia Traditional Mahogany">Arqadia Traditional Mahogany </option> 
</select> </td></tr> 
</table> 
<input type="hidden" name="currency_code" value="USD"> 
<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>

Unfortunately, as we learned earlier, we can’t just copy and paste this code into our UI template since the form element would conflict with the form element already on the page. So we tweak it to get rid of the form element, add an item name, and give the button an ID that we can hook into with JavaScript.

<div id='framedPrintContainer' class='gsp_addleftmargin3'>
  <input type="hidden" name="cmd" value="_s-xclick">
  <input type="hidden" name="hosted_button_id" value="HPEYNB2JDUFRA">
  <input type='hidden' name='item_name' value='Framed Print - {{:MediaItem.Title}} (Item # {{:MediaItem.Id}})'>
  <table>
  <tr><td><input id='last_on0' type="hidden" name="on0" value="Print Size">Print Size</td></tr>
<tr><td>
<select name="os0">
     <option value="Small (8.1 in. x 8 in.)">Small (8.1 in. x 8 in.) $37.00 USD</option>
     <option value="Medium (16.2 in. x 16 in.)">Medium (16.2 in. x 16 in.) $52.00 USD</option>
     <option value="Large (24.3 in. x 24 in.)">Large (24.3 in. x 24 in.) $119.00 USD</option>
  </select> </td></tr>
  <tr><td><input type="hidden" name="on1" value="Frame Style">Frame Style</td></tr>
<tr><td>
<select name="os1">
     <option value="Matte Black">Matte Black </option>
     <option value="Butterscotch Distressed Maple">Butterscotch Distressed Maple </option>
     <option value="ECOCARE 2-inch Black">ECOCARE 2-inch Black </option>
     <option value="Arqadia Traditional Mahogany">Arqadia Traditional Mahogany </option>
  </select> </td></tr>
  </table>
  <input type="hidden" name="currency_code" value="USD">
  <p class='gsp_addleftmargin2'><input id='addFramedPrintToCart' class='btnAddToCart' 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!"></p>
  <p class='gsp_fss gsp_em' style='padding-left:1em;vertical-align:middle;'>Usually ships in 3-4 days</p>
  <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
  </div>

The JavaScript is similar to what we discussed in a previous post:

var bindAddToCartEvent = function() {
 $('#addFramedPrintToCart).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();

Now you have a shopping cart that can handle multiple prices and options for each item. When your customer picks the desired options and adds the item to the cart, they can confirm the options they selected are correct.

sc29

The details about the user’s selected options are passed along to you in the notification PayPal sends you when the purchase is complete.

In the next post we’ll discuss adding multiple ‘add to cart’ buttons to a single page.

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.