Skip to content

Commit f09c4a5

Browse files
author
Scott Bommarito
authored
Show deprecation information for current package on the Display Package page (#6917)
1 parent 98475cd commit f09c4a5

16 files changed

Lines changed: 654 additions & 59 deletions

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

Lines changed: 39 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/page-display-package.less

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,40 @@
1919
}
2020
}
2121

22+
.deprecation-container {
23+
.deprecation-expander {
24+
display: flex;
25+
justify-content: space-between;
26+
vertical-align: middle;
27+
28+
.deprecation-expander-icon-container {
29+
display: flex;
30+
align-items: center;
31+
32+
.deprecation-expander-icon {
33+
position: unset;
34+
top: unset;
35+
}
36+
}
37+
38+
.deprecation-expander-info-right {
39+
padding-left: 15px;
40+
}
41+
}
42+
43+
.deprecation-content-container {
44+
margin-top: 15px;
45+
46+
p {
47+
margin-top: 5px;
48+
}
49+
50+
p:last-of-type {
51+
margin-bottom: 0px;
52+
}
53+
}
54+
}
55+
2256
.failed-validation-alert-list {
2357
margin-top: 15px;
2458
margin-bottom: 15px;

src/NuGetGallery/Controllers/PackagesController.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ public partial class PackagesController
110110
private readonly ICoreLicenseFileService _coreLicenseFileService;
111111
private readonly ILicenseExpressionSplitter _licenseExpressionSplitter;
112112
private readonly IFeatureFlagService _featureFlagService;
113+
private readonly IPackageDeprecationService _deprecationService;
113114

114115
public PackagesController(
115116
IPackageService packageService,
@@ -137,7 +138,8 @@ public PackagesController(
137138
IDiagnosticsService diagnosticsService,
138139
ICoreLicenseFileService coreLicenseFileService,
139140
ILicenseExpressionSplitter licenseExpressionSplitter,
140-
IFeatureFlagService featureFlagService)
141+
IFeatureFlagService featureFlagService,
142+
IPackageDeprecationService deprecationService)
141143
{
142144
_packageService = packageService;
143145
_uploadFileService = uploadFileService;
@@ -165,6 +167,7 @@ public PackagesController(
165167
_coreLicenseFileService = coreLicenseFileService ?? throw new ArgumentNullException(nameof(coreLicenseFileService));
166168
_licenseExpressionSplitter = licenseExpressionSplitter ?? throw new ArgumentNullException(nameof(licenseExpressionSplitter));
167169
_featureFlagService = featureFlagService ?? throw new ArgumentNullException(nameof(featureFlagService));
170+
_deprecationService = deprecationService ?? throw new ArgumentNullException(nameof(deprecationService));
168171
}
169172

170173
[HttpGet]
@@ -737,7 +740,8 @@ public virtual async Task<ActionResult> DisplayPackage(string id, string version
737740
return HttpNotFound();
738741
}
739742

740-
var model = new DisplayPackageViewModel(package, currentUser);
743+
var deprecation = _deprecationService.GetDeprecationByPackage(package);
744+
var model = new DisplayPackageViewModel(package, currentUser, deprecation);
741745

742746
model.ValidatingTooLong = _validationService.IsValidatingTooLong(package);
743747
model.PackageValidationIssues = _validationService.GetLatestPackageValidationIssues(package);

src/NuGetGallery/NuGetGallery.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,6 +1931,7 @@
19311931
<Content Include="Views\Packages\License.cshtml" />
19321932
<Content Include="Views\Packages\_ManageDeprecation.cshtml" />
19331933
<Content Include="Views\Shared\_MultiSelectDropdown.cshtml" />
1934+
<Content Include="Views\Packages\_DisplayPackageDeprecation.cshtml" />
19341935
</ItemGroup>
19351936
<ItemGroup>
19361937
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />

src/NuGetGallery/Scripts/gallery/page-display-package.js

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
$(function () {
22
'use strict';
33

4-
function configureCopyButton(id) {
5-
var copyButton = $('#' + id + '-button');
6-
copyButton.popover({ trigger: 'manual' });
7-
8-
copyButton.click(function () {
9-
var text = $('#' + id + '-text').text().trim();
10-
window.nuget.copyTextToClipboard(text, copyButton);
11-
copyButton.popover('show');
12-
setTimeout(function () {
13-
copyButton.popover('destroy');
14-
}, 1000);
4+
// Configure the deprecation information container
5+
var container = $('#show-deprecation-content-container');
6+
if ($('#deprecation-content-container').children().length) {
7+
// If the deprecation information container has content, configure it as an expander.
8+
window.nuget.configureExpander("deprecation-content-container", "ChevronDown", null, "ChevronUp");
9+
container.keydown(function (event) {
10+
if (event.which === 13) { // Enter
11+
$(event.target).click();
12+
}
1513
});
16-
}
17-
14+
}
15+
else {
16+
// If the container does not have content, remove its expander attributes
17+
var expanderAttributes = ['data-toggle', 'data-target', 'aria-expanded', 'aria-controls', 'tabindex'];
18+
for (var i in expanderAttributes) {
19+
container.removeAttr(expanderAttributes[i]);
20+
}
21+
22+
$('#deprecation-expander-icon-right').hide();
23+
}
24+
25+
// Configure ReadMe container
1826
var readmeContainer = $("#readme-container");
1927
if (readmeContainer[0])
2028
{
@@ -41,6 +49,7 @@ $(function () {
4149
});
4250
}
4351

52+
// Configure expanders
4453
window.nuget.configureExpanderHeading("dependency-groups");
4554
window.nuget.configureExpanderHeading("version-history");
4655
window.nuget.configureExpander(
@@ -50,6 +59,21 @@ $(function () {
5059
"CalculatorSubtract",
5160
"Show more");
5261

62+
// Configure package manager copy buttons
63+
function configureCopyButton(id) {
64+
var copyButton = $('#' + id + '-button');
65+
copyButton.popover({ trigger: 'manual' });
66+
67+
copyButton.click(function () {
68+
var text = $('#' + id + '-text').text().trim();
69+
window.nuget.copyTextToClipboard(text, copyButton);
70+
copyButton.popover('show');
71+
setTimeout(function () {
72+
copyButton.popover('destroy');
73+
}, 1000);
74+
});
75+
}
76+
5377
for (var i in packageManagers)
5478
{
5579
configureCopyButton(packageManagers[i]);

src/NuGetGallery/Services/IPackageDeprecationService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,7 @@ Task UpdateDeprecation(
3737
/// Fetches all <see cref="Cwe"/>s with a <see cref="Cwe.CweId"/> contained in <paramref name="ids"/>.
3838
/// </summary>
3939
IReadOnlyCollection<Cwe> GetCwesById(IEnumerable<string> ids);
40+
41+
PackageDeprecation GetDeprecationByPackage(Package package);
4042
}
4143
}

src/NuGetGallery/Services/PackageDeprecationService.cs

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

44
using System;
55
using System.Collections.Generic;
6+
using System.Data.Entity;
67
using System.Linq;
78
using System.Threading.Tasks;
89
using NuGet.Services.Entities;
@@ -171,5 +172,15 @@ public IReadOnlyCollection<Cwe> GetCwesById(IEnumerable<string> ids)
171172

172173
return cwes;
173174
}
175+
176+
public PackageDeprecation GetDeprecationByPackage(Package package)
177+
{
178+
return _deprecationRepository.GetAll()
179+
.Include(d => d.Cves)
180+
.Include(d => d.Cwes)
181+
.Include(d => d.AlternatePackage.PackageRegistration)
182+
.Include(d => d.AlternatePackageRegistration)
183+
.SingleOrDefault(d => d.PackageKey == package.Key);
184+
}
174185
}
175186
}

src/NuGetGallery/ViewModels/DeletePackageViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace NuGetGallery
1111
public class DeletePackageViewModel : DisplayPackageViewModel
1212
{
1313
public DeletePackageViewModel(Package package, User currentUser, IReadOnlyList<ReportPackageReason> reasons)
14-
: base(package, currentUser)
14+
: base(package, currentUser, null)
1515
{
1616
DeletePackagesRequest = new DeletePackagesRequest
1717
{

src/NuGetGallery/ViewModels/DisplayPackageViewModel.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace NuGetGallery
1313
{
1414
public class DisplayPackageViewModel : ListPackageItemViewModel
1515
{
16-
public DisplayPackageViewModel(Package package, User currentUser)
16+
public DisplayPackageViewModel(Package package, User currentUser, PackageDeprecation deprecation)
1717
: this(package, currentUser, (string)null)
1818
{
1919
HasSemVer2Version = NuGetVersion.IsSemVer2;
@@ -46,6 +46,28 @@ public DisplayPackageViewModel(Package package, User currentUser)
4646
DownloadsPerDayLabel = DownloadsPerDay < 1 ? "<1" : DownloadsPerDay.ToNuGetNumberString();
4747
IsDotnetToolPackageType = package.PackageTypes.Any(e => e.Name.Equals("DotnetTool", StringComparison.OrdinalIgnoreCase));
4848
}
49+
50+
if (deprecation != null)
51+
{
52+
DeprecationStatus = deprecation.Status;
53+
54+
CveIds = deprecation.Cves?.Select(c => c.CveId).ToList();
55+
CvssRating = deprecation.CvssRating;
56+
CweIds = deprecation.Cwes?.Select(c => c.CweId).ToList();
57+
58+
AlternatePackageId = deprecation.AlternatePackageRegistration?.Id;
59+
60+
var alternatePackage = deprecation.AlternatePackage;
61+
if (alternatePackage != null)
62+
{
63+
// A deprecation should not have both an alternate package registration and an alternate package.
64+
// In case a deprecation does have both, we will hide the alternate package registration's ID in this model.
65+
AlternatePackageId = alternatePackage?.Id;
66+
AlternatePackageVersion = alternatePackage?.Version;
67+
}
68+
69+
CustomMessage = deprecation.CustomMessage;
70+
}
4971
}
5072

5173
private DisplayPackageViewModel(Package package, User currentUser, string pushedBy)
@@ -141,6 +163,14 @@ public bool HasNewerRelease
141163
public IReadOnlyCollection<CompositeLicenseExpressionSegment> LicenseExpressionSegments { get; set; }
142164
public EmbeddedLicenseFileType EmbeddedLicenseType { get; set; }
143165

166+
public PackageDeprecationStatus DeprecationStatus { get; set; }
167+
public IReadOnlyCollection<string> CveIds { get; set; }
168+
public decimal? CvssRating { get; set; }
169+
public IReadOnlyCollection<string> CweIds { get; set; }
170+
public string AlternatePackageId { get; set; }
171+
public string AlternatePackageVersion { get; set; }
172+
public string CustomMessage { get; set; }
173+
144174
private IDictionary<User, string> _pushedByCache = new Dictionary<User, string>();
145175

146176
private string GetPushedBy(Package package, User currentUser)

src/NuGetGallery/ViewModels/ManagePackageViewModel.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,16 @@ public VersionDeprecationState(Package package, string text)
156156
CvssRating = deprecation.CvssRating;
157157
CweIds = deprecation.Cwes?.Select(c => new VulnerabilityDetailState(c)).ToList();
158158

159-
// A deprecation should not have both an alternate package registration and an alternate package.
160-
// In case a deprecation does have both, we will hide the alternate package registration's ID in this model.
161159
AlternatePackageId = deprecation.AlternatePackageRegistration?.Id;
160+
162161
var alternatePackage = deprecation.AlternatePackage;
163-
AlternatePackageId = alternatePackage?.Id;
164-
AlternatePackageVersion = alternatePackage?.Version;
162+
if (alternatePackage != null)
163+
{
164+
// A deprecation should not have both an alternate package registration and an alternate package.
165+
// In case a deprecation does have both, we will hide the alternate package registration's ID in this model.
166+
AlternatePackageId = alternatePackage?.Id;
167+
AlternatePackageVersion = alternatePackage?.Version;
168+
}
165169

166170
CustomMessage = deprecation.CustomMessage;
167171

0 commit comments

Comments
 (0)