public async Task ShouldLoadEntryIntoHtmlFile()
        {
            var projectInfo = this.TestContext.ShouldGetProjectDirectoryInfo(this.GetType());

            var blobContainerName = this.TestContext.Properties["blobContainerName"].ToString();
            var htmlFile          = this.TestContext.Properties["htmlFile"].ToString();

            htmlFile = Path.Combine(projectInfo.FullName, htmlFile);
            var slug = this.TestContext.Properties["slug"].ToString();

            var container = cloudStorageAccountClassic.CreateCloudBlobClient().GetContainerReference(blobContainerName);
            var keys      = new AzureBlobKeys();

            keys.Add <BlogEntry>(i => i.Slug);

            var repository = new BlogRepository(keys, container);
            var blogEntry  = await repository.LoadSingleAsync <BlogEntry>(slug);

            var h2          = new XElement("h2", blogEntry.Title);
            var contentHtml = string.Format("<content>{0}</content>", blogEntry.Content);

            contentHtml = HtmlUtility.ConvertToXml(contentHtml);
            var content = XElement.Parse(contentHtml);

            var html = File.ReadAllText(htmlFile);
            var xDoc = XDocument.Parse(html);
            var body = xDoc.Root.Element("body");

            body.Value = string.Empty;
            body.Add(h2);
            body.Add(content.Elements());

            File.WriteAllText(htmlFile, xDoc.ToString());
        }
예제 #2
0
        /// <summary>
        /// Reduces <see cref="BlogEntry.Content"/> to an extract.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <returns></returns>
        public static BlogEntry WithContentAsExtract(this BlogEntry data)
        {
            if (data == null)
            {
                return(null);
            }

            #region functional members:

            Func <string, string> getExtract = content =>
            {
                if (string.IsNullOrEmpty(content))
                {
                    return(null);
                }

                traceSource?.TraceVerbose("Getting Blog Entry Content...");
                traceSource?.TraceVerbose("Getting Blog Entry Slug: {0}", data.Slug);

                var limit = default(int);

                try
                {
                    limit = 255;
                    if (XmlUtility.IsXml(content))
                    {
                        content = HtmlUtility.ConvertToXml(content);
                        content = content.Replace("&nbsp;", string.Empty); // TODO: this should be in HtmlUtility.ConvertToXml().
                        var rootElement = XElement.Parse(string.Format("<root>{0}</root>", content));
                        content = XObjectUtility.JoinFlattenedXTextNodes(rootElement);
                    }
                }
                catch (Exception ex)
                {
                    traceSource?.TraceError(ex);
                }

                return((content.Length > limit) ? string.Format("{0}...", content.Trim().Substring(0, limit - 1)) : content);
            };

            #endregion

            data.Content = getExtract(data.Content);

            return(data);
        }
예제 #3
0
        public async Task ShouldExpandUris()
        {
            var projectDirectoryInfo = this.TestContext.ShouldGetProjectDirectoryInfo(this.GetType());

            #region test properties:

            var jsonPath = this.TestContext.Properties["jsonPath"].ToString();
            jsonPath = projectDirectoryInfo.ToCombinedPath(jsonPath);
            this.TestContext.ShouldFindFile(jsonPath);

            var twitterHost = this.TestContext.Properties["twitterHost"].ToString();

            #endregion

            var json      = File.ReadAllText(jsonPath);
            var blogEntry = JsonConvert.DeserializeObject <BlogEntry>(json);
            Assert.IsNotNull(blogEntry, "The expected Blog Entry is not here.");

            var xml = HtmlUtility.ConvertToXml(blogEntry.Content);
            var xd  = XDocument.Parse(string.Format("<content>{0}</content>", xml));

            var twitterAnchors = xd.Descendants("a")
                                 .Where(i => i.Value.Contains(string.Format("://{0}/", twitterHost)));
            Assert.IsTrue(twitterAnchors.Any(), string.Format("The expected {0} anchors are not here.", twitterHost));

            var tasks = twitterAnchors.Select(async i =>
            {
                var uri = new Uri(i.Attribute("href").Value, UriKind.Absolute);
                this.TestContext.WriteLine("wrapped uri: {0}", uri);

                var response = await httpClient.GetAsync(uri);
                this.TestContext.WriteLine("HttpStatusCode: {0}", response.StatusCode);
                Assert.IsTrue(response.IsMovedOrRedirected(), "The expected status code is not here.");

                var expandedUri = response.Headers.Location;
                this.TestContext.WriteLine("expanded URI: {0}", expandedUri);

                i.ExpandTwitterAnchor(expandedUri.OriginalString, twitterHost);
            });
            await Task.WhenAll(tasks);

            var xhtml = xd.ToString().Replace(@"</a><a", "</a> <a");

            this.TestContext.WriteLine("XHTML:\n{0}", xhtml);
        }
        public async Task ShouldGenerateBlogEntry()
        {
            var projectInfo    = this.TestContext.ShouldGetProjectDirectoryInfo(this.GetType());
            var webProjectInfo = this.TestContext.ShouldGetConventionalProjectDirectoryInfo(this.GetType());

            #region test properties:

            var blobContainerName  = this.TestContext.Properties["blobContainerName"].ToString();
            var entryHeaderElement = this.TestContext.Properties["entryHeaderElement"].ToString();

            var entryPath = this.TestContext.Properties["entryPath"].ToString();
            entryPath = projectInfo.ToCombinedPath(entryPath);
            this.TestContext.ShouldFindFile(entryPath);

            var entryOutputPath = this.TestContext.Properties["entryOutputPath"].ToString();
            entryOutputPath = projectInfo.ToCombinedPath(entryOutputPath);
            this.TestContext.ShouldFindFile(entryOutputPath);

            #endregion

            var container = cloudStorageAccountClassic.CreateCloudBlobClient().GetContainerReference(blobContainerName);
            var keys      = new AzureBlobKeys();
            keys.Add <BlogEntry>(i => i.Slug);

            var repository = new BlogRepository(keys, container);

            var entry = File.ReadAllText(entryPath);
            entry = HtmlUtility.ConvertToXml(entry);
            var xdEntry = XDocument.Parse(entry);
            var body    = xdEntry.Root.Element("body");
            Assert.IsNotNull(body, "The expected body is not here.");

            var title   = body.Element(entryHeaderElement).GetInnerXml();
            var content = new XElement("body",
                                       body
                                       .Elements()
                                       .Where(i => !i.Name.LocalName.Equals(entryHeaderElement))
                                       ).GetInnerXml();

            var blogEntry = (new BlogEntry
            {
                Content = content,
                InceptDate = DateTime.Now,
                IsPublished = true,
                Title = title
            }).WithDefaultSlug();

            if (await repository.HasEntityAsync <BlogEntry>(blogEntry.Slug))
            {
                var previousEntry = await repository.LoadSingleAsync <BlogEntry>(blogEntry.Slug);

                Assert.IsNotNull(previousEntry, "The expected previous entry is not here.");
                blogEntry.InceptDate = previousEntry.InceptDate;
            }

            await repository.SaveEntityAsync(blogEntry);

            Assert.IsTrue(await repository.HasEntityAsync <BlogEntry>(blogEntry.Slug), "The expected Blog Entry is not in the Repository.");
            var json = blogEntry.ToJson(useJavaScriptCase: true);
            File.WriteAllText(entryOutputPath, json);
        }
예제 #5
0
        public async Task ShouldExpandUrisFromNewEntry()
        {
            var projectDirectoryInfo = this.TestContext.ShouldGetProjectDirectoryInfo(this.GetType());

            #region test properties:

            var htmlPath = this.TestContext.Properties["htmlPath"].ToString();
            htmlPath = projectDirectoryInfo.ToCombinedPath(htmlPath);
            this.TestContext.ShouldFindFile(htmlPath);

            var twitterHost   = this.TestContext.Properties["twitterHost"].ToString();
            var hootsuiteHost = this.TestContext.Properties["hootsuiteHost"].ToString();

            #endregion

            #region functional members:

            string expandAmpersand(string s)
            {
                s = s
                    .Replace(" & ", " &amp; ")
                    .Replace("Q&A", "Q&amp;A")
                    .Replace("R&B", "R&amp;B")
                ;

                return(s);
            }

            string expandArrows(string s)
            {
                s = s
                    .Replace("<<", "&lt;&lt;")
                    .Replace(">>", "&gt;&gt;")
                    .Replace("=>", "=&gt;")
                    .Replace("<=", "&lt;=")
                    .Replace("->", "-&gt;")
                    .Replace("<-", "&lt;-")
                    .Replace("<Action<", "&lt;Action&lt;")
                    .Replace("<Func<", "&lt;Func&lt;")
                    .Replace("Action<", "Action&lt;")
                    .Replace("Func<", "Func&lt;")
                ;
                return(s);
            }

            string expandComparisonOperatorGlyphs(string s)
            {
                s = s
                    .Replace(" < ", " &lt; ")
                    .Replace(" > ", " &gt; ")
                ;
                return(s);
            }

            string expandGenericNotation(string s)
            {
                s = s.Replace("<T>", "&lt;T&gt;");
                return(s);
            }

            async Task <Uri> expandUri(Uri expandableUri)
            {
                var messageBuilder = new StringBuilder();

                messageBuilder.AppendLine($"expanding wrapped URI: {expandableUri}...");

                var response = await httpClient.GetAsync(expandableUri);

                messageBuilder.AppendLine($"HttpStatusCode: {response.StatusCode}");
                if (response.IsMovedOrRedirected())
                {
                    var expandedUri = response.Headers.Location;
                    messageBuilder.AppendLine($"expanded URI: {expandedUri}");

                    this.TestContext.WriteLine(messageBuilder.ToString());

                    return(expandedUri);
                }
                else
                {
                    messageBuilder.AppendLine($"WARNING: the expected status code is not here. Headers location: {response.Headers.Location}");

                    this.TestContext.WriteLine(messageBuilder.ToString());

                    return(expandableUri);
                }
            }

            bool isHost(string context, string host) => context.Contains($"://{host}/");

            async Task processTwitterAnchorsAsync(XDocument xDocument)
            {
                this.TestContext.WriteLine("looking for Twitter anchors of the form <a*>*://{0}//*</a>...", twitterHost);
                var twitterAnchors = xDocument.Descendants("a").Where(i => isHost(i.Value, twitterHost));

                var tasks = twitterAnchors.Select(async i =>
                {
                    var uri         = new Uri(i.Attribute("href").Value, UriKind.Absolute);
                    var expandedUri = await expandUri(uri);
                    if (isHost(expandedUri?.OriginalString, hootsuiteHost))
                    {
                        expandedUri = await expandUri(expandedUri);
                    }

                    i.ExpandTwitterAnchor(expandedUri.OriginalString, twitterHost);
                });
                await Task.WhenAll(tasks);
            }

            string removeAnchorLineBreaks(string s)
            {
                var regexes = new[]
                {
                    new Regex(@"(\r\n?\s*)<a "),
                    new Regex(@"</a>\s*(\r\n?\s*)")
                };

                regexes.ForEachInEnumerable(re =>
                {
                    s = re.Replace(s, match =>
                    {
                        if (match.Groups.Count() != 2)
                        {
                            return(match.Value);
                        }
                        return(match.Value.Replace(match.Groups[1].Value, " "));
                    });
                });

                return(s);
            }

            string removeImageLineBreaks(string s)
            {
                var re = new Regex(@"(\r\n?\s*)(<img [^>]+/>)\s*(\r\n?\s*)");

                s = re.Replace(s, match =>
                {
                    if (match.Groups.Count() != 4)
                    {
                        return(match.Value);
                    }
                    return($" {match.Groups[2].Value} ");
                });

                return(s);
            }

            string removeNewLineAndSpaceAfterParagraphElement(string s)
            {
                var re = new Regex(@"(\<p\>)\r\n\s+");

                re.Matches(s).OfType <Match>().ForEachInEnumerable(i =>
                {
                    s = s.Replace(i.Value, i.Groups[1].Value);
                });

                return(s);
            }

            string removeNonBreakingSpace(string s)
            {
                s = s.Replace("&nbsp;", " ");

                return(s);
            }

            #endregion

            var html = File.ReadAllText(htmlPath);
            html = expandAmpersand(html);
            html = expandArrows(html);
            html = expandComparisonOperatorGlyphs(html);
            html = expandGenericNotation(html);
            html = removeNonBreakingSpace(html);
            html = HtmlUtility.ConvertToXml(html);

            var xd = XDocument.Parse(html);
            await processTwitterAnchorsAsync(xd);

            html = xd.ToString();
            html = removeAnchorLineBreaks(html);
            html = removeImageLineBreaks(html);
            html = removeNewLineAndSpaceAfterParagraphElement(html);
            File.WriteAllText(htmlPath, html);
        }