/// <summary>
        /// Asynchronously download all of the release notes provided to this function and convert them to HTML
        /// </summary>
        /// <param name="items">List of items that you want to display in the release notes</param>
        /// <param name="latestVersion">The latest version (most current version) of your releases</param>
        /// <param name="cancellationToken">Token to cancel the async download requests</param>
        /// <returns>The release notes formatted as HTML and ready to display to the user</returns>
        public virtual async Task <string> DownloadAllReleaseNotes(List <AppCastItem> items, AppCastItem latestVersion, CancellationToken cancellationToken)
        {
            _sparkle.LogWriter.PrintMessage("Preparing to initialize release notes...");
            StringBuilder sb = new StringBuilder(InitialHTML);

            foreach (AppCastItem castItem in items)
            {
                _sparkle.LogWriter.PrintMessage("Initializing release notes for {0}", castItem.Version);
                // TODO: could we optimize this by doing multiple downloads at once?
                var releaseNotes = await GetReleaseNotes(castItem, _sparkle, cancellationToken);

                sb.Append(string.Format(ReleaseNotesTemplate,
                                        castItem.Version,
                                        castItem.PublicationDate.ToString("D"), // was dd MMM yyyy
                                        releaseNotes,
                                        latestVersion.Version.Equals(castItem.Version) ? "#ABFF82" : "#AFD7FF"));
            }
            sb.Append("</body></html>");

            _sparkle.LogWriter.PrintMessage("Done initializing release notes!");
            return(sb.ToString());
        }
        /// <summary>
        /// Grab the release notes for the given item and return their release notes
        /// in HTML format so that they can be displayed to the user.
        /// </summary>
        /// <param name="item"><see cref="AppCastItem"/>item to download the release notes for</param>
        /// <param name="sparkle"><see cref="SparkleUpdater"/> that can be used for logging information
        /// about the release notes grabbing process (or its failures)</param>
        /// <param name="cancellationToken">token that can be used to cancel a release notes
        /// grabbing operation</param>
        /// <returns>The release notes, formatted as HTML, for a given release of the software</returns>
        protected virtual async Task <string> GetReleaseNotes(AppCastItem item, SparkleUpdater sparkle, CancellationToken cancellationToken)
        {
            string criticalUpdate = item.IsCriticalUpdate ? "Critical Update" : "";

            // at first try to use embedded description
            if (!string.IsNullOrEmpty(item.Description))
            {
                // check for markdown
                var containsHtmlRegex = new Regex(@"<\s*([^ >]+)[^>]*>.*?<\s*/\s*\1\s*>");
                if (containsHtmlRegex.IsMatch(item.Description))
                {
                    if (item.IsCriticalUpdate)
                    {
                        item.Description = "<p><em>" + criticalUpdate + "</em></p>" + "<br/>" + item.Description;
                    }
                    return(item.Description);
                }
                else
                {
                    var md = new MarkdownSharp.Markdown();
                    if (item.IsCriticalUpdate)
                    {
                        item.Description = "*" + criticalUpdate + "*" + "\n\n" + item.Description;
                    }
                    var temp = md.Transform(item.Description);
                    return(temp);
                }
            }

            // not embedded so try to release notes from the link
            if (string.IsNullOrEmpty(item.ReleaseNotesLink))
            {
                return(null);
            }

            // download release notes
            sparkle.LogWriter.PrintMessage("Downloading release notes for {0} at {1}", item.Version, item.ReleaseNotesLink);
            string notes = await DownloadReleaseNotes(item.ReleaseNotesLink, cancellationToken, sparkle);

            sparkle.LogWriter.PrintMessage("Done downloading release notes for {0}", item.Version);
            if (string.IsNullOrEmpty(notes))
            {
                return(null);
            }

            // check dsa of release notes
            if (!string.IsNullOrEmpty(item.ReleaseNotesSignature))
            {
                if (ChecksReleaseNotesSignature &&
                    _sparkle.SignatureVerifier != null &&
                    Utilities.IsSignatureNeeded(_sparkle.SignatureVerifier.SecurityMode, _sparkle.SignatureVerifier.HasValidKeyInformation(), false) &&
                    sparkle.SignatureVerifier.VerifySignatureOfString(item.ReleaseNotesSignature, notes) == ValidationResult.Invalid)
                {
                    return(null);
                }
            }

            // process release notes
            var extension = Path.GetExtension(item.ReleaseNotesLink);

            if (extension != null && MarkdownExtensions.Contains(extension.ToLower()))
            {
                try
                {
                    var md = new MarkdownSharp.Markdown();
                    if (item.IsCriticalUpdate)
                    {
                        notes = "*" + criticalUpdate + "*" + "\n\n" + notes;
                    }
                    notes = md.Transform(notes);
                }
                catch (Exception ex)
                {
                    sparkle.LogWriter.PrintMessage("Error parsing Markdown syntax: {0}", ex.Message);
                }
            }
            return(notes);
        }
        public virtual async Task <string> DownloadAllReleaseNotesWithButtons(List <AppCastItem> items, AppCastItem latestVersion, CancellationToken cancellationToken)
        {
            _sparkle.LogWriter.PrintMessage("Preparing to initialize release notes...");
            StringBuilder sb = new StringBuilder(_initialHTML);


            foreach (AppCastItem castItem in items)
            {
                StringBuilder releasesAllBlocksHtml = new StringBuilder(_initialHTML);
                releasesAllBlocksHtml.Append(Texts.rm.GetString("RELEASENOTESGRABBERREQUERIMENTS", Texts.cultereinfo) + "<br>");

                if (!String.IsNullOrEmpty(castItem.AndroidVersionMinimum))
                {
                    releasesAllBlocksHtml.Append("<br>" + Texts.rm.GetString("RELEASENOTESGRABBERMINIMUMANDROIDDISPLAYBUTTONS", Texts.cultereinfo) + castItem.AndroidVersionMinimum);
                }
                if (!String.IsNullOrEmpty(castItem.AndroidVersionMaximum))
                {
                    releasesAllBlocksHtml.Append("<br>" + Texts.rm.GetString("RELEASENOTESGRABBERMAXIMUMANDROIDDISPLAYBUTTONS", Texts.cultereinfo) + castItem.AndroidVersionMaximum);
                }
                if (!String.IsNullOrEmpty(castItem.AndroidVersion))
                {
                    releasesAllBlocksHtml.Append("<br>" + Texts.rm.GetString("RELEASENOTESGRABBERANDROIDDISPLAYBUTTONS", Texts.cultereinfo) + castItem.AndroidVersionMinimum);
                }
                releasesAllBlocksHtml.Append("<button onclick=\"window.external.Test('" + castItem.DownloadSignature + "')\">" + Texts.rm.GetString("RELEASENOTESGRABBERDOWNLOADS", Texts.cultereinfo) + "</button>");


                _sparkle.LogWriter.PrintMessage("Initializing release notes for {0}", castItem.Version);
                // TODO: could we optimize this by doing multiple downloads at once?
                var releaseNotes = await GetReleaseNotes(castItem, _sparkle, cancellationToken);

                sb.Append(string.Format(_separatorTemplate,
                                        castItem.Version,
                                        castItem.PublicationDate.ToString("D"), // was dd MMM yyyy
                                        releaseNotes,
                                        latestVersion.Version.Equals(castItem.Version) ? "#ABFF82" : "#AFD7FF", releasesAllBlocksHtml));
            }
            sb.Append("</body></html>");

            _sparkle.LogWriter.PrintMessage("Done initializing release notes!");
            return(sb.ToString());
        }