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!

Thursday, September 19, 2013

Gallery Server Pro 3.0.3 Released

Today we released 3.0.3, containing a number of updated third party components and a few bug fixes. 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 Quick Start Guide.

Updated components

Gallery Server Pro uses several third party components to provide an enhanced experience over what can be achieved with just the .NET Framework. These components were updated to the latest version:

  • Json.NET 5.0.6
  • Microsoft HTTP Client Libraries 2.2.13
  • Microsoft ASP.NET Web API Client Libraries 4.0.30506.0
  • Microsoft ASP.NET Web API Core Libraries 4.0.30506.0
  • Microsoft ASP.NET Web API Web Host 4.0.30506.0
  • Microsoft ASP.NET Web API 4.0.30506.0
  • Microsoft ASP.NET Universal Providers Core Libraries 1.2
  • MSBuildTasks 1.4.0.61
  • ErikEJ.SqlCeBulkCopy 2.1.6.3

Bug fixes

After releasing 3.0.2 about a week ago, we noticed a couple issues that we wanted to quickly fix. While we were at it, we threw in an easy bug fix.

Error "Cannot find skin path"

This bug was accidentally introduced in 3.0.2 and only affected those users. In galleries that were upgraded from 2.6, the following error can appear on the Site Settings - General page

Cannot find skin path
The gallery is configured to use a skin named Dark, but no skin by that name was found in the skin directory. Instead, these skin names were found: dark, light. Resolve this error by ensuring a directory named Dark exists at C:\path\to\skin_directory.

3.0.3 includes code that runs the first time you run the new version that checks for this scenario and automatically fixes it if necessary.

Upgrading from 2.6 may result in duplicate sets of tags

When upgrading from a 2.6 gallery where media objects had keywords imported from image metadata, those media objects may end up with two sets of tags settings. You’ll know you’re affected if the right pane shows the heading TAGS twice.

Version 3.0.3 now imports tags correctly from a 2.6 backup file. In addition, the first time you run it, special code runs that looks for duplicate tag records and automatically deletes them if found.

Slide show type on Gallery Control Settings page not inheriting from gallery setting

This was a minor bug that was easy to fix. The slide show type setting on the Gallery Control Settings page defaults to Inline, while the same setting on the Gallery Settings page defaults to FullScreen. The setting on the GCS page now inherits from the gallery setting.

Wednesday, September 11, 2013

Gallery Server Pro 3.0.2 Released

Today we released 3.0.2, containing 8 enhancements and 31 bug fixes. New features include a new default search option optimized for titles and captions, improvements to the naming of optimized images, and more.

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 Quick Start Guide.

New search option

The search window now allows you to search the title and caption fields:

3.0.2_blog1

Previously, the only way to search these fields was to select the Everything category, which has a pretty high performance cost and can take a long time for galleries with hundreds of thousands of objects.

Searching by title/caption is now also the default, as we expect it is the more common usage scenario.

Copy from a read-only gallery

Some users have requested the ability to copy media objects from a read-only gallery to another gallery. Previously, this was not supported. This ability helps enable the use case where you have a private and public gallery. For example, your private gallery might be a read-only view of your personal media collection. The public gallery is a curated set of media objects from your private gallery suitable for your audience. You periodically copy the desired items from your private gallery to your public gallery.

Note that you must be a gallery or site admin in the destination gallery to use this feature.

Enable password retrieval

In 3.0.0 and 3.0.1, the ability for users to request their password to be e-mailed to them was inadvertently turned off. This has now been restored as a default configuration setting.

This new setting applies only for new installations. To enable password retrieval for upgrades, open web.config and change the value of enablePasswordRetrieval to “true”.

Matching filenames

In versions earlier than 3.0.2, optimized images were created with the .jpeg extension, even if the original file’s extension ended in .jpg. If a user then deleted the original file using the Delete original files task, the optimized version was renamed to match the original, *except* for the extension, which retained the .jpeg value.

This is all fine and dandy, but it presented a problem for users synchronizing files between locations using a program like SyncBack. For example, let’s say you have 50 GB of photos you want to share with the world. Your web hosting company might not be happy with that much data on their servers, so you want to let Gallery Server Pro delete the original files from the web server once the web-optimized ones are created. So you set up SyncBack on your home PC to sync your media library with your hosted web server. You use GSP’s sync function to build media objects from the files and then delete the original files to clear up server space.

So far, so good, and you could have done this with earlier versions. But you want SyncBack to run periodically to keep your local library sync’d with your web-hosted library. When SyncBack runs, it won’t find any matches on the server for the local photos because the files on the server now all end in .jpeg and your originals end in .jpg. So it copies the entire 50 GB up again. Not cool.

3.0.2 now ensures that the web-optimized files are given the same name as the original file during the delete original files operation, thus allowing file syncing programs to continue matching up files based on their names.

BUG FIX: Gallery control settings occasionally reset or gallery assigned to another gallery

I’ve been trying to solve this one for a long time, and I finally figured it out. What happens is that the options on the Gallery Control Settings page are occasionally reset to their default values. Or, in other cases, a page that has been assigned to a particular gallery may get re-assigned to the default gallery.

The issue was caused by code that was not thread-safe, resulting in a code path that wiped out the gallery control settings when two threads tried to access the same chunk of code. It is now fixed and I hope I never see this issue again.

BUG FIX: Album titles not appearing in HTML title element

Prior to 3.0, the HTML title tag was dynamically assigned to the title of the current album, but in 3.0.0 and 3.0.1, default.aspx included a hard-coded <title> element set to ‘Media Gallery’. This prevented to the auto-title code from running, resulting in every page in your gallery having the same generic title. Most browsers use this title as the name on the tab, as seen here in Safari:

3.0.2_blog2

In 3.0.2 the hard-coded title element in default.aspx was removed, allowing the gallery to auto-generate this value to the name of the current album:

3.0.2_blog3

If you prefer the original behavior or would like to use your own title, open default.aspx and add a <title> element to the <head> tag with the desired text:

<title>Media Gallery</title>

List of all enhancements

View detailed report of enhancements

  • New default search option for title/caption
  • Allow copying from a read-only gallery to a read/write gallery
  • Allow backup/restore functionality after trial period expires
  • Show improved error message during 2.6 => 3.0 migration
  • Allow ability to cancel all pending video/audio conversions
  • Enable password retrieval by default
  • Filename of optimized image file should match original file after original is deleted
  • New media objects may not appear right away after a sync

List of all bug fixes

View detailed report of bug fixes

  • Gallery control settings occasionally reset or gallery assigned to another gallery
  • Tooltip message incorrect on site settings page
  • Apostrophe in tag/person not supported
  • Tooltip over thumbnail title is truncated after apostrophe
  • Cannot log in from create account page
  • Error creating gallery when current gallery has read-only media objects path
  • Concurrency error may occur when retrieving galleries
  • Cannot synchronize more than one object when metadata extraction is disabled
  • Album titles not appearing in HTML title element
  • Synchronization error when directory names differ by case
  • Cannot restore membership only
  • User may not be able to delete their user album or account
  • "Insufficient permission" message shown to user on create account page
  • New user may receive message 'Your account does not have permission to make changes to roles'
  • List of roles resets to filtered view after adding or deleting a role
  • Changing album title to empty string results in a silent HTTP 500 error
  • Error on site settings page when skin directory contains capital letters
  • Cannot assign user as owner of an album whose title contains a comma
  • Null reference exception when creating an album with no parent album selected
  • 'Also apply watermark to thumbnails' setting should be disabled when parent setting is unchecked
  • User may be able to see albums in breadcrumb menu she does not have access to
  • Error may occur while logging error thrown by ImageMagick (convert.exe)
  • After deleting a media object, view resets to first media object instead of sibling
  • Duration on video & audio page sometimes incorrectly calculated
  • Search results do not allow downloading original file
  • Sync fails when ImageMagick creates corrupt file
  • Move/copy toolbar buttons generate an error when browsing search results
  • Input focus reset when message box auto-disappears
  • Media files are deleted during gallery deletion
  • Tag/people auto-suggest not working for less-privileged users
  • Cancelling an "in progress" encoding leaves the encoding engine unable to process other media items