Skip to content

Commit 48058f7

Browse files
authored
Preview readme when uploading packages(#8160) (#8160)
1 parent 23a2ea5 commit 48058f7

13 files changed

Lines changed: 177 additions & 5 deletions

File tree

src/Bootstrap/dist/css/bootstrap-theme.css

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Bootstrap/less/theme/all.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
@import "common-licenses.less";
66
@import "common-list-packages.less";
77
@import "common-multi-select-dropdown.less";
8+
@import "common-readme.less";
89
@import "common-user-package-list.less";
910
@import "page-about.less";
1011
@import "page-account-settings.less";
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.readme {
2+
.readme-container{
3+
display: block;
4+
padding: 10.5px;
5+
background-color: @pre-bg;
6+
border: 1px solid #ccc;
7+
word-break: normal;
8+
margin-bottom: @default-margin-bottom;
9+
overflow: auto;
10+
max-height: 450px;
11+
}
12+
}

src/NuGetGallery.Services/PackageManagement/PackageService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,7 @@ public virtual Package EnrichPackageFromNuGetPackage(
692692
package.EmbeddedLicenseType = GetEmbeddedLicenseType(packageMetadata);
693693
package.LicenseExpression = GetLicenseExpression(packageMetadata);
694694
package.HasEmbeddedIcon = !string.IsNullOrWhiteSpace(packageMetadata.IconFile);
695+
package.HasReadMe = !string.IsNullOrWhiteSpace(packageMetadata.ReadmeFile);
695696
package.EmbeddedReadmeType = GetEmbeddedReadmeType(packageMetadata);
696697

697698
return package;
@@ -750,7 +751,7 @@ private static EmbeddedReadmeFileType GetEmbeddedReadmeType(PackageMetadata pack
750751

751752
var extension = Path.GetExtension(packageMetadata.ReadmeFile);
752753

753-
if (MarkdownFileExtension.Equals(extension, StringComparison.OrdinalIgnoreCase) || string.Empty == extension)
754+
if (MarkdownFileExtension.Equals(extension, StringComparison.OrdinalIgnoreCase))
754755
{
755756
return EmbeddedReadmeFileType.Markdown;
756757
}

src/NuGetGallery/Controllers/PackagesController.cs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using System.Web;
1818
using System.Web.Caching;
1919
using System.Web.Mvc;
20+
using System.Windows.Forms;
2021
using NuGet.Packaging;
2122
using NuGet.Services.Entities;
2223
using NuGet.Services.Licenses;
@@ -343,6 +344,8 @@ private async Task<ActionResult> UploadPackageInternal(SubmitPackageRequest mode
343344
verifyRequest.IsSymbolsPackage = false;
344345
verifyRequest.LicenseFileContents = await GetLicenseFileContentsOrNullAsync(packageMetadata, packageArchiveReader);
345346
verifyRequest.LicenseExpressionSegments = GetLicenseExpressionSegmentsOrNull(packageMetadata.LicenseMetadata);
347+
verifyRequest.ReadmeFileContents = await GetReadmeFileContentsOrNullAsync(packageMetadata, packageArchiveReader);
348+
346349
model.InProgressUpload = verifyRequest;
347350
return View(model);
348351
}
@@ -613,6 +616,7 @@ private async Task<JsonResult> GetVerifyPackageView(User currentUser,
613616
model.Warnings.AddRange(packageContentData.Warnings.Select(w => new JsonValidationMessage(w)));
614617
model.LicenseFileContents = packageContentData.LicenseFileContents;
615618
model.LicenseExpressionSegments = packageContentData.LicenseExpressionSegments;
619+
model.ReadmeFileContents = packageContentData.ReadmeFileContents;
616620

617621
if (packageContentData.EmbeddedIconInformation != null)
618622
{
@@ -635,13 +639,15 @@ public PackageContentData(
635639
IReadOnlyList<IValidationMessage> warnings,
636640
string licenseFileContents,
637641
IReadOnlyCollection<CompositeLicenseExpressionSegmentViewModel> licenseExpressionSegments,
638-
EmbeddedIconInformation embeddedIconInformation)
642+
EmbeddedIconInformation embeddedIconInformation,
643+
string readmeFileContents)
639644
{
640645
PackageMetadata = packageMetadata;
641646
Warnings = warnings;
642647
LicenseFileContents = licenseFileContents;
643648
LicenseExpressionSegments = licenseExpressionSegments;
644649
EmbeddedIconInformation = embeddedIconInformation;
650+
ReadmeFileContents = readmeFileContents;
645651
}
646652

647653
public JsonResult ErrorResult { get; }
@@ -650,6 +656,7 @@ public PackageContentData(
650656
public string LicenseFileContents { get; }
651657
public IReadOnlyCollection<CompositeLicenseExpressionSegmentViewModel> LicenseExpressionSegments { get; }
652658
public EmbeddedIconInformation EmbeddedIconInformation { get; }
659+
public string ReadmeFileContents { get; }
653660
}
654661

655662
private class EmbeddedIconInformation
@@ -673,6 +680,7 @@ private async Task<PackageContentData> ValidateAndProcessPackageContents(User cu
673680
IReadOnlyCollection<CompositeLicenseExpressionSegmentViewModel> licenseExpressionSegments = null;
674681
PackageMetadata packageMetadata = null;
675682
EmbeddedIconInformation embeddedIconInformation = null;
683+
string readmeFileContents = null;
676684

677685
using (Stream uploadedFile = await _uploadFileService.GetUploadFileAsync(currentUser.Key))
678686
{
@@ -720,6 +728,7 @@ private async Task<PackageContentData> ValidateAndProcessPackageContents(User cu
720728
licenseFileContents = await GetLicenseFileContentsOrNullAsync(packageMetadata, packageArchiveReader);
721729
licenseExpressionSegments = GetLicenseExpressionSegmentsOrNull(packageMetadata.LicenseMetadata);
722730
embeddedIconInformation = await GetEmbeddedIconOrNullAsync(packageMetadata, packageArchiveReader);
731+
readmeFileContents = await GetReadmeFileContentsOrNullAsync(packageMetadata, packageArchiveReader);
723732
}
724733
catch (Exception ex)
725734
{
@@ -730,7 +739,7 @@ private async Task<PackageContentData> ValidateAndProcessPackageContents(User cu
730739
}
731740
}
732741

733-
return new PackageContentData(packageMetadata, warnings, licenseFileContents, licenseExpressionSegments, embeddedIconInformation);
742+
return new PackageContentData(packageMetadata, warnings, licenseFileContents, licenseExpressionSegments, embeddedIconInformation, readmeFileContents);
734743
}
735744

736745
private IReadOnlyCollection<CompositeLicenseExpressionSegmentViewModel> GetLicenseExpressionSegmentsOrNull(LicenseMetadata licenseMetadata)
@@ -788,6 +797,18 @@ private static async Task<EmbeddedIconInformation> GetEmbeddedIconOrNullAsync(Pa
788797
return new EmbeddedIconInformation(imageContentType, imageData);
789798
}
790799

800+
private async Task<string> GetReadmeFileContentsOrNullAsync(PackageMetadata packageMetadata, PackageArchiveReader packageArchiveReader)
801+
{
802+
if (string.IsNullOrWhiteSpace(packageMetadata.ReadmeFile))
803+
{
804+
return null;
805+
}
806+
807+
var readmeFilename = FileNameHelper.GetZipEntryPath(packageMetadata.ReadmeFile);
808+
var readmeResult = await _readMeService.GetReadMeHtmlAsync(readmeFilename, packageArchiveReader, Encoding.UTF8);
809+
return readmeResult?.Content;
810+
}
811+
791812
private static async Task<byte[]> ReadPackageFile(PackageArchiveReader packageArchiveReader, string filename)
792813
{
793814
using (var packageFileStream = packageArchiveReader.GetStream(filename))
@@ -2600,9 +2621,14 @@ protected virtual async Task<JsonResult> VerifyPackageInternal(
26002621
{
26012622
return afterValidationJsonResult;
26022623
}
2603-
2624+
26042625
if (formData.Edit != null)
26052626
{
2627+
if (package.HasReadMe && package.EmbeddedReadmeType != EmbeddedReadmeFileType.Absent)
2628+
{
2629+
return Json(HttpStatusCode.BadRequest, new[] { new JsonValidationMessage(Strings.ReadmeNotEditableWithEmbeddedReadme) });
2630+
}
2631+
26062632
try
26072633
{
26082634
if (await _readMeService.SaveReadMeMdIfChanged(

src/NuGetGallery/RequestModels/VerifyPackageRequest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ public VerifyPackageRequest(PackageMetadata packageMetadata, IEnumerable<User> p
110110
public string RepositoryUrl { get; set; }
111111
public string RepositoryType { get; set; }
112112
public string ReleaseNotes { get; set; }
113+
public string ReadmeFileContents { get; set; }
113114
public bool RequiresLicenseAcceptance { get; set; }
114115
public string Summary { get; set; }
115116
public string Tags { get; set; }

src/NuGetGallery/Scripts/gallery/async-file-upload.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@
255255
$(reportContainerElement).attr("data-bind", "template: { name: 'verify-metadata-template', data: data }");
256256
$("#verify-package-container").append(reportContainerElement);
257257
ko.applyBindings({ data: model }, reportContainerElement);
258+
if (model.ReadmeFileContents) {
259+
$('#import-readme-container').addClass('hidden');
260+
} else {
261+
$('#import-readme-container').removeClass('hidden');
262+
}
258263

259264
var submitContainerElement = document.createElement("div");
260265
$(submitContainerElement).attr("id", "submit-block");

src/NuGetGallery/Services/IReadMeService.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Text;
55
using System.Threading.Tasks;
6+
using NuGet.Packaging;
67
using NuGet.Services.Entities;
78

89
namespace NuGetGallery
@@ -30,6 +31,16 @@ public interface IReadMeService
3031
/// <returns>ReadMe converted to HTML.</returns>
3132
Task<RenderedReadMeResult> GetReadMeHtmlAsync(Package package);
3233

34+
/// <summary>
35+
/// Get the converted HTML from the package with Readme markdown.
36+
/// </summary>
37+
/// <param name="readmeFileName">The path of Readme markdown.</param>
38+
/// <param name="packageArchiveReader">
39+
/// The <see cref="PackageArchiveReader"/> instance providing the package metadata.
40+
/// </param>
41+
/// <returns>ReadMe converted to HTML.</returns>
42+
Task<RenderedReadMeResult> GetReadMeHtmlAsync(string readmeFileName, PackageArchiveReader packageArchiveReader, Encoding encoding);
43+
3344
/// <summary>
3445
/// Get package ReadMe markdown from storage.
3546
/// </summary>

src/NuGetGallery/Services/ReadMeService.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Web;
1212
using CommonMark;
1313
using CommonMark.Syntax;
14+
using NuGet.Packaging;
1415
using NuGet.Services.Entities;
1516

1617
namespace NuGetGallery
@@ -98,6 +99,28 @@ public async Task<RenderedReadMeResult> GetReadMeHtmlAsync(Package package)
9899
GetReadMeHtml(readMeMd);
99100
}
100101

102+
/// <summary>
103+
/// Get the converted HTML from the package with Readme markdown.
104+
/// </summary>
105+
/// <param name="readmeFileName">The path of Readme markdown.</param>
106+
/// <param name="packageArchiveReader">
107+
/// The <see cref="PackageArchiveReader"/> instance providing the package metadata.
108+
/// </param>
109+
/// <returns>ReadMe converted to HTML.</returns>
110+
public async Task<RenderedReadMeResult> GetReadMeHtmlAsync(string readmeFileName, PackageArchiveReader packageArchiveReader, Encoding encoding)
111+
{
112+
var readmeMd = await GetReadMeMdAsync(readmeFileName, packageArchiveReader, encoding);
113+
var result = new RenderedReadMeResult
114+
{
115+
Content = readmeMd,
116+
ImagesRewritten = false
117+
};
118+
119+
return string.IsNullOrEmpty(readmeMd) ?
120+
result :
121+
GetReadMeHtml(readmeMd);
122+
}
123+
101124
/// <summary>
102125
/// Get package ReadMe markdown from storage.
103126
/// </summary>
@@ -113,6 +136,23 @@ public async Task<string> GetReadMeMdAsync(Package package)
113136
return null;
114137
}
115138

139+
/// <summary>
140+
/// Get content of Readme markdown.
141+
/// </summary>
142+
/// <param name="readmeFileName">Package entity associated with the ReadMe.</param>
143+
/// <param name="packageArchiveReader">
144+
/// The <see cref="PackageArchiveReader"/> instance providing the package metadata.
145+
/// </param>
146+
/// <returns>ReadMe markdown from package metadata.</returns>
147+
private async Task<string> GetReadMeMdAsync(string readmeFileName, PackageArchiveReader packageArchiveReader, Encoding encoding)
148+
{
149+
using (var readmeFileStream = packageArchiveReader.GetStream(readmeFileName))
150+
using (var streamReader = new StreamReader(readmeFileStream, encoding))
151+
{
152+
return await streamReader.ReadToEndAsync();
153+
}
154+
}
155+
116156
/// <summary>
117157
/// Save a pending ReadMe if changes are detected.
118158
/// </summary>

src/NuGetGallery/Strings.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)