Thursday, June 20, 2013

Deep dive into UI Templates

One of the best new features in 3.0  is UI Templates. The main user interface for browsing a gallery is built from jsRender templates you can edit using only HTML and JavaScript skills. In this post we’ll take a deep look at how it works and how you can harness its power.

Overview

Let’s start by identifying the five sections of the gallery that are built from UI templates.

uitmpl_sections_mo

uitmpl_sections1

These images show the five templates: header, left pane, right pane, album, and media object. The center pane shows either the album or media object template, depending on whether the user is looking at an album or an individual media object.

I should point out that the Actions menu and the album breadcrumb links are the only portion of the page that is not part of any template. Instead, its HTML is generated on the server and inserted into the page under the header. A future version of GSP may merge this portion into the header template so that the entire page is 100% template driven.

Each template consists of a chunk of HTML containing jsRender syntax and some JavaScript that tells the browser what to do with the HTML. Typically the script invokes the jsRender template engine and appends the generated HTML to the page. A robust set of gallery data is available on the client that gives you access to important information such as the album and media object data, user permissions, and gallery settings. We’ll get into the structure of this data later in this post.

You can view the UI template definitions on the UI Templates page in the site admin area. In these two images you can see the HTML and JavaScript values:

uitmpl_lp_html1

uitmpl_lp_js1

You can assign which albums this particular template applies to on the Target Albums tab. If multiple templates target the same album, the most specific one wins. For example, if the default template is assigned to ‘All albums’ and a second template is assigned to the Samples album, the second template will be used for the Samples album and any of its children because that template definition is ‘closer’ to the album.

uitmpl_lp_ta

The preview tab lets you see the result of any edits you make before saving the changes.

uitmpl_lp_preview

Anatomy of the left pane

Let’s take a close look at how one of the templates works. We’ll choose the left pane template first. The HTML is simple:

<div id='{{:Settings.ClientId}}_lptv'></div>
It defines a single, empty div tag and gives it a unique ID. The text between the double brackets is jsRender syntax that refers to the ClientId property of the Settings object. This particular property provides a string that is unique to the current instance of the Gallery user control on the page. When it is rendered on the page you end up with HTML similar to this:

<div id='gsp_g_lptv'></div>

Note: Defining a unique ID is not required for most galleries, but it is there for admins who want to include two instances of the gallery control on a page. For example, you might have a slideshow running in one part of a web page and a video playing in another.

Astute observers will notice that a single div tag doesn’t look anything like a complex treeview. So how does that div tag eventually become the treeview? Let’s look at the JavaScript that is part of the template:

// Render the left pane, but not for touchscreens UNLESS the left pane is the only visible pane
var isTouch = window.Gsp.isTouchScreen();
var renderLeftPane = !isTouch  || (isTouch && ($('.gsp_tb_s_CenterPane:visible, .gsp_tb_s_RightPane:visible').length == 0));

if (renderLeftPane ) {
 $('#{{:Settings.LeftPaneClientId}}').html( $.render [ '{{:Settings.LeftPaneTmplName}}' ]( window.{{:Settings.ClientId}}.gspData ));

 var options = {
  albumIdsToSelect: [{{:Album.Id}}],
  navigateUrl: '{{:App.CurrentPageUrl}}'
 };

 // Call the gspTreeView plug-in, which adds an album treeview
 $('#{{:Settings.ClientId}}_lptv').gspTreeView(window.{{:Settings.ClientId}}.gspAlbumTreeData, options);
}

Technically this text is not pure JavaScript. Do you see the jsRender syntax in there? That’s right, it is ALSO a jsRender template that will be run through the jsRender engine to produce pure JavaScript just before execution. This is an extraordinarily powerful feature and can be harnessed to produce a wide variety of UI possibilities. Imagine writing script that loops through the images in an album to calculate some value or invoke a callback to the server to request data about a specific album.

The first thing the script does is decide whether the left pane template should even be appended to the page. We decide not to show it on touchscreens for two reasons: (1) touchscreens typically have small screens and cannot afford the real estate required by the left pane (2) the splitter control that separates the left, center, and right panes does not work well on touchscreens (that’s something I need to work on).

The script refers to the function window.Gsp.isTouchScreen(). You’ll find this function in the minified file gallery.min.js. If you are editing the gallery you will probably prefer to have un-minified script files loaded into the browser. You can easily accomplish this by switching the debug setting to ‘true’ in web.config.

If the script decides the left pane is to be rendered, it executes this line:

$('#{{:Settings.LeftPaneClientId}}').html( $.render [ '{{:Settings.LeftPaneTmplName}}' ]( window.{{:Settings.ClientId}}.gspData ));
This will look familiar to anyone with jsRender experience or other types of template engines. In plain English, it says to take the template having the name LeftPaneTmplName and the data in the variable gspData, run in through the jsRender engine, and assign the resulting HTML to the HTML element named LeftPaneClientId. In other words, it takes the string <div id='{{:Settings.ClientId}}_lptv'></div>, converts it to <div id='gsp_g_lptv'></div>, and adds it to the page’s HTML DOM.

So far all the script has done is add a div tag to the page, but we still need to convert it into the album tree. That’s what the last section does:

var options = {
 albumIdsToSelect: [{{:Album.Id}}],
 navigateUrl: '{{:App.CurrentPageUrl}}'
};

// Call the gspTreeView plug-in, which adds an album treeview
$('#{{:Settings.ClientId}}_lptv').gspTreeView(window.{{:Settings.ClientId}}.gspAlbumTreeData, options);

The JavaScript file I mentioned above contains several jQuery plug-ins to help reduce the complexity of the templates and to maximize the amount of script that is cached by the browser (inline script is never cached). Here we use jQuery to grab a reference to our generated div tag and we invoke the gspTreeView plug-in on it, passing along the album treeview data and a few options. The tree data is a JSON object containing the album structure and is included in every page request. In turn, the gspTreeView plug-in is a wrapper around the third party jQuery tree control jsTree, the code for which is in the lib.min.js file (or lib.js if you are running with debug=true). There are several third party script libraries in that file.

The treeview plug-in builds an HTML tree from the data and appends it to the div tag, resulting in the tree view you see in the left pane. You can play with the HTML and JavaScript and then use the preview tab to see how those edits affect the output.

Adding a logo to the header

A common requirement is to add your logo to the top of the page. This was a little tricky to do in previous versions of GSP because you had to first figure out how the header was constructed, which code file to edit, and then make your change. Some changes required editing C# code and recompiling due to the use of server controls. Now all you have to do is edit the header UI template. Let’s say we want to replace the title with a logo, like this:

uitmpl_ex_logo1

Go to the UI Templates page and choose Header from the gallery item dropdown. We want to preserve the original header template in case we want to revert to it, so select Copy as new, enter a name, then click Save:

uitmpl_ex_logo2

Now go back to the default header template by selecting Default from the Name dropdown. Click the Target Albums tab and uncheck the albums. Save. This forces the new template to take over for all albums.

uitmpl_ex_logo3

Return to the new template and activate the HTML tab. Scroll down until you find the place where the title is rendered:

{{if Settings.Title}}
 <p class='gsp_bannertext'>
 {{if Settings.TitleUrl}}<a title='{{:Settings.TitleUrlTt}}' href='{{:Settings.TitleUrl}}'>{{:Settings.Title}}</a>{{else}}{{:Settings.Title}}{{/if}}</p>
{{/if}}

Replace this section with an img tag that points to your logo and save:

<img src='http://www.galleryserverpro.com/images/gsp_logo_346x80.png' />

That’s it! The logo now appears in the top left corner as shown in the image shown earlier.

The client data model

Each page contains a rich set of data that is included with the browser request. A global JavaScript variable named gspData exists that is scoped to the current gallery user control instance. In a default installation, you can find it at window.gsp_g.gspData, as seen in this image from Chrome:

uitmpl_api2

Let’s take a brief look at each top-level property:

ActiveGalleryItems – An array of GalleryItem instances that are currently selected. A gallery item can represent an album or media object (photo, video, audio, document, YouTube snippet, etc.)

ActiveMetaItems – An array of MetaItem instances describing the currently selected item(s).

Album – Information about the current album. It has two important properties worth explaining: GalleryItems and MediaItems. Both represent albums and media objects, but a GalleryItem instance contains only basic information about each item while a MediaItem instance contains all the metadata and other details about an item. Because a GalleryItem instance is lightweight, it is well suited for album thumbnail views where you only need basic information. Therefore, to optimize the data passed to the browser, the MediaItems property is null when viewing an album and the GalleryItems property is null when viewing a single media object.

App – Information about application-wide settings, such as the skin path and current URL.

MediaItem – Detailed information about the current media object. Has a value only when viewing a single media object.

Resource – Contains language resources.

Settings – Contains gallery-specific settings.

User – Information about the current user.

Here is a complete list of client objects and their properties (click the first image for a larger version):

uitmpl_api3a

uitmpl_api3b

Most properties are self-explanatory, but a few can use explanation:

Album.SortById – An integer that indicates which metadata field the album is sorted by. It maps to the enumeration values of MetadataItemName. The ID values are listed in the table on the Metadata page in the site admin area.

Album.VirtualType – An integer that indicates the type of the current album. It maps to the enumeration values of VirtualAlbumType (NotSpecified=0, NotVirtual=1, Root=2, Tag=3, People=4, Search=5)

MediaItem.Index - The one-based index of this media object among the others in the containing album.

MediaItem.Views and GalleryItem.Views – The Views property is an array of DisplayObject instances. Each DisplayObject represents a thumbnail, optimized, or original view of a media object or album.

MediaItem.ViewIndex and GalleryItem.ViewIndex – The zero-based index of the view currently being rendered in the browser. This value can be used to access the matching view in the Views property. The thumbnail is always be at index 0.

DisplayObject.HtmlOutput – The HTML for the display object. For thumbnails this is a <img> tag; the optimized/original HTML is generated from the media template defined for this MIME type on the Media Templates page in the site admin area.

DisplayObject.ScriptOutput – The JavaScript to execute to help render the display object. For example, it may contain the script necessary to initialize and run FlowPlayer for a Flash video. This property is populated from the script defined for this MIME type on the Media Templates page in the site admin area.

DisplayObject.Url – An URL that links directly to the media object file. For example, this value can be assigned to the src attribute of an img, video, or audio tag.

DisplayObject.ViewSize – This is an integer that maps to the enumeration values of DisplayObjectType (Unknown=0, Thumbnail=1, Optimized=2, Original=3, External=4).

DisplayObject.ViewType – An integer that maps to the enumeration values of MimeTypeCategory (NotSet=0, Other=1, Image=2, Video=3, Audio=4). For example, imagine a video MediaItem or GalleryItem. The Views property will have two or three DisplayObject instances (three if a web-optimized version has been created; otherwise two).The DisplayObject instance having ViewSize=1 represents the thumbnail image, so you can expect the ViewType to be 2 (image). But the remaining DisplayObject instances will have ViewType=3 (video).

MetaItem.GTypeId – Indicates the kind of gallery object the meta item describes. It is an integer that maps to the GalleryObjectType enumeration (None=0, All=1, MediaObject=2, Album=3, Image=4, Audio=5, Video=6, Generic=7, External=8, Unknown=9). Typically the client will never have the values of None (0), All (1), or Unknown (9).

The default UI templates use this data model, so they are an excellent place to look for examples of how to do things. It can be a little tricky, though, because much of the data access is done within the jQuery plug-ins.

UI Template examples

To get you up and running quickly, let’s look at a few examples. Paste these samples into a template to see it in action.

Show username or login link

<p>{{if User.IsAuthenticated}}
 Welcome, {{:User.UserName}}
{{else}}
 <a href='{{:App.CurrentPageUrl}}?g=login'>Log In</a>
{{/if}}
</p>

Determine if user has permission to delete media objects in the current album

<p>You {{if !Album.Permissions.DeleteMediaObject}}do not{{/if}} have permission to delete media objects in the album '{{:Album.Title}}'.</p>

Show the number of albums and media objects in the current album

<p>This album contains {{:Album.NumAlbums}} child albums and {{:Album.NumMediaItems}} media objects ({{:Album.NumGalleryItems}} total).</p>

Show a bulleted list of hyperlinked album and media object titles

<ul>
 {{for Album.GalleryItems}}
 <li>
   <a href='{{: ~getGalleryItemUrl(#data) }}'>{{:Title}} ({{getItemTypeDesc:ItemType}})</a>
 </li>
 {{/for}}
</ul>

This example shows a few interesting things:

  • for loop – The template loops through each gallery item of the album. Inside the loop the data context changes to that of the GalleryItem instance. For example, the {{:Title}} refers to the title of the gallery item being iterated on.
  • getGalleryItemUrl  – The call to getGalleryItemUrl is a jsRender helper function defined in gallery.js. Helper functions are useful when you need some JavaScript to figure out how to render something, like here to help calculate the URL to the album or media object. You can define your own helper function in the window.Gsp.Init function of gallery.js.
  • #data – A reference to the currently scoped data instance is passed to getGalleryItemUrl. In this example it is an instance of GalleryItem. You can refer to parent data items with the parent keyword. For example, using {{:#parent.parent.data.Album.Title}} from inside the GalleryItems loop gets the current album title by navigating up to the root data structure and then back down to the current Album title.
  • jsRender converter – The string {{getItemTypeDesc:ItemType}} says to run the ItemType property through a jsRender converter named getItemTypeDesc. A converter takes a property as input and massages it in some way. For example, you might want to do this to format a date/time value. The converter in this example converts an integer to a description such as Image, Album, etc. It is defined in gallery.js and, as with helper functions, you can define you own.

Note: This example requires that the GalleryItems property of the Album is populated. We discussed earlier that this property will have a value when viewing an album but is null when viewing an individual media object. To get this to work when a single media object is visible, change GalleryItems to MediaItems in the template.

Note: In 3.0.0 getGalleryItemUrl and getItemTypeDesc are defined in the gspThumbnails plug-in, so they are only available when that plug-in is invoked (as it is when viewing album thumbnails). In 3.0.1, I intend to refactor these to the generic page load function window.Gsp.Init in gallery.js, making them available to the entire page, regardless of which templates are visible.

 

Add image preview on thumbnail hover

When you hover over a thumbnail image, you see a larger version of the image appear in a popup that disappears automatically when you move the cursor away:

uitmpl_ex_img_pvw

This example requires adding to both the HTML and JavaScript album UI template. Open the album UI template and insert this HTML as the first line in the HTML tab:

<div id='pvw' style='display:none;'><img id='imgpvw' style='width:400px;'></div>

Then add this JavaScript to the end of the script in the JavaScript tab:

$('#pvw').dialog({
 appendTo: $('.gsp_floatcontainer'),
 autoOpen: false,
 position: { my: "left top", at: "left center" } 
});

$('.thmb[data-it=' + window.Gsp.Constants.ItemType_Image + '] .gsp_thmb_img').hover(
 function () {
  $('#imgpvw').prop('src', this.src.replace('dt=1', 'dt=2'));
  $('#pvw').dialog("option", "width", 425)
   .dialog( "option", "position", { my: "left+30 top+20", at: "right bottom", of: $(this)} )
   .dialog('open');
 }
);

$('.gsp_floatcontainer', $('#{{:Settings.ClientId}}')).mouseleave(function() {
  $('#pvw').dialog('close');
});

It works by opening a jQuery UI dialog window on the hover event of the thumbnail image.

 

Add virtual album links

Enhance the left pane by adding some links to popular tags or people in your gallery:

uitmpl_ex_tags

This is easily accomplished by adding the following to the end of the HTML text for the left pane UI template:

<p style='color:#B2D6A2;' class='gsp_addtopmargin5 gsp_addleftmargin4'>TAGS</p>
<div class='gsp_addleftmargin10'>
 <p><a href='{{:App.CurrentPageUrl}}?tag=Party'>Party</a></p> 
 <p><a href='{{:App.CurrentPageUrl}}?tag=Vacation'>Vacation</a></p>
 <p><a href='{{:App.CurrentPageUrl}}?tag=Family'>Family</a></p>
</div>

<p style='color:#B2D6A2;' class='gsp_addtopmargin5 gsp_addleftmargin4'>PEOPLE</p>
<div class='gsp_addleftmargin10'>
 <p><a href='{{:App.CurrentPageUrl}}?people=Roger%20Martin%2bMargaret'>Roger & Margaret</a></p>
 <p><a href='{{:App.CurrentPageUrl}}?people=Margaret'>Margaret</a></p>
 <p><a href='{{:App.CurrentPageUrl}}?people=Skyler'>Skyler</a></p>
</div>

When creating the hyperlink, notice spaces are encoded with %20 (e.g. Roger%20Martin). To create a link for multiple tags, separate them with %2b (an encoded + sign). The gallery does not support searching for both tags and people at the same time.

 

Show last 5 objects added in album

Show the five most recently added items in an album at the top of the center pane:

uitmpl_ex_last5

Before editing the template, go to the Metadata page and make sure the DateAdded property is visible for albums and media objects. If not, make it visible and click the rebuild action.

Open the album UI template and look for the following text in the HTML tab:

<div class='gsp_floatcontainer'>
Paste this text just before it:

<div id='divRecent' class='gsp_floatcontainer'></div>
Now paste the following at the end of the script on the JavaScript tab:

var getUrl = function(gItem) {
 var qs = { aid: gItem.IsAlbum ? gItem.Id : null, moid: gItem.IsAlbum ? null : gItem.Id };

 if (gItem.IsAlbum) {
 // Strip off the tag and people qs parms for albums if present, since we want them to link directly to the album.
 // We don't do this for media objects so they can be browsed within the context of their tag/people.
 qs.tag = null;
 qs.people = null;
 qs.search = null;
 }

  return Gsp.GetUrl(document.location.href, qs);
};

$.ajax({
  type: "GET",
  url: window.Gsp.AppRoot + '/api/albums/' + window.{{:Settings.ClientId}}.gspData.Album.Id + '/galleryitems/?sortByMetaNameId=111&sortAscending=false',
  dataType: 'json',
  success: function (galleryItems) {
   galleryItems.splice(5); // Keep the first 5 elements; get rid of the rest
   var html = "<p style='color:#B2D6A2;' class='gsp_addleftmargin2'>LAST 5 ITEMS</p><div class='gsp_addleftmargin5'";
   $.each(galleryItems, function(idx, galleryItem) {
    html += "<p><a href='" + getUrl(galleryItem) + "'>" + galleryItem.Title + "</a></p>";
   });
  html += "</div>";
  $('#divRecent').html(html);
  },
  error: function (response) {
    $.gspShowMsg("Action Aborted", response.responseText, { msgType: 'error', autoCloseDelay: 0 });
  }
});

What we’re doing here is first adding a div tag to receive the HTML we generate. In the JavaScript we make a call to the server to retrieve the items in this album sorted in descending order on the DateAdded field (sortByMetaNameId=111). You can see the ID values of other fields on the Metadata page.

When the server returns the data (which is an array of GalleryItem instances), we get rid of everything except the first 5 items, then we iterate through them and build up an HTML string. Finally we append the HTML to the div tag, resulting in it being shown on the screen.

NOTE: When you request a sorted list as shown above, the gallery tries to be smart and saves this sort preference in your profile, causing the album to be sorted on this field whenever you view it. This is probably not what you want. I intend to change this behavior in a future version. Meanwhile, if you don’t mind editing the source code you can stop this behavior by commenting out the call to the PersistUserSortPreference function in GalleryObjectController.GetGalleryItemsInAlbum().

What if you wanted to include the date added property in the HTML? For example, instead of the title ‘Road to nowhere’, you have ‘Road to nowhere (Added 2013-08-27)’. Unfortunately, the AJAX method we are using returns an array of GalleryItem instances, which is a lightweight data structure that doesn’t have any metadata in it. Refer to the screenshot earlier in this post to see its properties. What we really need is an array of MediaItem properties, which includes the metadata. And there is an API call we can make to get that data. It looks like this:

/api/albums/22/mediaitems/

The 22 is the album ID. But there’s a problem. Version 3.0.0 does not allow us to request a custom sort through this method, so we just get the items in the same order as they are shown in the album view. I intend to fix this in a future version.

AJAX API calls

The previous example made a call to the web server to retrieve data. There are a number of API calls available for reading and writing data in the gallery. Here’s a brief overview:

HTTP Method URL Description
ALBUMS
GET /api/albums/{AlbumId}/inflated Returns a GalleryData instance. Album.GalleryItems will be populated; Album.MediaItems will be null. Optional parms: top (int), skip (int) Can be used for paged results. Example: api/albums/22/inflated/?top=5&skip=5 returns the 6th – 10th gallery items in album 22.
GET /api/albums/{AlbumId}/galleryitems Returns an array of GalleryItem instances representing the albums and media objects in the album. Optional parms: sortByMetaNameId (int), sortAscending (bool) Can be used to return items in the album in a custom sort. Note that 3.0.0 will cause the user’s profile to be updated with this sort preference for this album. Example: /api/albums/22/galleryitems/?sortByMetaNameId=111&sortAscending=false returns the items in album 22 sorted in descending order on the DateAdded metadata property.
GET /api/albums/{AlbumId}/mediaitems Returns an array of MediaItem instances representing the albums and media objects in the album. Optional parms: none
GET /api/albums/{AlbumId}/meta Returns an array of MetaItem instances representing the metadata for the album. Optional parms: none
POST /api/albums Updates a limited set of properties for the album: DateStart, DateEnd, SortByMetaName, SortAscending, IsPrivate, Owner
POST /api/albums/{AlbumId}/sortalbum?sortbyMetaNameId={{MetaNameId}}&sortAscending={{true|false}} Resort the items in the album and persist to the database. No value is returned. Perform a GET to retrieve the sorted items.
POST /api/albums/{AlbumId}/getsortedalbum Resort the items in the album but DO NOT persist to the database. Requires an instance of AlbumAction to be POSTed. See example in gallery.js.
DELETE /api/albums/{AlbumId} Deletes the specified album, including the media files and directory.
MEDIA
GET api/mediaitems/{MediaObjectId}/inflated Returns a GalleryData instance. The MediaItem property contains data for the specified media item. The Album.MediaItems property contains data for the remaining items in the album. Album.GalleryItems is null.
GET api/mediaitems/{MediaObjectId}/meta Returns an array of MetaItem instances belonging to the specified media object.
POST /api/mediaitems/createfromfile Adds a media file to an album. Prior to calling this method, the file should exist in App_Data\_Temp. Requires an instance of AddMediaObjectSettings to be POSTed. See example usage in gs\pages\task\addobjects.ascx.
PUT /api/mediaitems/ Persists changes to the database about the MediaItem instance PUT to the method. Current implementation saves the title only and requires that the media item exist.
DELETE /api/mediaitems/{MediaObjectId} Permanently deletes the media object from the file system and data store.
META ITEMS
GET /api/meta/tags/?galleryId={GalleryId} Gets an array of Tag instances containing all tags used in the specified gallery. Optional parms: q (string) Specifies a search string to filter the tags by. Example: /api/meta/tags/?galleryId=1&q=bob returns all tags with the text bob in them.
GET /api/meta/people/?galleryId={GalleryId} Gets an array of Tag instances containing all people tagged in the specified gallery. Optional parms: q (string) Specifies a search string to filter the tags by. Example: /api/meta/tags/?galleryId=1&q=bob returns all tags with the text bob in them
POST /api/meta/rebuildmetaitem?metaNameId={MetaNameId}&galleryId={GalleryId} Rebuild the data entries for the specified meta property for all media items in the gallery.
PUT /api/meta/ Persists the POSTed MetaItem instance to the database.
SYNCHRONIZATION
GET /api/task/startsync/?albumId={AlbumId}&isRecursive={true|false}&rebuildThumbnails={true|false}&rebuildOptimized={true|false}&password={Password} Begins a synchronization. Requires enabling the remote sync option on the Admin page in the site admin area.
GET /api/task/statussync/?id={GalleryId} Gets the status of the synchronization. Requires an HTTP header variable named X-ServerTask-TaskId to be set to the sync task ID. See example on gs/pages/task/synchronize.ascx
GET /api/task/abortsync/?id={GalleryId} Cancels a synchronization. Requires an HTTP header variable named X-ServerTask-TaskId to be set to the sync task ID. See example on gs/pages/task/synchronize.ascx
POST /api/task/startsync/ Begins a synchronization. Requires an instance of SyncOptions to be POSTed and an HTTP header variable named X-ServerTask-TaskId to be assigned a value. The sync page uses this method to start a sync.
MISCELLANEOUS
GET /api/task/purgecache/ Purges the cache on the web server. The next HTTP request will retrieve data from the database.
POST /api/task/logoff/ Logs off the current user

Friday, June 14, 2013

Using Active Directory with GSP 3.0

Some users have reported trouble integrating 3.0 with their Active Directory architecture. Over the last couple of days I had time to fire up my AD virtual machines and dig into this. I have good news and bad news. The good news is that integration is still possible. The bad news is that there are extra steps to make it happen.

I updated the 3.0 Quick Start Guide to include step by step instructions for integrating AD with your gallery. This information will eventually be in the full Admin Guide. When it is complete, the quick start guide will be discontinued.

So what happened?

The additional difficulty in getting GSP 3.0 working with AD comes down to two issues outside of my control:

  • The new role provider is not compatible with Active Directory.
  • The .NET Users applet in IIS Manager does not work with .NET 4.0 or higher.

I came up with workarounds for both issues. It was a pain in the rear figuring out the steps and you’ll do a bit of grumbling implementing them, but in the end they work. To add insult to injury, when I tried to report the role provider incompatibility to Microsoft, I couldn’t find the right place to do it on MS Connect. I’ll have to look harder when I have some extra time – they need to know about it.

There is a silver lining. During my investigation I think I figured out a way to streamline the integration in the future. A lot of the extra work involves adding the first AD account to the System Administrator role so that you can log into the gallery’s admin functions. I can write a function that does this programmatically from code, so it may be possible to temporarily add some kind of trigger (e.g. app setting in web.config) that does this for you.

Also, I should be able to tweak the Manage Users page so that you can edit the role membership of users without requiring the application to have edit permission to AD. Look for these improvements in one of the next releases.

Friday, May 31, 2013

Gallery Server Pro 3.0 Released

Two years in the making, version 3.0 of Gallery Server Pro is finally here! This is a major release and includes a tremendous number of new features and updated styling. Among them:

  • UI rewritten using modern HTML5/CSS3
  • Resizable, dockable 3-pane layout
  • New template engine lets you easily change the UI using only HTML/CSS skills
  • Tagging support for media and albums
  • Editable properties of media and albums
  • Skinnable UI - ships with dark and light skins
  • Two slide show modes - full screen and inline
  • Looks great on any device - iOS, Android, Windows Phone, Mac, Linux, and Windows
  • Improved video encoding
  • Improved sorting

Download Gallery Server Pro 3.0

New UI using modern HTML5/CSS3

The default skin is a gorgeous light text on black background:

gsp_standalone_dark_700x488

The new design was inspired by user feedback where I learned that images are best viewed against an 18% neutral gray background. The background color I chose ended up a little darker than this to enhance the contrast against the text.

The icons are modern, monochromatic, and match the trend we see with other applications and operating systems.

Clicking a thumbnail takes you to a larger version:

ss_image_3pane_700x488

Properties about the image appear in the right pane. A user with edit rights can change any property the administrator has made editable. There are subject tags and people tags. Each tag is a clickable link that takes you to a virtual album containing those tagged items. Tags are automatically imported from the image file if present.

Running a slide show is one of the most common actions, so its icon is bigger than the rest to bring attention to it.

Skinnable UI - ships with dark and light skins

A light skin is also available for those of you wanting the more traditional dark text on light background:

gsp_standalone_light_700x469

Each skin consists of a set of images and CSS files. The main CSS file – gallery.css - can be edited in the admin area, letting you make quick and easy changes.

Resizable, dockable 3-pane layout

The window is divided into three panes. Each pane is resizable and can be docked. Their availability and default visibility can be controlled by the administrator. By default, an album treeview appears in the left pane, the thumbnails or media object is in the middle, and details about the selected items are in the right pane. The contents of each pane are customizable by editing it’s template in the site admin area.

UI templates

The most frequent request I’ve heard about Gallery Server Pro was an easier way to customize the UI. Previous versions were heavily based on ASP.NET server controls hard coded into ASCX user controls. While .NET developers could download the source code to change things, it was a steep learning curve and, at any rate, not an option for anyone else.

For version 3, I completely replaced it with an architecture of flexible jsRender templates and Web.API callbacks. There are several templates covering the various areas of the screen (header, left pane, right pane, album view, media object view). These templates are managed on a new page in the site admin area:

ui_tmpl_html_700x604

In this screenshot, we are looking at the template for the album thumbnail view (shown in the center pane of the first screenshot of this blog post). The template is 100% jsRender syntax, which is a client side HTML rendering system. If one wishes to execute javascript at the time the template is rendered, enter it on the JavaScript tab. The Target Albums tab lets you select which albums the template applies to, and the Preview tab lets you see the effect of your changes before you save it.

Media templates

Version 3 also lets you customize the the exact HTML and javascript sent to a browser based on the media type and the client browser. For example, MP4 files are supported using native HTML5 syntax in many modern browsers but not Internet Explorer version 8 and earlier. We handle this by defining a default MP4 media template that specifies the HTML5 video tag:

media_tmpl_700x481

Then we specify another media template for MP4 using the browser ID ie1to8. That will catch all IE versions up to 8. For them we send some HTML and javascript that uses the FlowPlayer Flash plug-in to render the video.

media_tmpl_ie1to8_700x388

Gallery Server Pro ships with a default set of media templates that should work well for most of you, but feel free to tweak it as desired.

Two slide show modes - full screen and inline

Version 3 adds a full screen slide show capability, which is enabled by default.

fullscreen_slideshow

If you wish to revert to the older, inline slide show, the setting is on the Media Objects – General page. You may want to use the inline slideshow if you are integrating the gallery into an existing web site and you want to let the user cycle through images without having the entire screen taken over.

Looks great on any device - iOS, Android, Windows Phone, Mac, Linux, and Windows

A lot of work has gone into making the gallery look fantastic regardless of the device you are using, from a 3.5-inch iPhone to a 27-inch desktop monitor. The gallery uses adaptive rendering to hide certain elements on small screens, while using clever CSS in other aspects to reflow the right pane underneath the media object instead of to the right. For example, here is how an image looks on an 4-inch Android phone:

android1

If you scroll down, you see the rest of the page:

android2

Improved video encoding

Version 2.6 was set to encode all videos to Flash video, but Flash is a dying animal and doesn’t work on most tablets and phones. So version 3 now encodes all video to H.264 MP4 by default, leading to much greater compatibility on devices.

As with previous versions, the encoder still requires the installation of the Gallery Server Pro Binary Pack, available as a free download, and must run in a web site with full trust. The trust level is reported on the landing page in the site admin area.

Improved sorting

The sorting function has been overhauled and vastly improved. You can now sort by any metadata tag in ascending or descending order. To prevent overwhelming the user, a default gallery lets you choose from five properties.

sort1

When anonymous users or logged on users with view-only permission sort an album, it applies only to them. This preference is stored in session for anonymous users and in the user’s profile for logged on users. When an editor changes a sort, they are applying the new sort to all future users.

To change which properties are available to the user for sorting, edit the album template. For example, to allow sorting by Author, go to the Templates page in the site admin area and select the album template and look for the section where the sort options are defined. Then add a row for the Author:

sort2

Notice that we specify data-id=’2’. That number identifies the Author metadata item and can be found on the Metadata screen in the site admin area. Refer to the earlier screen shot of that page to see what I mean. We also hard code the text ‘Author’ rather than specify a variable like the others. After saving the changes, we can now sort by Author:

sort3

Conclusion

I am so pleased to release this version. The UI template feature alone allows dramatic new capabilities for making the gallery fit your requirements. In the coming weeks I’ll be releasing an updated Admin Guide. Also in the works are additional blog posts getting into the details of some of these features. This is an exciting time for Gallery Server Pro!

Friday, May 24, 2013

New Licensing Options for 3.0

Since its first release, the stand-alone version of Gallery Server Pro has been licensed under the GNU General Public License and available at no charge. Over the years I have had requests for a commercial license that frees you from the restrictions of the GPL and, for some companies, its stigma.

At the same time, the donation model I’ve been using has not been generating enough revenue to pay the bills, forcing me to accept short-term contracts to fill the gap. I don’t really mind that much, but it slows down the progress of Gallery Server Pro. My head is filled with cool ideas and I never seem to get caught up. I would love to spend more time fulfilling all the feature requests you send me.

As a result, version 3.0 will be released in several variants, as seen in the chart below.

GPL

GSP box graphic

FREE

GPL Professional

GSP box graphic

$49

Enterprise

GSP box graphic

$249

$149 Sale!

Unlimited file types
Unlimited albums and media files
SQL CE support
Source code available
Includes future 3.0.X releases
Gallery Server Pro logo hidden -
Commercial license - -
SQL Server 2005/2008/2012/Express
(up to 20x faster than SQL CE)
- -
SQL Azure support - -
Sample PayPal and Facebook templates - -
Guaranteed forum support - -

The free GPL version you know and love lives on, with full functionality supporting unlimited file types and number of media files using the SQL CE database.

The GPL Professional edition is the same as the free GPL version, except it comes with a special product key that hides the Gallery Server Pro logo in the footer of the page.

The Enterprise edition uses a commercial license and adds support for SQL Server and includes several bonus features such as SQL Azure support, sample PayPal and Facebook UI templates, and a guaranteed response in the forum. It is regularly priced at $249 but for a limited time I’m knocking $100 off.

The DotNetNuke module is not listed because it won’t be available until at least Entity Framework 6 is released. (It requires the multiple contexts per database feature being introduced in that version.)

These changes give you the flexibility you’ve been asking for while providing enough revenue to allow me to introduce features at a faster pace. It should be a win-win situation for all of us.

Saturday, May 18, 2013

Gallery Server Pro Headquarters Relocated to Boulder, CO

A few minutes ago I fired up the PC in my new home office in Boulder, CO. That’s right – my family has moved from my childhood home in Fort Atkinson, WI to a new home in Boulder. That’s why I haven’t been able to respond to the recent forum posts regarding v3 RC1 - I’ve been off the grid for several days except for what I can accomplish on a smart phone along I-80. Between packing and driving a giant U-Haul truck towing a car across Iowa and Nebraska, I could do little more than read the forum posts the last few days. I’ll address everything in the coming days as we settle into our new life.

We lived in Boulder a decade ago but had moved to WI to be closer to my folks in 2004. It’s been wonderful to be near family but the mountains and culture of Boulder kept whispering in our ears. Last summer we visited Boulder on a vacation and it really sunk in how much we missed the place. Check out some of the top rankings Boulder gets:

  • The 10 Happiest Cities – # 1
  • Top Brainiest Cities – No. 1
  • Ten Best Cities for the Next Decade – 4th
  • Gallup-Healthways Well-Being Index – No. 1
  • Best Cities to Raise an Outdoor Kid – No. 1
  • America's Top 25 Towns to Live Well – No. 1
  • Top 10 Healthiest Cities to Live and Retire – No. 6
  • Top 10 Cities for Artists – No. 8
  • Lesser-Known LGBT Family-Friendly Cities – No. 1
  • America's Foodiest Town – No. 1

I’ll continue working on Gallery Server Pro while Margaret looks for a new job. Our 5 year old son is excited to live in a neighborhood teeming with children and having a killer playground just around the corner. In fact, I just returned from buying him a treat from an ice cream truck meandering through the neighborhood. Sweet!

Friday, May 10, 2013

Gallery Server Pro 3.0 Release Candidate 1 Available

Today I am making available release candidate 1 of Gallery Server Pro 3.0. It is functionally complete and now includes support for upgrading from 2.6.0 and higher. This is a major release and includes a tremendous number of new features and updated styling. Here are a few previous blog posts to refresh your memory:

Public Preview of Gallery Server Pro 3.0

Gallery Server Pro 3.0 Beta Released!

Add a PayPal shopping cart and Facebook to your gallery in 10 minutes

You can download the release candidate here:

Download Gallery Server Pro 3.0 RC1
Download Gallery Server Pro 3.0 RC1 (source code)

Skins

The RC ships with two skins – dark and light. The Site Settings – General page lets you switch to a different skin and the popup help icon gives you tips on how you can tweak the styling to suit your preferences, including how to create your own skin.

The beta site has been using the dark skin for a while. Just for fun, I switched it to the light skin for the RC. Check it out.

Reporting Bugs

While I shipped the RC with no known bugs, I will bet good money some are still lurking. Gallery Server Pro is a complex product with over 125 settings, and I have not exhaustively tested every one, much less the nearly infinite combination of them. And don’t even get me started on browser compatibility (sorry IE 6 and 7 users, I can’t support you any longer.) So please report anything you find in the forum.

Product Key and Licensing

The product keys you used in 2.6 do not work in the RC, and I do not yet have the infrastructure in place to provide new ones, so your RC gallery must run in trial mode. The RC is fully functional in trial mode and I expect to release the RTM version before the 30-day window ends, along with an updated product key wizard.

There will be a few licensing changes for 3.0. A free GPL version will continue to be available and I am adding a commercial license that offers benefits not available to GPL license holders. A subsequent blog post will dig into the details.

Upgrade from 2.6

Version 3.0 supports upgrading 2.6 or higher galleries. Note that some settings will be preserved while others are reset to conform to the 3.0 requirements. The upgrade wizard from previous versions has been replaced by a new process that allows you to import a 2.6 backup file into a 3.0 gallery. Follow these steps:

  1. Make a backup of your database, web files, and media files. You’ll only need them if the upgrade fails and you need to revert to 2.6.
  2. In your 2.6 gallery, go to the backup/restore page and create a backup file of your user accounts and gallery data.
  3. Delete your web files, but not your media files (the ones stored in gs\mediaobjects by default).
  4. Download the RC and extract the files to your web directory. As with 2.6, the IIS account needs modify permission to the App_Data and gs\mediaobjects directories.
  5. If using SQL Server, open web.config and update the connectionStrings section to use the SqlClient provider instead of SQL CE (that is, comment out the SQL CE connection string and uncomment the SQL Server one.) Then update the SQL Server connection string to point to your gallery database. Note that 3.0 uses a completely different naming scheme, so the 3.0 tables will exist along side your 2.6 ones until you get a chance to manually delete the 2.6 ones (if you want to). For example, the table gs_Album from 2.6 is named Gsp.Album in 3.0 (note the use of a new Gsp schema).
  6. Open default.aspx in a browser. The gallery will detect that no data structure exists for the connection string named GalleryDb and automatically create it. (For you techies it’s using Code First Migrations for this part.)
  7. A default page will load with a message allowing you to create an admin account. Go ahead and create one. This account will be replaced during the restore operation, so don’t worry about writing down the password.
  8. Now you should be able to go to the backup/restore page in your 3.0 gallery. Click the restore tab and upload the backup file you created in step 2 above. Then restore it.
  9. The restore operation detects the file is from 2.6 and upgrades the schema as it imports it into the new tables it created in step 7. For large galleries this can take a few minutes, especially if you are using SQL CE.
  10. Once complete, review the settings on the various admin pages to verify everything looks the way you want it.

Writing the migration code to convert the 2.6 data to the 3.0 schema was a challenge, and I haven’t had very many databases to test it out on. So it’s very important you try out the upgrade process to make sure it is robust. I wouldn’t be surprised if we find a few glitches, but I need your help to find them.

If you don’t have time to try the upgrade yourself but would like to volunteer your database for testing, send your 2.6 database to me at roger*at*techinfosystems*dot*com (i.e. your .sdf or .mdf file). I’ll test it to make sure it converts. But be aware that a default gallery installation stores passwords as plain text in the database file, so if you won’t want me to see them, don’t send it to me. You can preserve some security by sending me your database anonymously.

Upgrade from the RC to 3.0 RTM

I am hopeful that very small changes will be necessary between the RC and the final release. If so, there is a good chance that you can easily upgrade your RC gallery by copying over a fresh set of web files. Unfortunately, I can’t promise this will be the case, so be prepared for the possibility that there won’t be a RC to RTM upgrade path.

When is it going to be released?

That depends on the RC. I’ll wait a couple weeks to see what gets reported. If there is nothing major, I think I’ll have it out by early June. That will be a Very Good Day.

Monday, April 22, 2013

Security Vulnerability for 2.6.1 Patched

Last week a security company alerted me to a vulnerability in Gallery Server Pro. In certain circumstances, a malicious user can manually construct an HTTP request to upload an arbitrary file to the server that may contain code, which may then be executed with a subsequent HTTP request. A user may also be able to upload a file to any album, even one where the user does not have edit album permission.

The fix is in the file GalleryServerPro.Web.dll (TechInfoSystem.GalleryServerPro.dll in the DotNetNuke version) and can be obtained in the patch listed on the release history page. Copy the DLL to your bin directory. No other action is necessary. (The other files in the patch fix a UI issue with IE10 and are not related to this vulnerability.)

I strongly recommend you apply this patch. It takes only a minute.

The fix has also been applied to the v3 code, so this vulnerability will not be present in the final 3.0 release.

I should stress that in a default installation anonymous users cannot take advantage of this vulnerability. To exploit the issue, the malicious user must already have a user account in the gallery and be logged on. To write a file outside the mediaobjects directory, the admin must have configured the IIS app pool identity to allow writing to those directories.

The source code for 2.6.1 has been updated, so no patch is necessary for that version. If you are curious, the fix is in the file Website\gs\handler\upload.ashx.cs (function SaveFileToServer).

My thanks goes out to the company who responsibly reported this issue and for giving me time to address it and get it into your hands.

Security best practice tips

Here are a few tips to maximize the security of your gallery installation:

  • Create a unique IIS app pool identity that is used solely for the gallery website. Lock down its permissions to read access for web files and read/write access to the App_Data and media objects directory.
  • Store the media objects directory outside the web application root. This has the added benefit of not triggering an app restart when an album is deleted (which deletes the directory, causing ASP.NET to restart the app).
  • Don’t let users enter HTML and especially javascript in titles and caption. These setting are on the User Settings page and are disabled by default.
  • Keep the option ‘Display detailed error message when an exception occurs’ disabled, turning it on for debugging only (Gallery Settings page).
  • Keep debug set to ‘false’ in web.config.
  • Switch to hashed or encrypted passwords for users.
  • Run the app in medium trust. This has a downside, though, in that you can’t take advantage of the features in the GSP Binary Pack.
  • (SQL Server) Use Windows Authentication to connect to the database using the IIS app pool identity and configure the database to give the smallest possible set of permissions to this user. Basically, that means select/execute permission on the views/stored procedures for 2.6 and select/update/insert/delete permission to the tables for 3.0+.