public DisplayPackageViewModel Setup( DisplayPackageViewModel viewModel, Package package, IReadOnlyCollection <Package> allVersions, User currentUser, IReadOnlyDictionary <int, PackageDeprecation> packageKeyToDeprecation, IReadOnlyList <PackageRename> packageRenames, RenderedMarkdownResult readmeResult) { _listPackageItemViewModelFactory.Setup(viewModel, package, currentUser); SetupCommon(viewModel, package, pushedBy: null, packageKeyToDeprecation: packageKeyToDeprecation); return(SetupInternal(viewModel, package, allVersions, currentUser, packageKeyToDeprecation, packageRenames, readmeResult)); }
/// <summary> /// Get the converted HTML from the stored ReadMe markdown. /// </summary> /// <param name="package">Package entity associated with the ReadMe.</param> /// <returns>ReadMe converted to HTML.</returns> public async Task <RenderedMarkdownResult> GetReadMeHtmlAsync(Package package) { var readMeMd = await GetReadMeMdAsync(package); var result = new RenderedMarkdownResult { Content = readMeMd, ImagesRewritten = false }; return(string.IsNullOrEmpty(readMeMd) ? result : _markdownService.GetHtmlFromMarkdown(readMeMd)); }
/// <summary> /// Get the converted HTML from the package with Readme markdown. /// </summary> /// <param name="readmeFileName">The path of Readme markdown.</param> /// <param name="packageArchiveReader"> /// The <see cref="PackageArchiveReader"/> instance providing the package metadata. /// </param> /// <returns>ReadMe converted to HTML.</returns> public async Task <RenderedMarkdownResult> GetReadMeHtmlAsync(string readmeFileName, PackageArchiveReader packageArchiveReader, Encoding encoding) { var readmeMd = await GetReadMeMdAsync(readmeFileName, packageArchiveReader, encoding); var result = new RenderedMarkdownResult { Content = readmeMd, ImagesRewritten = false }; return(string.IsNullOrEmpty(readmeMd) ? result : _markdownService.GetHtmlFromMarkdown(readmeMd)); }
public DisplayPackageViewModel Create( Package package, IReadOnlyCollection <Package> allVersions, User currentUser, IReadOnlyDictionary <int, PackageDeprecation> packageKeyToDeprecation, IReadOnlyList <PackageRename> packageRenames, RenderedMarkdownResult readmeResult) { var viewModel = new DisplayPackageViewModel(); return(Setup( viewModel, package, allVersions, currentUser, packageKeyToDeprecation, packageRenames, readmeResult)); }
public RenderedMarkdownResult GetHtmlFromMarkdown(string markdownString, int incrementHeadersBy) { var output = new RenderedMarkdownResult() { ImagesRewritten = false, Content = "" }; var readmeWithoutBom = markdownString.StartsWith("\ufeff") ? markdownString.Replace("\ufeff", "") : markdownString; // HTML encode markdown, except for block quotes, to block inline html. var encodedMarkdown = EncodedBlockQuotePattern.Replace(HttpUtility.HtmlEncode(readmeWithoutBom), "> "); var settings = CommonMarkSettings.Default.Clone(); settings.RenderSoftLineBreaksAsLineBreaks = true; // Parse executes CommonMarkConverter's ProcessStage1 and ProcessStage2. var document = CommonMarkConverter.Parse(encodedMarkdown, settings); foreach (var node in document.AsEnumerable()) { if (node.IsOpening) { var block = node.Block; if (block != null) { switch (block.Tag) { // Demote heading tags so they don't overpower expander headings. case BlockTag.AtxHeading: case BlockTag.SetextHeading: var level = (byte)Math.Min(block.Heading.Level + incrementHeadersBy, 6); block.Heading = new HeadingData(level); break; // Decode preformatted blocks to prevent double encoding. // Skip BlockTag.BlockQuote, which are partially decoded upfront. case BlockTag.FencedCode: case BlockTag.IndentedCode: if (block.StringContent != null) { var content = block.StringContent.TakeFromStart(block.StringContent.Length); var unencodedContent = HttpUtility.HtmlDecode(content); block.StringContent.Replace(unencodedContent, 0, unencodedContent.Length); } break; } } var inline = node.Inline; if (inline != null) { if (inline.Tag == InlineTag.Link) { // Allow only http or https links in markdown. Transform link to https for known domains. if (!PackageHelper.TryPrepareUrlForRendering(inline.TargetUrl, out string readyUriString)) { inline.TargetUrl = string.Empty; } else { inline.TargetUrl = readyUriString; } } else if (inline.Tag == InlineTag.Image) { if (!PackageHelper.TryPrepareUrlForRendering(inline.TargetUrl, out string readyUriString, rewriteAllHttp: true)) { inline.TargetUrl = string.Empty; } else { output.ImagesRewritten = output.ImagesRewritten || (inline.TargetUrl != readyUriString); inline.TargetUrl = readyUriString; } } } } }
private DisplayPackageViewModel SetupInternal( DisplayPackageViewModel viewModel, Package package, IReadOnlyCollection <Package> allVersions, User currentUser, IReadOnlyDictionary <int, PackageDeprecation> packageKeyToDeprecation, IReadOnlyDictionary <int, IReadOnlyList <PackageVulnerability> > packageKeyToVulnerabilities, IReadOnlyList <PackageRename> packageRenames, RenderedMarkdownResult readmeResult) { var dependencies = package.Dependencies.ToList(); viewModel.Dependencies = new DependencySetsViewModel(dependencies); var packageHistory = allVersions .OrderByDescending(p => new NuGetVersion(p.Version)) .ToList(); var pushedByCache = new Dictionary <User, string>(); viewModel.PackageVersions = packageHistory .Select( p => { var vm = new DisplayPackageViewModel(); _listPackageItemViewModelFactory.Setup(vm, p, currentUser); return(SetupCommon(vm, p, GetPushedBy(p, currentUser, pushedByCache), packageKeyToDeprecation, packageKeyToVulnerabilities)); }) .ToList(); viewModel.PushedBy = GetPushedBy(package, currentUser, pushedByCache); viewModel.PackageFileSize = package.PackageFileSize; viewModel.LatestSymbolsPackage = package.LatestSymbolPackage(); viewModel.LatestAvailableSymbolsPackage = viewModel.LatestSymbolsPackage != null && viewModel.LatestSymbolsPackage.StatusKey == PackageStatus.Available ? viewModel.LatestSymbolsPackage : package.LatestAvailableSymbolPackage(); if (packageHistory.Any()) { // calculate the number of days since the package registration was created // round to the nearest integer, with a min value of 1 // divide the total download count by this number viewModel.TotalDaysSinceCreated = Convert.ToInt32(Math.Max(1, Math.Round((DateTime.UtcNow - packageHistory.Min(p => p.Created)).TotalDays))); viewModel.DownloadsPerDay = viewModel.TotalDownloadCount / viewModel.TotalDaysSinceCreated; // for the package viewModel.DownloadsPerDayLabel = viewModel.DownloadsPerDay < 1 ? "<1" : viewModel.DownloadsPerDay.ToNuGetNumberString(); // Lazily load the package types from the database. viewModel.IsDotnetToolPackageType = package.PackageTypes.Any(e => e.Name.Equals("DotnetTool", StringComparison.OrdinalIgnoreCase)); viewModel.IsDotnetNewTemplatePackageType = package.PackageTypes.Any(e => e.Name.Equals("Template", StringComparison.OrdinalIgnoreCase)); } if (packageKeyToDeprecation != null && packageKeyToDeprecation.TryGetValue(package.Key, out var deprecation)) { viewModel.AlternatePackageId = deprecation.AlternatePackageRegistration?.Id; var alternatePackage = deprecation.AlternatePackage; if (alternatePackage != null) { // A deprecation should not have both an alternate package registration and an alternate package. // In case a deprecation does have both, we will hide the alternate package registration's ID in this model. viewModel.AlternatePackageId = alternatePackage?.Id; viewModel.AlternatePackageVersion = alternatePackage?.Version; } viewModel.CustomMessage = deprecation.CustomMessage; } if (packageRenames != null && packageRenames.Count != 0) { viewModel.PackageRenames = packageRenames; if (package.PackageRegistration?.RenamedMessage != null) { viewModel.RenamedMessage = package.PackageRegistration.RenamedMessage; } } viewModel.ReadMeHtml = readmeResult?.Content; viewModel.ReadMeImagesRewritten = readmeResult != null ? readmeResult.ImagesRewritten : false; viewModel.ReadmeImageSourceDisallowed = readmeResult != null ? readmeResult.ImageSourceDisallowed : false; viewModel.HasEmbeddedIcon = package.HasEmbeddedIcon; return(viewModel); }