Monday, December 16, 2013

Save time editing media properties with multiple select

This is a great trick that lets you spend less time managing your gallery and more time sharing it with others. You probably already know you can easily edit the property of an album or media object using the right pane. For example, here I’m tagging a photo with the word ‘Bird’:

meta1

Notice as I type I get a drop down showing tags that have been applied to other gallery objects. This makes it easy to reuse tags and prevent misspellings.

We can also tag media objects from the album thumbnail view by selecting the media object and applying the tag:

meta4

TIP: When selecting the thumbnail, be sure to click in the border area outside the image. If you click the image, the gallery takes you to a page showing the full size version of the media object.

Okay, updating one media object at a time is easy enough. But what if you want to apply a tag to a bunch of items at once? For example, what if you want to tag every photo in an album with the word ‘Bird’? The answer lies in the multi-select feature built into Gallery Server Pro. Select the desired items and then update the property. When you exit the field, your changes are applied to all selected items.

There are two ways to select thumbnails: drag-select and control-click. Drag-select means using the mouse to drag a rectangle around the desired thumbnails. Control-click involves holding the Ctrl key down and left-clicking thumbnails. When a tag has been applied to more than one item, it displays the quantity in parentheses. Here I’ve drag-selected the thumbnails and applied the ‘Bird’ tag to all 25 photos in the album:

meta5

Deleting tags from multiple items is just as easy. Select the thumbnails and then click the ‘X’ on the tag you want to delete.

This trick works for any of the properties in the right pane. For example, you can change the title of all items and then edit individual ones as desired. Or control-click your favorites and give them a five-star rating with a single click.

Note that some properties are not editable by default. For example, the width and height of images are read only. To make them editable, go to the Media Objects – Metadata page in the Site Admin area and select the Editable attribute for the desired properties.

Editing the properties of multiple items at once is a big time-saver. Enjoy your gallery!

Wednesday, December 11, 2013

Gallery Server Pro 3.1 API Documentation Released

Today we released the Gallery Server Pro API Documentation, freshly updated for 3.1. This is primarily a resource for developers who want to better understand  the namespaces and classes used in Gallery Server Pro.

This documentation can be accessed any time from the support page.

Monday, December 9, 2013

Gallery Server Pro Binary Pack Updated

Today we released an updated version of the Gallery Server Pro Binary Pack. It contains the latest versions of three open source utilities that give you advanced gallery functionality such as audio/video transcoding and video, PDF, TIF and EPS image generation. They are ImageMagick 6.8.7, FFmpeg 2.1.1, and GhostScript 9.10.

To update your gallery, download the Binary Pack, extract the contents from the zip file, and copy convert.exe and ffmpeg.exe over the existing files in the bin directory of your web application. GhostScript is updated by copying gs910w32.exe in the GhostScript directory to your web server and then running it. If you are using a web hosting company, you likely aren’t allowed to run executables on the server. In this case, the best you can do is create a support ticket to ask them to do it.

Tuesday, December 3, 2013

Beware the ultra cheap web host

There are a ton of hosting companies out there with a wide variety of prices and service levels. I work with many of them when installing Gallery Server Pro, and I thought I should share a few things I’ve learned.

1and1.com

You’ve seen the ads—they’re everywhere and they offer some of the lowest prices available. However, I spent several hours yesterday working with a customer and we were ultimately unsuccessful in getting GSP working. He has since moved to Arvixe where things are running smoothly. There were two main issues:

CREATE DATABASE permission denied in database 'master'. When you modify the connection string in web.config to point to an existing SQL Server database, Entity Framework Code First Migrations (triggered from GSP startup code) is supposed to detect the existing database and ensure its tables are updated to the current version. In our case, EF didn’t think the database existed and tried to create it. Since 1and1.com wisely prevents code from creating databases, we get the error.

EF should have detected the existing database, and I still don’t know whether it’s a bug in EF or some kind of configuration issue with 1and1.com. I also don’t know whether this is limited to a particular web server at 1and1.com or whether they all behave this way. I found a forum thread where the same issue is happening with a couple other web hosting companies.

Web.API calls failing. GSP uses AJAX callbacks to provide a responsive user experience, and these are done using the Web.API library. These calls were also failing at 1and1.com with a 404 File Not Found. For example, when I tried to sync, I saw this error:

sync404

This issue can sometimes be fixed by adding runAllManagedModulesForAllRequests="true" to the modules element in web.config, but at best that is a performance-sapping fix and at worst doesn’t even work. In our case, it didn’t work. Ultimately, this is caused by some kind of server configuration that I haven’t yet figured out. As with the create database error, I don’t know if the issue applies to all 1and1.com servers or just particular ones.

Finally, I found the 1and1.com control panel to be slow, counter-intuitive, and limited in terms of features. Because of these issues, I cannot recommend this company.

GoDaddy

This is another 800 pound gorilla in the web hosting space, and I know of several galleries successfully being hosted there, although I’m not sure any of them are running 3.0 or higher. I am a little concerned about them because the forum thread I previously mentioned called out GoDaddy as also causing the create database error, but I haven’t confirmed it for myself.

I have found their web tools to be difficult to use and limited, but they get the job done. I don’t have an official recommendation on whether or not to use GoDaddy, but it’s fair to say I like others better.

If you are successfully using GoDaddy with SQL Server and GSP 3.0 or higher, let us know so we at least know it’s an option.

Arvixe

Arvixe is a large and fast growing company that offers a range of plans starting as low as $5/month. In my experience, they offer an excellent balance of low prices and good service, so I do recommend them as an option. A few years ago, I teamed up with them to create a referral relationship, so take my advice with the proper skepticism and do your own research.

There are a few important things to note about Arvixe:

  • Their cheapest plan is best for small galleries. The PersonalClass ASP plan, at $5/month, is limited to 250 MB of shared RAM. Any time the app goes over this, it recycles. This is a frequent cause of synchronization failures in GSP, as the process of iterating through media files and building thumbnail and optimized images eats up a lot of memory very quickly. If you only have a few thousand items in your gallery and your images are fairly small (<5MB each), you are probably OK. Anything else and you should consider a BusinessClass plan or higher.
  • Must disable 32-bit application pool compatibility. By default Arvixe configures the application pool so that the option “Enable 32-Bit Applications” is set to true. When you install the GSP Binary Pack, FFmpeg is used to create web-optimized versions of video and audio files. However, the 32-bit setting causes the transcoding process to intermittently fail after a few seconds, creating corrupted files. The solution is to create a support ticket and ask that this option be disabled (set to false) for the website.
  • GhostScript support. GhostScript is one of the utilities included in the Binary Pack that provides support for EPS and PDF thumbnail generation, but it can only be installed by running the setup program on the server. Many web hosting companies won’t let you do this or refuse to do it for you when asked, but Arvixe will do it if you ask them. So, if you discover that you don’t get thumbnail images from your PDF or EPS files, even after you’ve installed the Binary Pack in your gallery, you probably don’t have GhostScript installed on the server. Create a support ticket and ask them to install the 32-bit and 64-bit versions of GhostScript. Yes, both. Even though the servers are all 64-bit, in some cases the 32-bit version is required. If they give you any pushback, search for GhostScript in their forums and point them to those discussions where it’s been done before.

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.

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!