Skip to content

Commit 20cb963

Browse files
rocklanskofman1zhhyu
authored
Display markdown licence files as HTML (#8156)
* refactored markdown to HTML conversion out of ReadMeService to sit inside it's own service, named MarkdownService, so that it can be reused * Display embedded licence MD file as rendered HTML (#7975) * added registration of MarkdownService * Put license MD rendering to HTML behind a feature flag * renamed licence to license because I can't spell * just registering IMarkdownService not MarkdownService * minor cleanups * minor cleanups after review * Resolving conflicts after rebase with dev * Display embedded licence MD file as rendered HTML (#7975) * added registration of MarkdownService * Put license MD rendering to HTML behind a feature flag * renamed licence to license because I can't spell * just registering IMarkdownService not MarkdownService * minor cleanups * minor cleanups after review * fixed usings * Fix CI * Update Co-authored-by: Svetlana Kofman <[email protected]> Co-authored-by: zhhyu <[email protected]>
1 parent 718bd1e commit 20cb963

23 files changed

Lines changed: 339 additions & 171 deletions

src/NuGetGallery.Services/Configuration/FeatureFlagService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public class FeatureFlagService : IFeatureFlagService
3939
private const string UsabillaOnEveryPageFeatureName = GalleryPrefix + "UsabillaEveryPage";
4040
private const string PackageRenamesFeatureName = GalleryPrefix + "PackageRenames";
4141
private const string EmbeddedReadmeFlightName = GalleryPrefix + "EmbeddedReadmes";
42+
private const string LicenseMdRenderingFlightName = GalleryPrefix + "LicenseMdRendering";
4243

4344
private const string ODataV1GetAllNonHijackedFeatureName = GalleryPrefix + "ODataV1GetAllNonHijacked";
4445
private const string ODataV1GetAllCountNonHijackedFeatureName = GalleryPrefix + "ODataV1GetAllCountNonHijacked";
@@ -274,6 +275,11 @@ public bool IsODataV2SearchNonHijackedEnabled()
274275
return _client.IsEnabled(ODataV2SearchNonHijackedFeatureName, defaultValue: true);
275276
}
276277

278+
public bool IsLicenseMdRenderingEnabled(User user)
279+
{
280+
return _client.IsEnabled(LicenseMdRenderingFlightName, user, defaultValue: false);
281+
}
282+
277283
public bool IsODataV2SearchCountNonHijackedEnabled()
278284
{
279285
return _client.IsEnabled(ODataV2SearchCountNonHijackedFeatureName, defaultValue: true);

src/NuGetGallery.Services/Configuration/IFeatureFlagService.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,10 @@ public interface IFeatureFlagService
218218
bool IsODataV2SearchNonHijackedEnabled();
219219

220220
/// <summary>
221+
/// Whether rendering licence Markdown content to HTML is enabled
222+
/// </summary>
223+
bool IsLicenseMdRenderingEnabled(User user);
224+
221225
/// Whether the /Search()/$count endpoint is enabled for non-hijacked queries for the V2 OData API.
222226
/// </summary>
223227
bool IsODataV2SearchCountNonHijackedEnabled();

src/NuGetGallery/App_Data/Files/Content/flags.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@
6666
"SiteAdmins": false,
6767
"Accounts": [],
6868
"Domains": []
69+
},
70+
"NuGetGallery.LicenseMdRendering": {
71+
"All": true,
72+
"SiteAdmins": false,
73+
"Accounts": [],
74+
"Domains": []
6975
}
7076
}
7177
}

src/NuGetGallery/App_Start/DefaultDependenciesModule.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,10 @@ protected override void Load(ContainerBuilder builder)
368368
.As<IReadMeService>()
369369
.InstancePerLifetimeScope();
370370

371+
builder.RegisterType<MarkdownService>()
372+
.As<IMarkdownService>()
373+
.InstancePerLifetimeScope();
374+
371375
builder.RegisterType<ApiScopeEvaluator>()
372376
.AsSelf()
373377
.As<IApiScopeEvaluator>()

src/NuGetGallery/Controllers/PackagesController.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ public partial class PackagesController
123123
private readonly IPackageDeprecationService _deprecationService;
124124
private readonly IPackageRenameService _renameService;
125125
private readonly IABTestService _abTestService;
126+
private readonly IMarkdownService _markdownService;
126127
private readonly IIconUrlProvider _iconUrlProvider;
127128
private readonly DisplayPackageViewModelFactory _displayPackageViewModelFactory;
128129
private readonly DisplayLicenseViewModelFactory _displayLicenseViewModelFactory;
@@ -162,7 +163,8 @@ public PackagesController(
162163
IPackageDeprecationService deprecationService,
163164
IPackageRenameService renameService,
164165
IABTestService abTestService,
165-
IIconUrlProvider iconUrlProvider)
166+
IIconUrlProvider iconUrlProvider,
167+
IMarkdownService markdownService)
166168
{
167169
_packageFilter = packageFilter;
168170
_packageService = packageService;
@@ -188,6 +190,8 @@ public PackagesController(
188190
_packageOwnershipManagementService = packageOwnershipManagementService;
189191
_contentObjectService = contentObjectService;
190192
_symbolPackageUploadService = symbolPackageUploadService;
193+
_markdownService = markdownService;
194+
191195
_trace = diagnosticsService?.SafeGetSource(nameof(PackagesController)) ?? throw new ArgumentNullException(nameof(diagnosticsService));
192196
_coreLicenseFileService = coreLicenseFileService ?? throw new ArgumentNullException(nameof(coreLicenseFileService));
193197
_licenseExpressionSplitter = licenseExpressionSplitter ?? throw new ArgumentNullException(nameof(licenseExpressionSplitter));
@@ -198,7 +202,7 @@ public PackagesController(
198202
_iconUrlProvider = iconUrlProvider ?? throw new ArgumentNullException(nameof(iconUrlProvider));
199203

200204
_displayPackageViewModelFactory = new DisplayPackageViewModelFactory(_iconUrlProvider);
201-
_displayLicenseViewModelFactory = new DisplayLicenseViewModelFactory(_iconUrlProvider);
205+
_displayLicenseViewModelFactory = new DisplayLicenseViewModelFactory(_iconUrlProvider, _markdownService, _featureFlagService);
202206
_listPackageItemViewModelFactory = new ListPackageItemViewModelFactory(_iconUrlProvider);
203207
_managePackageViewModelFactory = new ManagePackageViewModelFactory(_iconUrlProvider);
204208
_deletePackageViewModelFactory = new DeletePackageViewModelFactory(_iconUrlProvider);
@@ -614,6 +618,17 @@ private async Task<JsonResult> GetVerifyPackageView(User currentUser,
614618
model.HasExistingAvailableSymbols = hasExistingSymbolsPackageAvailable;
615619
model.Warnings.AddRange(packageContentData.Warnings.Select(w => new JsonValidationMessage(w)));
616620
model.LicenseFileContents = packageContentData.LicenseFileContents;
621+
622+
var license = packageMetadata.LicenseMetadata?.License;
623+
624+
if (_featureFlagService.IsLicenseMdRenderingEnabled(currentUser) &&
625+
license != null &&
626+
packageMetadata.LicenseMetadata?.Type == LicenseType.File &&
627+
Path.GetExtension(license).Equals(ServicesConstants.MarkdownFileExtension, StringComparison.InvariantCulture))
628+
{
629+
model.LicenseFileContentsHtml = _markdownService.GetHtmlFromMarkdown(packageContentData.LicenseFileContents, incrementHeadersBy: 2)?.Content;
630+
}
631+
617632
model.LicenseExpressionSegments = packageContentData.LicenseExpressionSegments;
618633
model.ReadmeFileContents = packageContentData.ReadmeFileContents;
619634

@@ -1136,7 +1151,7 @@ public virtual async Task<ActionResult> License(string id, string version)
11361151
throw;
11371152
}
11381153

1139-
var model = _displayLicenseViewModelFactory.Create(package, licenseExpressionSegments, licenseFileContents);
1154+
var model = _displayLicenseViewModelFactory.Create(package, licenseExpressionSegments, licenseFileContents, GetCurrentUser());
11401155

11411156
return View(model);
11421157
}

src/NuGetGallery/Helpers/ViewModelExtensions/DisplayLicenseViewModelFactory.cs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,37 +10,44 @@ namespace NuGetGallery
1010
{
1111
public class DisplayLicenseViewModelFactory
1212
{
13+
private readonly IMarkdownService _markdownService;
14+
private readonly IFeatureFlagService _featureFlagService;
1315
private PackageViewModelFactory _packageViewModelFactory;
1416

15-
public DisplayLicenseViewModelFactory(IIconUrlProvider iconUrlProvider)
17+
public DisplayLicenseViewModelFactory(IIconUrlProvider iconUrlProvider, IMarkdownService markdownService, IFeatureFlagService featureFlagService)
1618
{
1719
_packageViewModelFactory = new PackageViewModelFactory(iconUrlProvider);
20+
_markdownService = markdownService;
21+
_featureFlagService = featureFlagService;
1822
}
1923

2024
public DisplayLicenseViewModel Create(
2125
Package package,
2226
IReadOnlyCollection<CompositeLicenseExpressionSegment> licenseExpressionSegments,
23-
string licenseFileContents)
27+
string licenseFileContents,
28+
User currentUser)
2429
{
2530
var viewModel = new DisplayLicenseViewModel();
26-
return Setup(viewModel, package, licenseExpressionSegments, licenseFileContents);
31+
return Setup(viewModel, package, licenseExpressionSegments, licenseFileContents, currentUser);
2732
}
2833

2934
private DisplayLicenseViewModel Setup(
3035
DisplayLicenseViewModel viewModel,
3136
Package package,
3237
IReadOnlyCollection<CompositeLicenseExpressionSegment> licenseExpressionSegments,
33-
string licenseFileContents)
38+
string licenseFileContents,
39+
User currentUser)
3440
{
3541
_packageViewModelFactory.Setup(viewModel, package);
36-
return SetupInternal(viewModel, package, licenseExpressionSegments, licenseFileContents);
42+
return SetupInternal(viewModel, package, licenseExpressionSegments, licenseFileContents, currentUser);
3743
}
3844

3945
private DisplayLicenseViewModel SetupInternal(
4046
DisplayLicenseViewModel viewModel,
4147
Package package,
4248
IReadOnlyCollection<CompositeLicenseExpressionSegment> licenseExpressionSegments,
43-
string licenseFileContents)
49+
string licenseFileContents,
50+
User currentUser)
4451
{
4552
viewModel.EmbeddedLicenseType = package.EmbeddedLicenseType;
4653
viewModel.LicenseExpression = package.LicenseExpression;
@@ -57,6 +64,13 @@ private DisplayLicenseViewModel SetupInternal(
5764
viewModel.LicenseExpressionSegments = licenseExpressionSegments;
5865
viewModel.LicenseFileContents = licenseFileContents;
5966

67+
if (_featureFlagService.IsLicenseMdRenderingEnabled(currentUser) &&
68+
package.EmbeddedLicenseType == EmbeddedLicenseFileType.Markdown &&
69+
licenseFileContents != null)
70+
{
71+
viewModel.LicenseFileContentsHtml = _markdownService.GetHtmlFromMarkdown(licenseFileContents)?.Content;
72+
}
73+
6074
return viewModel;
6175
}
6276
}

src/NuGetGallery/Helpers/ViewModelExtensions/DisplayPackageViewModelFactory.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public DisplayPackageViewModel Create(
2424
User currentUser,
2525
IReadOnlyDictionary<int, PackageDeprecation> packageKeyToDeprecation,
2626
IReadOnlyList<PackageRename> packageRenames,
27-
RenderedReadMeResult readmeResult)
27+
RenderedMarkdownResult readmeResult)
2828
{
2929
var viewModel = new DisplayPackageViewModel();
3030
return Setup(
@@ -44,7 +44,7 @@ public DisplayPackageViewModel Setup(
4444
User currentUser,
4545
IReadOnlyDictionary<int, PackageDeprecation> packageKeyToDeprecation,
4646
IReadOnlyList<PackageRename> packageRenames,
47-
RenderedReadMeResult readmeResult)
47+
RenderedMarkdownResult readmeResult)
4848
{
4949
_listPackageItemViewModelFactory.Setup(viewModel, package, currentUser);
5050
SetupCommon(viewModel, package, pushedBy: null, packageKeyToDeprecation: packageKeyToDeprecation);
@@ -58,7 +58,7 @@ private DisplayPackageViewModel SetupInternal(
5858
User currentUser,
5959
IReadOnlyDictionary<int, PackageDeprecation> packageKeyToDeprecation,
6060
IReadOnlyList<PackageRename> packageRenames,
61-
RenderedReadMeResult readmeResult)
61+
RenderedMarkdownResult readmeResult)
6262
{
6363
var dependencies = package.Dependencies.ToList();
6464

src/NuGetGallery/NuGetGallery.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,9 @@
303303
<DependentUpon>202007220027197_AddEmbeddedReadmeTypeColumn.cs</DependentUpon>
304304
</Compile>
305305
<Compile Include="Modules\CookieComplianceHttpModule.cs" />
306+
<Compile Include="Services\IMarkdownService.cs" />
306307
<Compile Include="Services\IPackageMetadataValidationService.cs" />
308+
<Compile Include="Services\MarkdownService.cs" />
307309
<Compile Include="Services\PackageMetadataValidationService.cs" />
308310
<Compile Include="Services\ConfigurationIconFileProvider.cs" />
309311
<Compile Include="Services\IconUrlDeprecationValidationMessage.cs" />
@@ -515,7 +517,7 @@
515517
<Compile Include="Services\PackageRenameService.cs" />
516518
<Compile Include="Services\PackageShouldNotBeSignedUserFixableValidationMessage.cs" />
517519
<Compile Include="Services\PlainTextOnlyValidationMessage.cs" />
518-
<Compile Include="Services\RenderedReadMeResult.cs" />
520+
<Compile Include="Services\RenderedMarkdownResult.cs" />
519521
<Compile Include="Services\SearchSideBySideService.cs" />
520522
<Compile Include="Services\SymbolPackageValidationResult.cs" />
521523
<Compile Include="Services\FlatContainerContentFileMetadataService.cs" />

src/NuGetGallery/RequestModels/VerifyPackageRequest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ public VerifyPackageRequest(PackageMetadata packageMetadata, IEnumerable<User> p
105105
public string LicenseExpression { get; set; }
106106
public IReadOnlyCollection<CompositeLicenseExpressionSegmentViewModel> LicenseExpressionSegments { get; set; }
107107
public string LicenseFileContents { get; set; }
108+
public string LicenseFileContentsHtml { get; set; }
108109
public string MinClientVersionDisplay { get; set; }
109110
public string ProjectUrl { get; set; }
110111
public string RepositoryUrl { get; set; }
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
namespace NuGetGallery
5+
{
6+
public interface IMarkdownService
7+
{
8+
/// <summary>
9+
/// Returns HTML from the supplied markdown
10+
/// </summary>
11+
/// <param name="markdownString">markdown input</param>
12+
/// <returns>HTML data</returns>
13+
RenderedMarkdownResult GetHtmlFromMarkdown(string markdownString);
14+
15+
/// <summary>
16+
/// Returns HTML from the supplied markdown
17+
/// </summary>
18+
/// <param name="markdownString">markdown input</param>
19+
/// <param name="incrementHeadersBy">headers can be incremented by this value, eg if 2 supplied then h1 will become h3</param>
20+
/// <returns>HTML data</returns>
21+
RenderedMarkdownResult GetHtmlFromMarkdown(string markdownString, int incrementHeadersBy);
22+
}
23+
}

0 commit comments

Comments
 (0)