public void WriteGalleriesData(Gallery gallery)
        {
            if (!File.Exists(gallery.DataFilePath))
            {
                XmlDocument galleryData = new XmlDocument();

                XmlElement galleryElement = galleryData.CreateElement(_elementName_Root);
                galleryData.AppendChild(galleryElement);

                XmlAttribute nameAttribute = galleryData.CreateAttribute(_attributeName_GalleryName);
                nameAttribute.Value = gallery.Name;
                galleryElement.Attributes.Append(nameAttribute);

                XmlAttribute descriptionAttribute = galleryData.CreateAttribute(_attributeName_Description);
                descriptionAttribute.Value = gallery.Description;
                galleryElement.Attributes.Append(descriptionAttribute);

                XmlAttribute lastUpdatedTimeAttribute = galleryData.CreateAttribute(_attributeName_GalleryLastUpdateTime);
                lastUpdatedTimeAttribute.Value = gallery.LastUpdatedTime.ToString(CultureInfo.GetCultureInfo("en-GB"));
                galleryElement.Attributes.Append(lastUpdatedTimeAttribute);

                XmlAttribute createdTimeAttribute = galleryData.CreateAttribute(_attributeName_GalleryCreatedTime);
                createdTimeAttribute.Value = gallery.CreatedTime.ToString(CultureInfo.GetCultureInfo("en-GB"));
                galleryElement.Attributes.Append(createdTimeAttribute);

                int textItemCount = 0;
                foreach (var galleryItem in gallery.Items.OrderBy(i => i.SortOrder))
                {
                    XmlElement galleryItemElement = null;
                    switch (galleryItem.ItemType)
                    {
                        case GalleryItemType.Unknown:
                            break;
                        case GalleryItemType.Image:
                            galleryItemElement = galleryData.CreateElement(_elementName_Image);
                            galleryElement.AppendChild(galleryItemElement);

                            XmlAttribute galleryItemNameAttribute = galleryData.CreateAttribute(_attributeName_ImageName);
                            galleryItemNameAttribute.Value = ((GalleryImageItem)galleryItem).Name;
                            galleryItemElement.Attributes.Append(galleryItemNameAttribute);

                            XmlAttribute galleryItemTextDataAttribute = galleryData.CreateAttribute(_attributeName_ImageComment);
                            galleryItemTextDataAttribute.Value = galleryItem.TextData;
                            galleryItemElement.Attributes.Append(galleryItemTextDataAttribute);

                            if (((GalleryImageItem)galleryItem).IsCover)
                            {
                                XmlAttribute galleryItemIsCoverAttribute = galleryData.CreateAttribute(_attributeName_ImageIsCover);
                                galleryItemIsCoverAttribute.Value = true.ToString();
                                galleryItemElement.Attributes.Append(galleryItemIsCoverAttribute);
                            }
                            break;
                        case GalleryItemType.Text:
                            galleryItemElement = galleryData.CreateElement(_elementName_Text);
                            galleryElement.AppendChild(galleryItemElement);

                            var cdata = galleryData.CreateCDataSection(galleryItem.TextData);
                            galleryItemElement.AppendChild(cdata);

                            textItemCount++;

                            break;
                        default:
                            break;
                    }
                }

                if (textItemCount == 0)
                {
                    // If no text items exist in the gallery, we insert an empty one at the end
                    // This is to provide an example to the administor of how to include text in the gallery.

                    //XmlComment textExplanationCommentElement = galleryData.CreateComment("You can use Text elements to include blocks of information within your gallery. Images and text will appear in the order they are defined.");
                    //galleryElement.AppendChild(textExplanationCommentElement);

                    XmlElement placeholderTextItemElement = galleryData.CreateElement(_elementName_Text);
                    galleryElement.AppendChild(placeholderTextItemElement);

                    XmlCDataSection placeholderCData = galleryData.CreateCDataSection(String.Empty);
                    placeholderTextItemElement.AppendChild(placeholderCData);
                }

                using (XmlWriter writer = XmlWriter.Create(gallery.DataFilePath))
                {
                    galleryData.WriteTo(writer);
                }
            }
        }
        public void ReadGalleryData(Gallery gallery)
        {
            if (File.Exists(gallery.DataFilePath))
            {
                XmlReader reader = XmlReader.Create(gallery.DataFilePath);
                XmlDocument data = new XmlDocument();
                data.Load(reader);
                reader.Close();

                // This is what we need to do, but with proper error checking for null and invalid data.
                XmlNode galleryNode = data.SelectSingleNode(_elementName_Root);
                if (galleryNode != null)
                {
                    XmlNode galleryNameNode = galleryNode.Attributes.GetNamedItem(_attributeName_GalleryName);
                    if (galleryNameNode != null)
                    {
                        gallery.Name = galleryNameNode.Value;
                    }

                    XmlNode galleryDescriptionNode = galleryNode.Attributes.GetNamedItem(_attributeName_Description);
                    if (galleryDescriptionNode != null)
                    {
                        gallery.Description = galleryDescriptionNode.Value;
                    }

                    XmlNode galleryLastUpdatedTimeNode = galleryNode.Attributes.GetNamedItem(_attributeName_GalleryLastUpdateTime);
                    if (galleryLastUpdatedTimeNode != null)
                    {
                        gallery.LastUpdatedTime = DateTime.Parse(galleryLastUpdatedTimeNode.Value, CultureInfo.GetCultureInfo("en-GB"));
                    }

                    XmlNode galleryCreatedTimeNode = galleryNode.Attributes.GetNamedItem(_attributeName_GalleryCreatedTime);
                    if (galleryCreatedTimeNode != null)
                    {
                        gallery.CreatedTime = DateTime.Parse(galleryCreatedTimeNode.Value, CultureInfo.GetCultureInfo("en-GB"));
                    }

                    int nodeSortOrder = 0;
                    foreach (var childItem in galleryNode.ChildNodes)
                    {
                        XmlNode childNode = childItem as XmlNode;
                        if (childNode != null)
                        {
                            switch (childNode.Name)
                            {
                                case _elementName_Image:
                                    XmlNode imageNameNode = childNode.Attributes.GetNamedItem(_attributeName_ImageName);
                                    if (imageNameNode != null)
                                    {
                                        String imageName = imageNameNode.Value;

                                        // Check if there is an image item that matches this data node.
                                        var imageItem = gallery.Items.SingleOrDefault(i =>
                                        {
                                            if (i.ItemType == GalleryItemType.Image)
                                            {
                                                var image = i as GalleryImageItem;
                                                if (image.Name == imageName)
                                                {
                                                    return true;
                                                }
                                            }

                                            return false;
                                        }) as GalleryImageItem;

                                        // If we found a matching image, load the data for it from the XML.
                                        if (imageItem != null)
                                        {
                                            imageItem.SortOrder = nodeSortOrder;

                                            XmlNode imageCommentNode = childNode.Attributes.GetNamedItem(_attributeName_ImageComment);
                                            if (imageCommentNode != null)
                                            {
                                                imageItem.TextData = imageCommentNode.Value;
                                            }

                                            XmlNode imageIsCoverNode = childNode.Attributes.GetNamedItem(_attributeName_ImageIsCover);
                                            if (imageIsCoverNode != null)
                                            {
                                                imageItem.IsCover = bool.Parse(imageIsCoverNode.Value);
                                            }
                                        }
                                    }

                                    break;
                                case _elementName_Text:
                                    // Create new text items when processed in the XML data.
                                    GalleryTextItem textItem = new GalleryTextItem();
                                    textItem.SortOrder = nodeSortOrder;
                                    textItem.TextData = childNode.InnerText;

                                    gallery.Items.Add(textItem);

                                    break;
                                default:
                                    // Just ignore unknown element types.
                                    break;
                            }
                        }

                        nodeSortOrder++;
                    }
                }
            }
        }
        public GalleriesModel BuildGalleriesModel()
        {
            List<Gallery> galleries = new List<Gallery>();

            String[] galleryPaths = Directory.GetDirectories(_galleryContentRootFilePath);

            foreach (String galleryPath in galleryPaths)
            {
                // Create a new gallery for each directory.
                Gallery gallery = new Gallery();

                // Get the directory name to use as the gallery name.
                DirectoryInfo galleryDirectoryInfo = new DirectoryInfo(galleryPath);
                gallery.FolderName = galleryDirectoryInfo.Name;
                gallery.Name = galleryDirectoryInfo.Name;

                // We set the last updated time to now by default, but this will be read from the data file later if it exists.
                gallery.LastUpdatedTime = DateTime.Now;
                gallery.CreatedTime = DateTime.Now;

                gallery.CoverImageHttpPath = Path.Combine(_galleryContentRootHttpPath, String.Format("{0}/{1}", gallery.FolderName, _galleryCoverFileName));
                gallery.FeedImageHttpPath = _baseHttpUrl + Path.Combine(_galleryContentRootHttpPath, String.Format("{0}/{1}", gallery.FolderName, _galleryCoverFileName));
                gallery.CoverImageFilePath = Path.Combine(_galleryContentRootFilePath, String.Format(@"{0}\{1}", gallery.FolderName, _galleryCoverFileName));

                String imagesPath = Path.Combine(galleryPath, _galleryFolderOriginalImages);
                String[] imageFilePaths = Directory.GetFiles(imagesPath, _imageFileTypeFilter);

                // Create zip paths and files.
                String zipFileNameLQ = String.Format(_zipFileLQFormat, gallery.FolderName);
                String zipFileNameHQ = String.Format(_zipFileHQFormat, gallery.FolderName);
                String zipFileFullPathLQ = Path.Combine(galleryPath, zipFileNameLQ);
                String zipFileFullPathHQ = Path.Combine(galleryPath, zipFileNameHQ);
                ZipFile zipFileLQ = null;
                ZipFile zipFileHQ = null;
                if (!File.Exists(zipFileFullPathLQ))
                {
                    zipFileLQ = new ZipFile(zipFileFullPathLQ);
                    zipFileLQ.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
                }
                if (!File.Exists(zipFileFullPathHQ))
                {
                    zipFileHQ = new ZipFile(zipFileFullPathHQ);
                    zipFileHQ.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
                }

                // Loop over all of the images found in the path and create
                // items for each one with the required data.
                foreach (String imagePath in imageFilePaths)
                {
                    GalleryImageItem image = new GalleryImageItem();
                    image.Name = Path.GetFileName(imagePath);

                    image.FullImageHttpPath = Path.Combine(_galleryContentRootHttpPath, String.Format("{0}/{1}/{2}", gallery.FolderName, _galleryFolderOriginalImages, image.Name));
                    image.ScaledImageHttpPath = Path.Combine(_galleryContentRootHttpPath, String.Format("{0}/{1}/{2}", gallery.FolderName, _galleryFolderScaledImages, image.Name));
                    image.ThumbImageHttpPath = Path.Combine(_galleryContentRootHttpPath, String.Format("{0}/{1}/{2}", gallery.FolderName, _galleryFolderThumbs, image.Name));

                    image.FullImageFileLocation = Path.Combine(_galleryContentRootFilePath, String.Format(@"{0}\{1}\{2}", gallery.FolderName, _galleryFolderOriginalImages, image.Name));
                    image.ScaledImageFileLocation = Path.Combine(_galleryContentRootFilePath, String.Format(@"{0}\{1}\{2}", gallery.FolderName, _galleryFolderScaledImages, image.Name));
                    image.ThumbImageFileLocation = Path.Combine(_galleryContentRootFilePath, String.Format(@"{0}\{1}\{2}", gallery.FolderName, _galleryFolderThumbs, image.Name));

                    // Alternative code that maps http paths to local file system.
                    //image.FullImageFileLocation = HttpContext.Current.Server.MapPath(image.FullImageHttpPath);
                    //image.ScaledImageFileLocation = HttpContext.Current.Server.MapPath(image.ScaledImageHttpPath);
                    //image.ThumbImageFileLocation = HttpContext.Current.Server.MapPath(image.ThumbImageHttpPath);

                    // Process each image to create scaled copies and thumbnails.
                    _imageProcessor.ProcessImage(image);

                    // If zip files are required add each image to the appropriate zips (either scaled or full size).
                    if (zipFileLQ != null)
                    {
                        zipFileLQ.AddFile(image.ScaledImageFileLocation, String.Empty);
                    }
                    if (zipFileHQ != null)
                    {
                        zipFileHQ.AddFile(image.FullImageFileLocation, String.Empty);
                    }

                    gallery.Items.Add(image);
                }

                // Build the file path for the gallery data file.
                String galleryDataFileName = String.Format(Settings.Default.File_GalleryDataFileNameFormat, gallery.UrlFriendlyName);
                gallery.DataFilePath = Path.Combine(_galleryContentRootFilePath, galleryDataFileName);

                /* Attempt to read data from the data file.
                 * This data will extend information already
                 * gathered from the gallery paths and includes
                 * textual contant such as descriptions and comments.
                 *
                 * Note: This may potentially change the gallery name.
                 * The folder name will not be changed, but
                 * the display name and Url friendly name may
                 * be changed.
                 */
                _dataProcessor.ReadGalleryData(gallery);

                gallery.GalleryExternalHttpPath = String.Format(_galleryHttpPathFormat, _baseHttpUrl, gallery.UrlFriendlyName);
                gallery.GalleryInternalHttpPath = String.Format(_galleryHttpPathFormat, String.Empty, gallery.UrlFriendlyName);
                gallery.GalleryExternalEnhancedHttpPath = String.Format(_galleryHttpEnhancedPathFormat, _baseHttpUrl, gallery.UrlFriendlyName);
                gallery.GalleryInternalEnhancedHttpPath = String.Format(_galleryHttpEnhancedPathFormat, String.Empty, gallery.UrlFriendlyName);

                // Check if we need to create a gallery cover.
                if (!File.Exists(gallery.CoverImageFilePath) && gallery.Items.Count > 0)
                {
                    GalleryImageItem galleryCoverImage = null;

                    // First we attempt to resolve the gallery cover from the information known about the images.
                    galleryCoverImage = gallery.Items.FirstOrDefault(i =>
                    {
                        if (i.ItemType == GalleryItemType.Image)
                        {
                            var image = i as GalleryImageItem;
                            if (image != null)
                            {
                                return image.IsCover;
                            }
                        }
                        return false;
                    }) as GalleryImageItem;

                    //if (galleryCoverImage == null)
                    //{
                    //    // If the first attempt failed, next we try to resolve the image
                    //    // by looking for an image named:
                    //    //      Cover
                    //    //      GalleryCover

                    //    galleryCoverImage = gallery.Items.FirstOrDefault(i =>
                    //    {
                    //        if (i.ItemType == GalleryItemType.Image)
                    //        {
                    //            var image = i as GalleryImageItem;
                    //            if (String.Compare(image.Name, "cover.jpg", true) == 0 ||
                    //                String.Compare(image.Name, "gallerycover.jpg", true) == 0)
                    //            {
                    //                return image.IsCover;
                    //            }
                    //        }
                    //        return false;
                    //    }) as GalleryImageItem;
                    //}

                    if (galleryCoverImage == null)
                    {
                        // Finally, as a last resort to resolve the cover image, just select the first image in the gallery.
                        galleryCoverImage = (GalleryImageItem)gallery.Items.FirstOrDefault(i => i.ItemType == GalleryItemType.Image);
                    }

                    if (galleryCoverImage != null)
                    {
                        // Now we have the image, render the cover from it.
                        _coverRenderer.GenerateCoverImage(gallery.Name, gallery.CoverImageFilePath, galleryCoverImage.FullImageFileLocation);

                        // Mark this as the cover image. (This will be written to the data file later to guide the administrator if they need to change the cover image.)
                        galleryCoverImage.IsCover = true;
                    }
                }

                // If zip files are required, write the data out to disk.
                if (zipFileLQ != null)
                {
                    zipFileLQ.Save();
                }
                if (zipFileHQ != null)
                {
                    zipFileHQ.Save();
                }

                // Populate some information about the zips on the gallery object for use later when the gallery is displayed.
                gallery.ZipLQFileSizeBytes = new FileInfo(zipFileFullPathLQ).Length;
                gallery.ZipHQFileSizeBytes = new FileInfo(zipFileFullPathHQ).Length;
                gallery.ZipLQHttpPath = Path.Combine(_galleryContentRootHttpPath, String.Format("{0}/{1}", gallery.FolderName, zipFileNameLQ));
                gallery.ZipHQHttpPath = Path.Combine(_galleryContentRootHttpPath, String.Format("{0}/{1}", gallery.FolderName, zipFileNameHQ));
                gallery.ZipLQFileLocation = Path.Combine(_galleryContentRootFilePath, String.Format(@"{0}\{1}", gallery.FolderName, zipFileNameLQ));
                gallery.ZipHQFileLocation = Path.Combine(_galleryContentRootFilePath, String.Format(@"{0}\{1}", gallery.FolderName, zipFileNameHQ));

                _dataProcessor.WriteGalleriesData(gallery);

                // Add the gallery to the list.
                galleries.Add(gallery);
            }

            SyndicationFeed feed = _feedBuilder.BuildGalleryFeed(galleries);

            GalleriesModel model = new GalleriesModel(_galleriesIndexHttpPath, galleries, feed);
            return model;
        }