/// <summary>
        /// Sorts the <paramref name="galleryItems" /> in the order in which they are passed.
        /// This method is used when a user is manually sorting an album and has dragged an item to a new position.
        /// </summary>
        /// <param name="galleryItems">The gallery objects to sort. Their position in the array indicates the desired
        /// sequence. Only <see cref="Entity.GalleryItem.Id" /> and <see cref="Entity.GalleryItem.ItemType" /> need be
        /// populated.</param>
        /// <param name="userName">Name of the logged on user.</param>
        public static void Sort(GalleryItem[] galleryItems, string userName)
        {
            if (galleryItems == null || galleryItems.Length == 0)
            {
                return;
            }

            try
            {
                // To improve performance, grab a writable collection of all the items in the album containing the first item.
                // At the time this function was written, the galleryItems parameter will only include items in a single album,
                // so this step allows us to get all the items in a single step. For robustness and to support all possible usage,
                // the code in the iteration manually loads a writable instance if it's not in the collection.
                var galleryObjects = GetWritableSiblingGalleryObjects(galleryItems[0]);

                var seq = 1;
                foreach (var galleryItem in galleryItems)
                {
                    // Loop through each item and update its Sequence property to match the order in which it was passed.
                    var item = galleryItem;

                    var galleryObject = galleryObjects.FirstOrDefault(go => go.Id == item.Id && go.GalleryObjectType == (GalleryObjectType)item.ItemType);

                    if (galleryObject == null)
                    {
                        // Not found, so load it manually. This is not expected to ever occur when manually sorting an album, but we
                        // include it for robustness.
                        if ((GalleryObjectType)galleryItem.ItemType == GalleryObjectType.Album)
                        {
                            galleryObject = Factory.LoadAlbumInstance(galleryItem.Id, false, true);
                        }
                        else
                        {
                            galleryObject = Factory.LoadMediaObjectInstance(galleryItem.Id, true);
                        }
                    }

                    galleryObject.Sequence = seq;
                    GalleryObjectController.SaveGalleryObject(galleryObject, userName);
                    seq++;
                }

                HelperFunctions.PurgeCache();
            }
            catch (Exception ex)
            {
                AppEventController.LogError(ex);

                throw;
            }
        }
        /// <summary>
        /// Gets a writable collection of the gallery objects in the album containing <paramref name="galleryItem" />, including 
        /// <paramref name="galleryItem" />. If <paramref name="galleryItem" /> does not represent a valid object, an empty 
        /// collection is returned. Guaranteed to not return null.
        /// </summary>
        /// <param name="galleryItem">A gallery item. The object must have the <see cref="GalleryItem.Id" /> and 
        /// <see cref="GalleryItem.ItemType" /> properties specified; the others are optional.</param>
        /// <returns>An instance of <see cref="IGalleryObjectCollection" />.</returns>
        private static IGalleryObjectCollection GetWritableSiblingGalleryObjects(GalleryItem galleryItem)
        {
            IGalleryObject parentAlbum;

            try
            {
                int parentAlbumId;
                if ((GalleryObjectType)galleryItem.ItemType == GalleryObjectType.Album)
                {
                    parentAlbumId = LoadAlbumInstance(galleryItem.Id, false).Parent.Id;
                }
                else
                {
                    parentAlbumId = Factory.LoadMediaObjectInstance(galleryItem.Id).Parent.Id;
                }

                parentAlbum = LoadAlbumInstance(parentAlbumId, true, true);
            }
            catch (InvalidAlbumException)
            {
                parentAlbum = new NullGalleryObject();
            }
            catch (InvalidMediaObjectException)
            {
                parentAlbum = new NullGalleryObject();
            }

            return parentAlbum.GetChildGalleryObjects();
        }
        /// <summary>
        /// Converts the <paramref name="galleryObject" /> to an instance of <see cref="Entity.GalleryItem" />.
        /// The instance can be JSON-serialized and sent to the browser.
        /// </summary>
        /// <param name="galleryObject">The gallery object to convert to an instance of
        /// <see cref="Entity.GalleryItem" />. It may be a media object or album.</param>
        /// <param name="moBuilderOptions">A set of properties to be used to build the HTML, JavaScript or URL for the 
        /// <paramref name="galleryObject" />.</param>
        /// <returns>Returns an <see cref="Entity.GalleryItem" /> object containing information
        /// about the requested item.</returns>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="galleryObject" /> or 
        /// <paramref name="moBuilderOptions" /> is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown when <paramref name="moBuilderOptions" /> does
        /// has a null or empty <see cref="MediaObjectHtmlBuilderOptions.Browsers" /> property.</exception>
        public static GalleryItem ToGalleryItem(IGalleryObject galleryObject, MediaObjectHtmlBuilderOptions moBuilderOptions)
        {
            if (galleryObject == null)
                throw new ArgumentNullException("galleryObject");

            if (moBuilderOptions == null)
                throw new ArgumentNullException("moBuilderOptions");

            if (moBuilderOptions.Browsers == null || moBuilderOptions.Browsers.Length == 0)
                throw new ArgumentOutOfRangeException("moBuilderOptions.Browsers", "The Browsers array property must have at least one element.");

            moBuilderOptions.GalleryObject = galleryObject;

            var gItem = new GalleryItem
                                        {
                                            Id = galleryObject.Id,
                                            Title = galleryObject.Title,
                                            Caption = galleryObject.Caption,
                                            Views = GetViews(moBuilderOptions).ToArray(),
                                            ViewIndex = 0,
                                            MimeType = (int)galleryObject.MimeType.TypeCategory,
                                            ItemType = (int)galleryObject.GalleryObjectType
                                        };

            IAlbum album = galleryObject as IAlbum;
            if (album != null)
            {
                gItem.IsAlbum = true;
                //gItem.DateStart = album.DateStart;
                //gItem.DateEnd = album.DateEnd;
                gItem.NumAlbums = album.GetChildGalleryObjects(GalleryObjectType.All, !Utils.IsAuthenticated).Count;
                gItem.NumMediaItems = album.GetChildGalleryObjects(GalleryObjectType.MediaObject, !Utils.IsAuthenticated).Count;
            }

            return gItem;
        }