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!