Example #1
0
 private static BlogMLBlog GetBlog(string filename)
 {
     using (var stream = File.OpenRead(filename))
     {
         return(BlogMLSerializer.Deserialize(stream));
     }
 }
Example #2
0
        private bool Initialize()
        {
            string blogMLFileLocation;

            if (!CommandLineUtils.TryGetFileLocation("BlogML File To convert", out blogMLFileLocation))
            {
                return(false);
            }

            using (StreamReader blogReader = File.OpenText(blogMLFileLocation))
            {
                BlogToConvert = BlogMLSerializer.Deserialize(blogReader);
            }

            AssignAllToOneUser = CommandLineUtils.GetBoolean("Assign all posts and tags to one existing user?");

            if (AssignAllToOneUser)
            {
                OneUserId = CommandLineUtils.GetInteger("What is the ID of the one existing user?");
            }

            GreatestExistingUserId = CommandLineUtils.GetInteger("What is the highest integer id of any existing user in your ghost blog?");

            return(true);
        }
Example #3
0
 private BlogMLBlog DeserializeBlogMlByStream(Stream stream)
 {
     try {
         return(BlogMLSerializer.Deserialize(stream));
     }
     catch (Exception ex) {
         _orchardServices.Notifier.Error(T("Error deserializing your blog Error:{0} - Please verify that this is an XML file stream", ex.Message));
         throw;
     }
 }
        /// <summary>
        ///  setps:
        ///  1, import category,tags, authors ...
        ///  2, import post, page, command
        /// </summary>
        public async Task <BlogMLImporterResult> ImportAsync(User user, string xml)
        {
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }

            if (xml == null)
            {
                throw new ArgumentNullException(nameof(xml));
            }

            _xml  = xml;
            _user = user;

            var blog = new BlogMLBlog();

            try
            {
                blog = BlogMLSerializer.Deserialize(XmlReader);
            }
            catch (Exception ex)
            {
                string message = string.Format("BlogML could not load with 2.0 specs. {0}", ex.Message);

                _logger.LogError(message);
            }

            try
            {
                await ImportCategoryAsync(blog);

                LoadFromXmlDocument();

                await ImportPostsAsync(blog);

                _logger.LogInformation($"Import completed. \n Category count: {CategoryCount}, Post count: {PostCount}, Page count: {PageCount} . ");

                return(new BlogMLImporterResult()
                {
                    CategoryCount = CategoryCount,
                    PageCount = PageCount,
                    PostCount = PostCount,
                });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "BlogML import failed.");
                throw;
            }
        }
        public void Can_Deserialize_Exported_File()
        {
            var migrator = new Migrator();

            migrator.Migrate();

            var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, BlogMLExportWorker.ExportFileName);
            var file = new FileStream(path, FileMode.Open);
            var blog = BlogMLSerializer.Deserialize(file);

            Assert.NotNull(blog);
            Assert.NotEqual(0, blog.Posts.Count);
            Assert.NotEqual(0, blog.Categories.Count);
        }
Example #6
0
        public void Process(BlogMLBlog blogml)
        {
            var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ExportFileName);

            Log.InfoFormat("Creating BlogML export file in {0}", path);

            using (var output = new FileStream(path, FileMode.Create))
            {
                Log.Info("Serializing Blog data into file.");
                BlogMLSerializer.Serialize(output, blogml);
            }

            Log.Info("Finished writing the export file.");
        }
        public async Task <bool> ImportAsync(User author)
        {
            _author = author;

            Message = string.Empty;

            var blog = new BlogMLBlog();

            try
            {
                blog = BlogMLSerializer.Deserialize(XmlReader);
            }
            catch (Exception ex)
            {
                Message = string.Format("BlogReader.Import: BlogML could not load with 2.0 specs. {0}", ex.Message);

                _logger.LogError(Message);

                return(false);
            }

            if (blog.Authors.Count > 0)
            {
                var firstAuthor = blog.Authors[0];
            }

            try
            {
                LoadFromXmlDocument();

                await LoadBlogCategories(blog);

                LoadBlogExtendedPosts(blog);

                await LoadBlogPosts();

                Message = string.Format("Imported {0} new posts", PostCount);
            }
            catch (Exception ex)
            {
                Message = string.Format("BlogReader.Import: {0}", ex.Message);

                _logger.LogError(Message);

                return(false);
            }

            return(true);
        }
        public void Exports_Post_Category_Relations()
        {
            var migrator = new Migrator();

            migrator.Migrate();

            var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, BlogMLExportWorker.ExportFileName);
            var file = new FileStream(path, FileMode.Open);
            var blog = BlogMLSerializer.Deserialize(file);

            var post       = blog.Posts[0];
            var categories = post.Categories;

            Assert.NotNull(categories);
            Assert.NotEmpty(categories);
        }
Example #9
0
        public BlogMLBlog DeserializeXMLStream(Stream stream)
        {
            BlogMLBlog blogPosts;

            try
            {
                blogPosts = BlogMLSerializer.Deserialize(stream);
            }
            catch (Exception exc)
            {
                var message    = "Failed to deserialize XML file";
                var logMessage = "Failed to deserialize provided XML file.";
                throw new ValidationException(() => message, logMessage, exc);
            }

            stream.Close();
            stream.Dispose();

            return(blogPosts);
        }
Example #10
0
        public BlogMLBlog DeserializeXMLStream(Stream stream)
        {
            try
            {
                stream.Position = 0;

                XmlReaderSettings readerSettings = new XmlReaderSettings();
                readerSettings.ConformanceLevel = ConformanceLevel.Document;
                readerSettings.CheckCharacters  = true;
                readerSettings.ValidationType   = ValidationType.None;

                var         xmlReader = XmlReader.Create(stream, readerSettings);
                XmlDocument xdoc      = new XmlDocument();
                xdoc.Load(xmlReader);
            }
            catch (Exception exc)
            {
                var message    = BlogGlobalization.ImportBlogPosts_FileIsNotValidXML_Message;
                var logMessage = "Provided file is not a valid XML file.";
                throw new ValidationException(() => message, logMessage, exc);
            }

            BlogMLBlog blogPosts;

            try
            {
                stream.Position = 0;
                blogPosts       = BlogMLSerializer.Deserialize(stream);
            }
            catch (Exception exc)
            {
                var message    = BlogGlobalization.ImportBlogPosts_FailedToDeserializeXML_Message;
                var logMessage = "Failed to deserialize provided XML file.";
                throw new ValidationException(() => message, logMessage, exc);
            }

            stream.Close();
            stream.Dispose();

            return(blogPosts);
        }
Example #11
0
        static void ConvertBlog(string inputXml, string outDir)
        {
            BlogMLBlog blog;

            using (var fs = File.OpenRead(inputXml))
            {
                blog = BlogMLSerializer.Deserialize(fs);
            }

            // Load the document and set the root element.
            var blogDoc = new XmlDocument();

            using (XmlTextReader tr = new XmlTextReader(inputXml))
            {
                tr.Namespaces = false;
                blogDoc.Load(inputXml);
            }


            var categories = blog.Categories
                             .Select(cat => new CategoryRef {
                Title = cat.Title, Id = cat.ID
            })
                             .ToDictionary(x => x.Id);

//            var mdConverter = new Converter(new DivPScheme());
            var mdConverter = new Converter();

            blog.Posts.ForEach(post =>
            {
                var slug = post.PostUrl.Substring(post.PostUrl.LastIndexOf('/') + 1);
                var tags = GetTags(blogDoc, post.ID);

                var header   = ComposeBlogHeader(post, categories, tags);
                var markdown = mdConverter.Convert(post.Content.UncodedText);

                Console.WriteLine($"Writing {slug} ({post.Title})");

                WriteConvertedMarkdown(outDir, slug, header, markdown);
            });
        }
Example #12
0
        /// <summary>
        /// Imports BlogML file into blog
        /// </summary>
        /// <returns>
        /// True if successful
        /// </returns>
        public override bool Import()
        {
            Message = string.Empty;
            var blog = new BlogMLBlog();

            try
            {
                blog = BlogMLSerializer.Deserialize(XmlReader);
            }
            catch (Exception ex)
            {
                Message = string.Format("BlogReader.Import: BlogML could not load with 2.0 specs. {0}", ex.Message);
                Utils.Log(Message);
                return(false);
            }

            try
            {
                LoadFromXmlDocument();

                LoadBlogCategories(blog);

                LoadBlogExtendedPosts(blog);

                LoadBlogPosts();

                Message = string.Format("Imported {0} new posts", PostCount);
            }
            catch (Exception ex)
            {
                Message = string.Format("BlogReader.Import: {0}", ex.Message);
                Utils.Log(Message);
                return(false);
            }

            return(true);
        }
Example #13
0
        static void ConvertBlog(string inputXml, string outDir)
        {
            BlogMLBlog blog;

            using (var fs = File.OpenRead(inputXml))
            {
                blog = BlogMLSerializer.Deserialize(fs);
            }

            // Load the document and set the root element.
            var blogDoc = new XmlDocument();

            using (XmlTextReader tr = new XmlTextReader(inputXml))
            {
                tr.Namespaces = false;
                blogDoc.Load(inputXml);
            }


            var categories = blog.Categories
                             .Select(cat => new CategoryRef {
                Title = cat.Title, Id = cat.ID
            })
                             .ToDictionary(x => x.Id);

            var convertedPostCount = 0;

            ITextFormatter textFormatter = new DefaultTextFormatter();

            IBlogUrlConverter blogUrlConverter =
                new TechnologyToolboxBlogUrlConverter();

            IBlogAttachmentService blogPostAttachmentService =
                new TechnologyToolboxBlogAttachmentService();

            ICodeBlockAnalyzer codeBlockAnalyzer =
                new OneNoteCodeBlockAnalyzer();

            var reverseMarkdownConfig = new Config
            {
                CodeBlockLanguageMapper =
                    new TechnologyToolboxCodeBlockLanguageMapper(),
                EmphasisChar                     = '_',
                GithubFlavored                   = true,
                HorizontalRuleString             = "---",
                ListNumberingStyle               = ListNumberingStyle.AlwaysOne,
                RemoveExcessIndentationFromCode  = true,
                RemoveTrailingWhitespaceFromCode = true
            };

            var imageUrlMapper = new TechnologyToolboxImageUrlMapper();

            var linkMapper = new LinkMapper(blogUrlConverter);

            blog.Posts.ForEach(post =>
            {
                var pipeline = new BlogPostConversionPipelineBuilder()
                               .ForPost(post)
                               .WithOutputDirectory(outDir)
                               .AddStep(new SlugDeterminationStep(blogUrlConverter))
                               .AddStep(new TechnologyToolboxMsdnBlogUrlExtractionStep(blogDoc))
                               // Extract tags *before* preprocessing blog post (for
                               // example, to allow the preprocessing step to remove tags
                               // embedded in the content of the post)
                               .AddStep(new TechnologyToolboxTagExtractionStep(blogDoc))
                               // Replace image URLs *before* preprocessing blog post (for
                               // example, to allow the preprocessing step to replace <img>
                               // elements embedded in the content of the post with Hugo
                               // "figure" shortcodes)
                               .AddStep(new ImageUrlReplacementStep(imageUrlMapper))
                               .AddStep(new FixHtmlFormattingIssuesStep())
                               .AddStep(new TechnologyToolboxBlogPostPreprocessor(
                                            linkMapper))
                               // Perform code analysis *after* preprocessing blog post (for
                               // example, to avoid overwriting "Text" language specified for
                               // log excerpts)
                               .AddStep(new CodeBlockAnalysisStep(
                                            codeBlockAnalyzer,
                                            reverseMarkdownConfig.CodeBlockLanguageMapper))
                               .AddStep(new ReverseMarkdownPreprocessingStep())
                               .AddStep(new ReverseMarkdownConversionStep(
                                            reverseMarkdownConfig))
                               .AddStep(new ReverseMarkdownPostprocessingStep())
                               .AddStep(new AttachmentLookupStep(blogPostAttachmentService))
                               .AddStep(new CategoryLookupStep(categories))
                               .AddStep(new SaveMarkdownStep(textFormatter, false))
                               .AddStep(new CreateBlogArchivePagesStep())
                               .Build();

                pipeline.Execute();

                convertedPostCount++;
            });

            Console.WriteLine($"Posts converted: {convertedPostCount}");
        }
Example #14
0
        public virtual OxiteModel BlogMLSave(Area areaInput, string slugPattern, User currentUser)
        {
            //TODO: (erikpo) Change this to be user selectable in the multiple blogs case if the user is a site owner
            Area area = areaService.GetArea("Blog");

            if (area == null)
            {
                return(null);
            }

            ValidationStateDictionary validationState = new ValidationStateDictionary();
            XmlTextReader             reader          = null;
            bool modifiedSite = false;

            try
            {
                reader = new XmlTextReader(Request.Files[0].InputStream);

                BlogMLBlog blog     = BlogMLSerializer.Deserialize(reader);
                Language   language = languageService.GetLanguage(site.LanguageDefault);

                area.Description = blog.SubTitle;
                area.DisplayName = blog.Title;
                area.Created     = blog.DateCreated;
                areaService.EditArea(area, out validationState);

                if (!site.HasMultipleAreas)
                {
                    site.DisplayName = area.DisplayName;
                    site.Description = area.Description;

                    siteService.EditSite(site, out validationState);

                    if (!validationState.IsValid)
                    {
                        throw new Exception();
                    }

                    modifiedSite = true;
                }

                postService.RemoveAll(area);

                foreach (BlogMLPost blogMLPost in blog.Posts)
                {
                    if (string.IsNullOrEmpty(blogMLPost.Title) || string.IsNullOrEmpty(blogMLPost.Content.Text))
                    {
                        continue;
                    }

                    Post post = blogMLPost.ToPost(blog, currentUser, slugPattern);

                    postService.AddPost(post, currentUser, false, out validationState, out post);

                    if (!validationState.IsValid)
                    {
                        throw new Exception();
                    }

                    foreach (BlogMLComment blogMLComment in blogMLPost.Comments)
                    {
                        Comment comment = blogMLComment.ToComment(blog, currentUser, language);

                        postService.ValidateComment(comment, out validationState);

                        if (validationState.IsValid)
                        {
                            postService.AddCommentWithoutMessages(area, post, comment, comment.Creator, false, out validationState, out comment);

                            if (!validationState.IsValid)
                            {
                                throw new Exception();
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ModelState.AddModelErrors(validationState);

                if (!string.IsNullOrEmpty(ex.Message))
                {
                    ModelState.AddModelError("ModelName", ex);
                }

                return(BlogML(areaInput));
            }
            finally
            {
                if (reader != null)
                {
                    reader.Close();
                }
            }

            if (modifiedSite)
            {
                OxiteApplication.Load(ControllerContext.HttpContext);
            }

            return(new OxiteModel {
                Container = area
            });
        }
Example #15
0
        public virtual OxiteViewModel ImportSave(Blog blog, string slugPattern)
        {
            if (blog == null)
            {
                return(null);
            }

            ValidationStateDictionary validationState = new ValidationStateDictionary();
            XmlTextReader             reader          = null;
            bool modifiedSite = false;

            try
            {
                reader = new XmlTextReader(Request.Files[0].InputStream);

                BlogMLBlog         blogMLBlog = BlogMLSerializer.Deserialize(reader);
                Language           language   = languageService.GetLanguage(context.Site.LanguageDefault);
                BlogInputForImport blogInput  = new BlogInputForImport(blogMLBlog.SubTitle, blogMLBlog.SubTitle, blogMLBlog.DateCreated);
                ModelResult <Blog> results    = blogService.EditBlog(blog, blogInput);

                if (!results.IsValid)
                {
                    ModelState.AddModelErrors(results.ValidationState);

                    return(Import(blog));
                }

                if (!context.Site.HasMultipleBlogs)
                {
                    Site site = siteService.GetSite();

                    site.DisplayName = blog.DisplayName;
                    site.Description = blog.Description;

                    siteService.EditSite(site, out validationState);

                    if (!validationState.IsValid)
                    {
                        throw new Exception();
                    }

                    modifiedSite = true;
                }

                postService.RemoveAll(blog);

                foreach (BlogMLPost blogMLPost in blogMLBlog.Posts)
                {
                    if (string.IsNullOrEmpty(blogMLPost.Title) || string.IsNullOrEmpty(blogMLPost.Content.Text))
                    {
                        continue;
                    }

                    PostInputForImport postInput      = blogMLPost.ToImportPostInput(blogMLBlog, context.Site.CommentingDisabled | blog.CommentingDisabled, slugPattern, blogMLPost.Approved ? EntityState.Normal : EntityState.PendingApproval, context.User.Cast <User>());
                    ModelResult <Post> addPostResults = postService.AddPost(blog, postInput);

                    if (!addPostResults.IsValid)
                    {
                        ModelState.AddModelErrors(addPostResults.ValidationState);

                        return(Import(blog));
                    }

                    foreach (BlogMLComment blogMLComment in blogMLPost.Comments)
                    {
                        CommentInputForImport     commentInput      = blogMLComment.ToImportCommentInput(blogMLBlog, context.User.Cast <User>(), language);
                        ModelResult <PostComment> addCommentResults = commentService.AddComment(addPostResults.Item, commentInput);

                        if (!addCommentResults.IsValid)
                        {
                            ModelState.AddModelErrors(addCommentResults.ValidationState);

                            return(Import(blog));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ModelState.AddModelErrors(validationState);

                if (!string.IsNullOrEmpty(ex.Message))
                {
                    ModelState.AddModelError("ModelName", ex);
                }

                return(Import(blog));
            }
            finally
            {
                if (reader != null)
                {
                    reader.Close();
                }
            }

            if (modifiedSite)
            {
                OxiteApplication.Load(ControllerContext.HttpContext);
            }

            return(new OxiteViewModel {
                Container = blog
            });
        }
Example #16
0
        public void ImportBlog(Stream stream)
        {
            var importedBlog = BlogMLSerializer.Deserialize(stream);

            ImportBlog(importedBlog);
        }
Example #17
0
        public IEnumerable <TaskStep> Execute(Dictionary <string, object> properties)
        {
            var inputFile = (string)properties["inputFile"];

            if (!File.Exists(inputFile))
            {
                throw new ArgumentException("The file: '{0}' does not exist.");
            }

            var progress = 0;

            yield return(new TaskStep(progress++, "Input file '{0}' found", inputFile));

            using (var reader = new StreamReader(inputFile))
            {
                yield return(new TaskStep(progress++, "Deserializing input file into BlogML object model"));

                var blog = BlogMLSerializer.Deserialize(reader);

                yield return(new TaskStep(progress++, "Successfully deserialized BlogML object model from input file"));

                yield return(new TaskStep(progress++, "Blog posts found: {0}", blog.Posts.Count));

                var remainingProgress = 100 - progress;
                var postCount         = blog.Posts.Count;
                var postIndex         = 0;

                foreach (var post in blog.Posts)
                {
                    postIndex++;
                    progress = (int)(((double)postIndex / postCount) * (double)remainingProgress);

                    var entry = new Entry();
                    entry.Author              = authenticator.GetName();
                    entry.HideChrome          = false;
                    entry.IsDiscussionEnabled = true;
                    entry.Status              = post.PostType == BlogPostTypes.Article ? EntryStatus.PublicPage : EntryStatus.PublicBlog;
                    entry.Title           = entry.MetaTitle = NoLongerThan(200, post.Title);
                    entry.Published       = post.DateCreated < DateTime.Today.AddYears(-100) ? DateTime.UtcNow : post.DateCreated;
                    entry.Summary         = post.HasExcerpt ? NoLongerThan(500, StripHtml(post.Excerpt.UncodedText)) : "";
                    entry.MetaDescription = post.HasExcerpt ? NoLongerThan(200, StripHtml(post.Excerpt.UncodedText)) : NoLongerThan(200, StripHtml(post.Content.UncodedText));
                    entry.Name            = NoLongerThan(100, (post.PostUrl ?? post.PostName ?? post.ID).Trim('/'));

                    // Ensure this post wasn't already imported
                    var existing = repository.FindFirstOrDefault(new EntryByNameQuery(entry.Name));
                    if (existing != null)
                    {
                        yield return(new TaskStep(progress, "Did NOT import post '{0}', because a post by this name already exists", entry.Name));

                        continue;
                    }

                    var revision = entry.Revise();
                    revision.Author = authenticator.GetName();
                    revision.Body   = post.Content.UncodedText;
                    revision.Format = Formats.Html;
                    revision.Reason = "Imported from BlogML";

                    foreach (BlogMLComment comment in post.Comments)
                    {
                        var newComment = entry.Comment();
                        newComment.AuthorEmail = NoLongerThan(100, comment.UserEMail);
                        newComment.AuthorName  = NoLongerThan(100, comment.UserName);
                        newComment.AuthorUrl   = NoLongerThan(100, comment.UserUrl);
                        newComment.IsSpam      = !comment.Approved;
                        newComment.Posted      = comment.DateCreated < DateTime.Today.AddYears(-100) ? DateTime.UtcNow : comment.DateCreated;
                        newComment.Body        = comment.Content.UncodedText;
                    }

                    foreach (BlogMLCategoryReference categoryRef in post.Categories)
                    {
                        var category = blog.Categories.FirstOrDefault(x => x.ID == categoryRef.Ref);
                        if (category == null)
                        {
                            continue;
                        }

                        var tagName = new string(
                            (category.Title ?? string.Empty)
                            .ToLowerInvariant()
                            .Select(x => char.IsLetterOrDigit(x) ? x : '-')
                            .ToArray());

                        if (string.IsNullOrEmpty(tagName))
                        {
                            continue;
                        }

                        var existingTag = repository.FindFirstOrDefault(new SearchTagsByNameQuery(tagName));
                        if (existingTag == null)
                        {
                            existingTag = new Tag {
                                Name = tagName
                            };
                            repository.Add(existingTag);
                        }

                        existingTag.Add(entry);
                    }

                    repository.Add(entry);

                    yield return(new TaskStep(progress, "Imported post '{0}'", entry.Name));
                }
            }
            yield break;
        }
Example #18
0
        /// <summary>
        /// Converts the blog export to BlogML
        /// </summary>
        public void Convert()
        {
            XDocument blogger = XDocument.Load(_bloggerExportPath);

            // extract basic information
            string   blogTitle   = blogger.Element(AtomNamespace + "feed").Element(AtomNamespace + "title").Value;
            DateTime blogUpdated = DateTime.Parse(blogger.Element(AtomNamespace + "feed").Element(AtomNamespace + "updated").Value).ToUniversalTime();
            string   authorName  = blogger.Element(AtomNamespace + "feed").Element(AtomNamespace + "author").Element(AtomNamespace + "name").Value;
            string   authorUri   = blogger.Element(AtomNamespace + "feed").Element(AtomNamespace + "author").Element(AtomNamespace + "uri").Value;
            string   authorEmail = blogger.Element(AtomNamespace + "feed").Element(AtomNamespace + "author").Element(AtomNamespace + "email").Value;

            // assume the updated date and then hunt backwards in time
            DateTime blogCreated = blogUpdated;

            LogMessage(string.Format(CultureInfo.CurrentCulture,
                                     Properties.Resources.Converter_MessageBlogTitle,
                                     blogTitle,
                                     blogUpdated));

            LogMessage(string.Format(CultureInfo.CurrentCulture,
                                     Properties.Resources.Converter_MessageBlogAuthor,
                                     authorName,
                                     authorEmail,
                                     authorUri));

            // parse ATOM entries
            var query = from entry in blogger.Descendants(AtomNamespace + "entry")
                        select new
            {
                Id          = entry.Element(AtomNamespace + "id").Value,
                Published   = DateTime.Parse(entry.Element(AtomNamespace + "published").Value).ToUniversalTime(),
                Updated     = DateTime.Parse(entry.Element(AtomNamespace + "updated").Value).ToUniversalTime(),
                Title       = entry.Element(AtomNamespace + "title").Value,
                Content     = entry.Element(AtomNamespace + "content").Value,
                AuthorName  = entry.Element(AtomNamespace + "author").Element(AtomNamespace + "name").Value,
                AuthorUri   = entry.Element(AtomNamespace + "author").Element(AtomNamespace + "uri") == null ? null : entry.Element(AtomNamespace + "author").Element(AtomNamespace + "uri").Value,
                AuthorEmail = entry.Element(AtomNamespace + "author").Element(AtomNamespace + "email") == null ? null : entry.Element(AtomNamespace + "author").Element(AtomNamespace + "email").Value,
                InReplyTo   = entry.Element(ThrNamespace + "in-reply-to") == null ? null : entry.Element(ThrNamespace + "in-reply-to").Attribute("ref").Value,
                Categories  = (from category in entry.Descendants(AtomNamespace + "category")
                               select new
                {
                    Scheme = category.Attribute("scheme").Value,
                    Term = category.Attribute("term").Value,
                }),
                Links = (from link in entry.Descendants(AtomNamespace + "link")
                         select new
                {
                    Rel = link.Attribute("rel").Value,
                    Type = link.Attribute("type").Value,
                    Href = link.Attribute("href").Value,
                }),
            };

            // separate out the different export categories from the ATOM entries
            Dictionary <string, BlogMLPost> posts      = new Dictionary <string, BlogMLPost>();
            List <CommentWithInReplyTo>     comments   = new List <CommentWithInReplyTo>();
            List <BlogMLAuthor>             authors    = new List <BlogMLAuthor>();
            List <BlogMLCategory>           categories = new List <BlogMLCategory>();
            NameValueCollection             settings   = new NameValueCollection();
            string template = null;

            foreach (var q in query)
            {
                // update the blog created date as we find earlier entires
                if (q.Published < blogCreated)
                {
                    blogCreated = q.Published;
                }

                // create a content holder
                BlogMLContent content = new BlogMLContent();
                content.Text = q.Content;

                // find categories and the type of entry
                List <BlogMLCategoryReference> categoryRefs = new List <BlogMLCategoryReference>();
                string entryKind = null;

                // get the type of entry and any post categories
                foreach (var c in q.Categories)
                {
                    if (c.Scheme == "http://schemas.google.com/g/2005#kind")
                    {
                        entryKind = c.Term;
                    }
                    else if (c.Scheme == "http://www.blogger.com/atom/ns#")
                    {
                        BlogMLCategoryReference categoryRef = new BlogMLCategoryReference();
                        categoryRef.Ref = UpdateCategoriesGetRef(ref categories, c.Term, q.Published);
                        categoryRefs.Add(categoryRef);
                    }
                    else
                    {
                        // we've found a category scheme we don't know about
                        LogMessage(string.Format(CultureInfo.CurrentCulture,
                                                 Properties.Resources.Converter_MessageUnexpectedCategoryScheme,
                                                 c.Scheme));
                    }
                }

                // process entry based on the entry kind
                switch (entryKind)
                {
                case "http://schemas.google.com/blogger/2008/kind#template":
                    template = q.Content;
                    break;

                case "http://schemas.google.com/blogger/2008/kind#settings":
                    LogMessage(string.Format(CultureInfo.CurrentCulture,
                                             Properties.Resources.Converter_ImportingSettings,
                                             q.Id,
                                             q.Content));

                    settings.Add(q.Id, q.Content);
                    break;

                case "http://schemas.google.com/blogger/2008/kind#post":
                    LogMessage(string.Format(CultureInfo.CurrentCulture,
                                             Properties.Resources.Converter_ImportingPost,
                                             q.Title));

                    // get a reference to the author of this entry
                    BlogMLAuthorReference authorReference = new BlogMLAuthorReference();
                    authorReference.Ref = UpdateAuthorsGetRef(ref authors, q.AuthorName, q.AuthorEmail, q.Published);

                    BlogMLPost post = new BlogMLPost();
                    post.Approved = true;
                    post.Authors.Add(authorReference);
                    if (categoryRefs.Count > 0)
                    {
                        post.Categories.AddRange(categoryRefs);
                    }
                    post.Content      = content;
                    post.DateCreated  = q.Published;
                    post.DateModified = q.Updated;
                    post.HasExcerpt   = false;
                    post.ID           = q.Id;
                    post.PostType     = BlogPostTypes.Normal;
                    post.Title        = q.Title;

                    posts.Add(q.Id, post);
                    break;

                case "http://schemas.google.com/blogger/2008/kind#comment":
                    LogMessage(string.Format(CultureInfo.CurrentCulture,
                                             Properties.Resources.Converter_ImportingComment,
                                             q.Title));

                    BlogMLComment comment = new BlogMLComment();
                    comment.Approved     = true;
                    comment.Content      = content;
                    comment.DateCreated  = q.Published;
                    comment.DateModified = q.Updated;
                    comment.ID           = q.Id;
                    comment.Title        = q.Title;
                    comment.UserEMail    = q.AuthorEmail;
                    comment.UserName     = q.AuthorName;
                    comment.UserUrl      = q.AuthorUri;

                    comments.Add(new CommentWithInReplyTo(comment, q.InReplyTo));
                    break;

                default:
                    LogMessage(string.Format(CultureInfo.CurrentCulture,
                                             Properties.Resources.Converter_MessageUnexpectedEntryKind,
                                             entryKind));
                    break;
                }
            }

            // add comments to posts
            foreach (CommentWithInReplyTo comment in comments)
            {
                if (posts.ContainsKey(comment.InReplyTo))
                {
                    BlogMLPost post = posts[comment.InReplyTo];

                    LogMessage(string.Format(CultureInfo.CurrentCulture,
                                             Properties.Resources.Converter_AttachingComment,
                                             comment.Comment.Title,
                                             post.Title));

                    post.Comments.Add(comment.Comment);
                }
                else
                {
                    LogMessage(string.Format(CultureInfo.CurrentCulture,
                                             Properties.Resources.Converter_OrphanedComment,
                                             comment.Comment.Title));
                }
            }

            // build the blog
            LogMessage(Properties.Resources.Converter_BuildingBlogML);

            BlogMLBlog blog = new BlogMLBlog();

            blog.Authors.AddRange(authors);
            blog.Categories.AddRange(categories);
            blog.DateCreated = blogCreated;
            blog.Posts.AddRange(posts.Values);
            blog.Title = blogTitle;

            // add blogger settings as extended properties
            foreach (string name in settings.Keys)
            {
                Pair <string, string> pair = new Pair <string, string>();
                pair.Key   = name;
                pair.Value = settings[name];
                blog.ExtendedProperties.Add(pair);
            }

            // output BlogML
            LogMessage(string.Format(CultureInfo.CurrentCulture,
                                     Properties.Resources.Converter_WritingBlogML,
                                     _blogmlPath));

            XmlWriterSettings writerSettings = new XmlWriterSettings();

            writerSettings.CheckCharacters  = true;
            writerSettings.CloseOutput      = true;
            writerSettings.ConformanceLevel = ConformanceLevel.Document;
            writerSettings.Indent           = true;

            using (XmlWriter writer = XmlWriter.Create(_blogmlPath, writerSettings))
            {
                BlogMLSerializer.Serialize(writer, blog);
            }
        }