Skip to content

Commit

Permalink
Adds deprecation links in PM UI (#4144)
Browse files Browse the repository at this point in the history
Search links within PM UI. Fixes NuGet/Home#10996
  • Loading branch information
Fernando Aguilar committed Jul 30, 2021
1 parent 3ec538c commit 8387514
Show file tree
Hide file tree
Showing 21 changed files with 451 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(NuGetCoreSrcDirectory)NuGet.Resolver\NuGet.Resolver.csproj" />
<ProjectReference Include="..\NuGet.PackageManagement.VisualStudio\NuGet.PackageManagement.VisualStudio.csproj" />
<ProjectReference Include="..\NuGet.VisualStudio\NuGet.VisualStudio.csproj" />
<ProjectReference Include="..\NuGet.VisualStudio.Common\NuGet.VisualStudio.Common.csproj" />
<ProjectReference Include="$(NuGetClientsSrcDirectory)NuGet.PackageManagement.VisualStudio\NuGet.PackageManagement.VisualStudio.csproj" />
<ProjectReference Include="$(NuGetClientsSrcDirectory)NuGet.VisualStudio\NuGet.VisualStudio.csproj" />
<ProjectReference Include="$(NuGetClientsSrcDirectory)NuGet.VisualStudio.Common\NuGet.VisualStudio.Common.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Sdk" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Globalization;
using System.Windows.Data;
using NuGet.VisualStudio.Internal.Contracts;

namespace NuGet.PackageManagement.UI
{
/// <summary>
/// Converts from <see cref="PackageDeprecationMetadataContextInfo"/> to <see cref="PackageItemDeprecationLabelState"/>
/// </summary>
public class DeprecationToDeprecationLabelStateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is PackageDeprecationMetadataContextInfo deprecation)
{
if (!string.IsNullOrEmpty(deprecation.AlternatePackage?.PackageId))
{
return PackageItemDeprecationLabelState.AlternativeAvailable;
}

return PackageItemDeprecationLabelState.Deprecation;
}

return PackageItemDeprecationLabelState.Invisible;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Globalization;
using System.Windows.Data;

namespace NuGet.PackageManagement.UI
{
/// <summary>
/// Parameter for <see cref="FormattedStringPartConverter"/>
/// </summary>
public enum FormattedStringPart
{
Prefix,
Suffix,
}

/// <summary>
/// Extracts from a formatted string with one '{0}' placeholder to either left or right side of the placeholder
/// </summary>
public class FormattedStringPartConverter : IValueConverter
{
/// <summary>
/// Extracts either left or right side of a string with one placeholder '{0}'
/// </summary>
/// <param name="value">string with placeholder</param>
/// <param name="targetType">Not used</param>
/// <param name="parameter">A <see cref="FormattedStringPart"/> value</param>
/// <param name="culture">Not used</param>
/// <returns><c>null </c> if invalid value or parameter, otherwise a string with either left or right side</returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string formattedString = value as string;
if (formattedString == null)
{
return null;
}

var mode = parameter as FormattedStringPart?;
if (!mode.HasValue)
{
return null;
}

int placeholderIndex = formattedString.IndexOf("{0}", StringComparison.Ordinal);
if (placeholderIndex < 0)
{
return null;
}

switch (mode)
{
case FormattedStringPart.Prefix:
return formattedString.Substring(0, placeholderIndex);
case FormattedStringPart.Suffix:
return formattedString.Substring(placeholderIndex + "{0}".Length);
}

return null;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@
[assembly: SuppressMessage("Build", "CA1501:'ListBoxToggleableItemsAutomationPeer' has an object hierarchy '8' levels deep within the defining module. If possible, eliminate base classes within the hierarchy to decrease its hierarchy level below '6': 'ListBoxAutomationPeer, SelectorAutomationPeer, ItemsControlAutomationPeer, FrameworkElementAutomationPeer, UIElementAutomationPeer, AutomationPeer, DispatcherObject, Object'", Justification = "<Pending>", Scope = "type", Target = "~T:NuGet.PackageManagement.UI.ListBoxToggleableItemsAutomationPeer")]
[assembly: SuppressMessage("Build", "CA1501:'ToggleableItemAutomationPeer' has an object hierarchy '6' levels deep within the defining module. If possible, eliminate base classes within the hierarchy to decrease its hierarchy level below '6': 'ListBoxItemAutomationPeer, SelectorItemAutomationPeer, ItemAutomationPeer, AutomationPeer, DispatcherObject, Object'", Justification = "<Pending>", Scope = "type", Target = "~T:NuGet.PackageManagement.UI.ToggleableItemAutomationPeer")]
[assembly: SuppressMessage("Build", "CA1501:'PackageManagerControl' has an object hierarchy '9' levels deep within the defining module. If possible, eliminate base classes within the hierarchy to decrease its hierarchy level below '6': 'UserControl, ContentControl, Control, FrameworkElement, UIElement, Visual, DependencyObject, DispatcherObject, Object'", Justification = "<Pending>", Scope = "type", Target = "~T:NuGet.PackageManagement.UI.PackageManagerControl")]
[assembly: SuppressMessage("Build", "CA1501:'PackageManagerProvidersLabel' has an object hierarchy '9' levels deep within the defining module. If possible, eliminate base classes within the hierarchy to decrease its hierarchy level below '6': 'UserControl, ContentControl, Control, FrameworkElement, UIElement, Visual, DependencyObject, DispatcherObject, Object'", Justification = "<Pending>", Scope = "type", Target = "~T:NuGet.PackageManagement.UI.PackageManagerProvidersLabel")]
[assembly: SuppressMessage("Build", "CA1501:'PackageManagerProvidersLabel' has an object hierarchy '9' levels deep within the defining module. If possible, eliminate base classes within the hierarchy to decrease its hierarchy level below '6': 'UserControl, ContentControl, Control, FrameworkElement, UIElement, Visual, DependencyObject, DispatcherObject, Object'", Justification = "Default WPF Hierarchy", Scope = "type", Target = "~T:NuGet.PackageManagement.UI.PackageManagerProvidersLabel")]
[assembly: SuppressMessage("Build", "CA1501:'PackageItemDeprecationLabel' has an object hierarchy '9' levels deep within the defining module. If possible, eliminate base classes within the hierarchy to decrease its hierarchy level below '6': 'UserControl, ContentControl, Control, FrameworkElement, UIElement, Visual, DependencyObject, DispatcherObject, Object'", Justification = "Default WPF Hierarchy", Scope = "type", Target = "~T:NuGet.PackageManagement.UI.PackageItemDeprecationLabel")]
[assembly: SuppressMessage("Build", "CA1501:'PackageManagerTopPanel' has an object hierarchy '9' levels deep within the defining module. If possible, eliminate base classes within the hierarchy to decrease its hierarchy level below '6': 'UserControl, ContentControl, Control, FrameworkElement, UIElement, Visual, DependencyObject, DispatcherObject, Object'", Justification = "<Pending>", Scope = "type", Target = "~T:NuGet.PackageManagement.UI.PackageManagerTopPanel")]
[assembly: SuppressMessage("Build", "CA1501:'PackageMetadataControl' has an object hierarchy '9' levels deep within the defining module. If possible, eliminate base classes within the hierarchy to decrease its hierarchy level below '6': 'UserControl, ContentControl, Control, FrameworkElement, UIElement, Visual, DependencyObject, DispatcherObject, Object'", Justification = "<Pending>", Scope = "type", Target = "~T:NuGet.PackageManagement.UI.PackageMetadataControl")]
[assembly: SuppressMessage("Build", "CA1501:'PackageRestoreBar' has an object hierarchy '9' levels deep within the defining module. If possible, eliminate base classes within the hierarchy to decrease its hierarchy level below '6': 'UserControl, ContentControl, Control, FrameworkElement, UIElement, Visual, DependencyObject, DispatcherObject, Object'", Justification = "<Pending>", Scope = "type", Target = "~T:NuGet.PackageManagement.UI.PackageRestoreBar")]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace NuGet.PackageManagement.UI
{
/// <summary>
/// Represents possible states of <see cref="PackageItemDeprecationLabel"/> control
/// </summary>
public enum PackageItemDeprecationLabelState
{
/// <summary>
/// Implies no Deprecation information
/// </summary>
Invisible,

/// <summary>
/// There exists Deprecation but no alternative package
/// </summary>
Deprecation,

/// <summary>
/// Deprecation with alternative package available
/// </summary>
AlternativeAvailable,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
<Compile Include="Common\ErrorFloodGate.cs" />
<Compile Include="Converters\AdditionConverter.cs" />
<Compile Include="Converters\BooleanToGridRowHeightConverter.cs" />
<Compile Include="Converters\DeprecationToDeprecationLabelStateConverter.cs" />
<Compile Include="Converters\AccessibleConverter.cs" />
<Compile Include="Converters\DateTimeConverter.cs" />
<Compile Include="Converters\IntToVulnerabilitySeverityConverter.cs" />
Expand All @@ -78,6 +79,7 @@
<Compile Include="Models\IText.cs" />
<Compile Include="Models\LicenseText.cs" />
<Compile Include="Models\LicenseFileText.cs" />
<Compile Include="Models\PackageItemDeprecationLabelState.cs" />
<Compile Include="Utility\IReconnectingNuGetSearchService.cs" />
<Compile Include="Utility\NuGetSearchServiceReconnector.cs" />
<Compile Include="ViewModels\LoadingStatusViewModel.cs" />
Expand Down Expand Up @@ -154,6 +156,9 @@
<Compile Include="Xamls\NuGetProjectUpgradeWindow.xaml.cs">
<DependentUpon>NuGetProjectUpgradeWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Xamls\PackageItemDeprecationLabel.xaml.cs">
<DependentUpon>PackageItemDeprecationLabel.xaml</DependentUpon>
</Compile>
<Compile Include="Xamls\PackageManagementFormatWindow.xaml.cs">
<DependentUpon>PackageManagementFormatWindow.xaml</DependentUpon>
</Compile>
Expand All @@ -171,6 +176,7 @@
<DependentUpon>ProductUpdateBar.xaml</DependentUpon>
</Compile>
<Compile Include="Converters\MessageLevelToBrushConverter.cs" />
<Compile Include="Converters\FormattedStringPartConverter.cs" />
<Compile Include="Converters\NotEqualConverter.cs" />
<Compile Include="Converters\BooleanToVisibilityConverter.cs" />
<Compile Include="Converters\DownloadCountToVisibilityConverter.cs" />
Expand Down Expand Up @@ -324,6 +330,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Xamls\PackageItemDeprecationLabel.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Xamls\PackageManagementFormatWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down
36 changes: 36 additions & 0 deletions src/NuGet.Clients/NuGet.PackageManagement.UI/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion src/NuGet.Clients/NuGet.PackageManagement.UI/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -999,4 +999,17 @@ Please see https://aka.ms/troubleshoot_nuget_cache for more help.</value>
<value>You have {0} vulnerable package(s) installed.</value>
<comment>{0} is the number of installed packages</comment>
</data>
</root>
<data name="Deprecation_MoreInfo" xml:space="preserve">
<value>More info</value>
</data>
<data name="Deprecation_LinkTooltip" xml:space="preserve">
<value>Select to view the alternative package.</value>
</data>
<data name="Deprecation_PackageItemMessage" xml:space="preserve">
<value>This package version is deprecated.</value>
</data>
<data name="Deprecation_PackageItemMessageAlternative" xml:space="preserve">
<value>This package version is deprecated. Use {0} instead.</value>
<comment>{0} is a NuGet package id (string)</comment>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,10 @@ public static class Commands

// no parameters. Overridable by hosting app.
public static ICommand ShowErrorsCommand { get; set; } = new RoutedCommand();

/// <summary>
/// Command parameter is search string
/// </summary>
public static ICommand SearchPackageCommand { get; set; } = new RoutedCommand();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -623,9 +623,10 @@

<Style
TargetType="{x:Type Hyperlink}"
x:Key="HyperlinkStyle">
x:Key="HyperlinkStyleNoUri">
<Setter Property="Foreground" Value="{DynamicResource {x:Static nuget:Brushes.ControlLinkTextKey}}" />
<Setter Property="FocusVisualStyle" Value="{StaticResource ControlsFocusVisualStyle}"/>

<Style.Triggers>
<Trigger
Property="IsMouseOver"
Expand All @@ -642,7 +643,15 @@
Property="TextBlock.TextDecorations"
Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>

<Style
TargetType="{x:Type Hyperlink}"
x:Key="HyperlinkStyle"
BasedOn="{StaticResource HyperlinkStyleNoUri}">

<Style.Triggers>
<Trigger
Property="NavigateUri"
Value="{x:Null}">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,20 @@ public Task<(PackageSearchMetadataContextInfo, PackageDeprecationMetadataContext
return _detailedPackageSearchMetadata.Value;
}

private PackageDeprecationMetadataContextInfo _deprecationMetadata;
public PackageDeprecationMetadataContextInfo DeprecationMetadata
{
get => _deprecationMetadata;
set
{
if (_deprecationMetadata != value)
{
_deprecationMetadata = value;
OnPropertyChanged(nameof(DeprecationMetadata));
}
}
}

public IEnumerable<PackageVulnerabilityMetadataContextInfo> Vulnerabilities { get; set; }

private (BitmapSource, IconBitmapStatus) GetInitialIconBitmapAndStatus()
Expand Down Expand Up @@ -698,6 +712,7 @@ private async Task ReloadPackageMetadataAsync()
await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
cancellationToken.ThrowIfCancellationRequested();

DeprecationMetadata = deprecationMetadata;
IsPackageDeprecated = deprecationMetadata != null;
VulnerabilityMaxSeverity = packageMetadata?.Vulnerabilities?.Max(v => v.Severity) ?? -1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,26 @@
AutomationProperties.LabeledBy="{Binding ElementName=_deprecationLabel}"
ToolTip="{x:Static nuget:Resources.Label_Deprecated}"
Moniker="{x:Static catalog:KnownMonikers.StatusWarning}" />
<TextBlock
<TextBlock
Name="_deprecationLabel"
FontWeight="Bold"
Text="{x:Static nuget:Resources.Label_Deprecated}"/>
</StackPanel>
</StackPanel>
<TextBox
Style="{DynamicResource SelectableTextBlockStyle}"
Margin="0,8,0,0"
TextWrapping="WrapWithOverflow"
Text="{Binding Path=PackageDeprecationReasons}"
AutomationProperties.Name="{x:Static nuget:Resources.Label_DeprecationReasons}"/>
<TextBlock>
<Hyperlink
NavigateUri="{Binding Path=PackageMetadata.PackageDetailsUrl}"
ToolTip="{Binding Path=PackageMetadata.PackageDetailsUrl}"
Command="{x:Static nuget:PackageManagerControlCommands.OpenExternalLink}"
Style="{StaticResource HyperlinkStyleNoUri}">
<Run Text="{x:Static nuget:Resources.Deprecation_MoreInfo}" />
</Hyperlink>
</TextBlock>
<StackPanel
Margin="0,8,0,0"
Orientation="Vertical"
Expand All @@ -50,12 +59,18 @@
<StackPanel
Orientation="Horizontal"
VerticalAlignment="Center">
<TextBox
Style="{DynamicResource SelectableTextBlockStyle}"
<TextBlock
TextWrapping="Wrap"
VerticalAlignment="Center"
AutomationProperties.LabeledBy="{Binding ElementName=_alternatePackageLabel}"
Text="{Binding Path=PackageDeprecationAlternatePackageText}"/>
AutomationProperties.LabeledBy="{Binding ElementName=_alternatePackageLabel}">
<Hyperlink
ToolTip="{x:Static nuget:Resources.Deprecation_LinkTooltip}"
Command="{x:Static nuget:Commands.SearchPackageCommand}"
Style="{StaticResource HyperlinkStyleNoUri}"
CommandParameter="{Binding Path=PackageMetadata.DeprecationMetadata.AlternatePackage.PackageId}">
<Run Text="{Binding Path=PackageDeprecationAlternatePackageText}" />
</Hyperlink>
</TextBlock>
</StackPanel>
</StackPanel>
</StackPanel>
Expand Down
Loading

0 comments on commit 8387514

Please sign in to comment.