Skip to content
This repository was archived by the owner on Oct 4, 2021. It is now read-only.

Commit 59bda25

Browse files
committed
[NuGet] Show license metadata in License Acceptance dialog
If the NuGet package has associated license metadata then show this in the dialog. License metadata can be an expression or a file. A license file will now be displayed in a dialog if the View License link is clicked. Warning icons and associated message will also be displayed for deprecated licenses. There are problems here. Could not get the text to wrap so ended up making the dialog resizable and always showing the scrollbars. Hyperlinks in license expressions do not open the url in a browser since the Xwt Xamarin.Mac's LabelBackend does not implement support for this.
1 parent 723db1f commit 59bda25

File tree

9 files changed

+273
-102
lines changed

9 files changed

+273
-102
lines changed

main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/LicenseAcceptanceDialog.cs

Lines changed: 91 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
using System;
3131
using System.Linq;
3232
using MonoDevelop.Core;
33-
using MonoDevelop.Ide;
33+
using MonoDevelop.Ide;
34+
using NuGet.PackageManagement.UI;
3435
using Xwt;
3536

3637
namespace MonoDevelop.PackageManagement
@@ -46,7 +47,6 @@ internal class LicenseAcceptanceDialog : Dialog
4647
public LicenseAcceptanceDialog (LicenseAcceptanceViewModel viewModel)
4748
{
4849
Height = 350;
49-
Resizable = false;
5050
Padding = 0;
5151
Title = GettextCatalog.GetString ("License Acceptance");
5252
this.viewModel = viewModel;
@@ -67,7 +67,7 @@ public LicenseAcceptanceDialog (LicenseAcceptanceViewModel viewModel)
6767
packagesList.Spacing = 0;
6868

6969
scroll = new ScrollView (packagesList);
70-
scroll.HorizontalScrollPolicy = ScrollPolicy.Never;
70+
scroll.HorizontalScrollPolicy = ScrollPolicy.Automatic;
7171
scroll.VerticalScrollPolicy = ScrollPolicy.Automatic;
7272
scroll.BorderVisible = false;
7373
scroll.BackgroundColor = Ide.Gui.Styles.BackgroundColor;
@@ -107,35 +107,15 @@ void AddPackages ()
107107
}
108108
}
109109

110-
protected override void OnShown ()
111-
{
112-
var count = packagesList.Children.Count ();
113-
if (count > 0 && count < 4) {
114-
scroll.VerticalScrollPolicy = ScrollPolicy.Never;
115-
var firstRow = packagesList.Children.First ();
116-
var rowHeight = firstRow.Size.Height;
117-
Height -= (rowHeight + firstRow.MarginTop + firstRow.MarginBottom) * (4 - count);
118-
} else if (count == 4) {
119-
scroll.VerticalScrollPolicy = ScrollPolicy.Never;
120-
} else if (count > 4) {
121-
scroll.VerticalScrollPolicy = ScrollPolicy.Automatic;
122-
Height += rowMargin.Top + rowMargin.Bottom;
123-
}
124-
base.OnShown ();
125-
}
126-
127110
void AddPackage (PackageLicenseViewModel package)
128111
{
129112
var titleBox = new VBox ();
130113
titleBox.Spacing = 0;
131114
titleBox.MarginBottom = 4;
132115
titleBox.PackStart (new Label {
133116
Markup = string.Format ("<span weight='bold'>{0}</span> – {1}", package.Id, package.Author),
134-
});
135-
var licenseLabel = new LinkLabel (GettextCatalog.GetString ("View License"));
136-
licenseLabel.Uri = package.LicenseUrl;
137-
licenseLabel.LinkClicked += (sender, e) => IdeServices.DesktopService.ShowUrl (e.Target.AbsoluteUri);
138-
titleBox.PackStart (licenseLabel);
117+
});
118+
AddLicenseInfo (package, titleBox);
139119

140120
var rowBox = new HBox ();
141121
rowBox.Margin = rowMargin;
@@ -151,6 +131,85 @@ void AddPackage (PackageLicenseViewModel package)
151131
packagesList.PackStart (rowBox);
152132
}
153133

134+
static void AddLicenseInfo (PackageLicenseViewModel package, VBox parentVBox)
135+
{
136+
if (package.LicenseLinks.Any ()) {
137+
if (package.LicenseLinks.Count == 1) {
138+
IText textLink = package.LicenseLinks [0];
139+
if (textLink is LicenseText licenseText) {
140+
AddLicenseLinkLabel (licenseText.Link, licenseText.Text, parentVBox);
141+
return;
142+
} else if (textLink is LicenseFileText licenseFileText) {
143+
AddFileLicenseLinkLabel (licenseFileText, parentVBox);
144+
return;
145+
} else {
146+
// Warning or free text fallback to showing an expression which will handle this.
147+
}
148+
}
149+
AddLicenseExpressionLabel (package, parentVBox);
150+
} else {
151+
AddLicenseLinkLabel (package.LicenseUrl, GettextCatalog.GetString ("View License"), parentVBox);
152+
}
153+
}
154+
155+
static void AddLicenseExpressionLabel (PackageLicenseViewModel package, VBox parentVBox)
156+
{
157+
var licenseLabel = new Label ();
158+
var builder = new LicenseLinkMarkupBuilder ();
159+
licenseLabel.Markup = builder.GetMarkup (package.LicenseLinks);
160+
licenseLabel.Wrap = WrapMode.None;
161+
162+
// Does not work. LabelBackend for Xamarin.Mac implementation does not ILabelEventSink.OnLinkClicked
163+
licenseLabel.LinkClicked += (sender, e) => IdeServices.DesktopService.ShowUrl (e.Target.AbsoluteUri);
164+
165+
foreach (WarningText warning in builder.Warnings) {
166+
AddLicenseWarningLabel (warning, parentVBox);
167+
}
168+
169+
var hbox = new HBox ();
170+
hbox.PackStart (licenseLabel, true);
171+
172+
parentVBox.PackStart (hbox);
173+
}
174+
175+
static void AddLicenseWarningLabel (WarningText warning, VBox parentVBox)
176+
{
177+
var hbox = new HBox ();
178+
var image = new ImageView {
179+
Image = ImageService.GetIcon ("md-warning", Gtk.IconSize.Menu),
180+
VerticalPlacement = WidgetPlacement.Start,
181+
};
182+
image.Accessible.RoleDescription = GettextCatalog.GetString ("Warning Icon");
183+
184+
hbox.PackStart (image);
185+
186+
var label = new Label {
187+
Text = warning.Text,
188+
Wrap = WrapMode.None
189+
};
190+
image.Accessible.LabelWidget = label;
191+
hbox.PackStart (label, true);
192+
193+
parentVBox.PackStart (hbox);
194+
}
195+
196+
static void AddFileLicenseLinkLabel (LicenseFileText licenseFileText, VBox parentVBox)
197+
{
198+
var licenseLabel = new LinkLabel (GettextCatalog.GetString ("View License"));
199+
licenseLabel.Uri = licenseFileText.CreateLicenseFileUri (1);
200+
licenseLabel.Tag = licenseFileText;
201+
licenseLabel.NavigateToUrl += (sender, e) => ShowFileDialog ((LinkLabel)sender);
202+
parentVBox.PackStart (licenseLabel);
203+
}
204+
205+
static void AddLicenseLinkLabel (Uri licenseUrl, string labelText, VBox parentVBox)
206+
{
207+
var licenseLabel = new LinkLabel (labelText);
208+
licenseLabel.Uri = licenseUrl;
209+
licenseLabel.LinkClicked += (sender, e) => IdeServices.DesktopService.ShowUrl (e.Target.AbsoluteUri);
210+
parentVBox.PackStart (licenseLabel);
211+
}
212+
154213
public new bool Run (WindowFrame parentWindow)
155214
{
156215
return base.Run (parentWindow) == Command.Ok;
@@ -162,6 +221,13 @@ protected override void Dispose (bool disposing)
162221
imageLoader.Dispose ();
163222
base.Dispose (disposing);
164223
}
224+
225+
static void ShowFileDialog (LinkLabel label)
226+
{
227+
var licenseFileText = (LicenseFileText)label.Tag;
228+
var dialog = new LicenseFileDialog (licenseFileText);
229+
dialog.Run (label.ParentWindow);
230+
}
165231
}
166232
}
167233

main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/LicenseFileDialog.cs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,16 @@
2424
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2525
// THE SOFTWARE.
2626

27+
using System;
28+
using System.Collections.Generic;
2729
using System.ComponentModel;
28-
using NuGet.PackageManagement.UI;
30+
using NuGet.PackageManagement.UI;
2931
using Xwt;
3032
using Xwt.Formats;
3133

32-
namespace MonoDevelop.PackageManagement.Gui
34+
namespace MonoDevelop.PackageManagement
3335
{
34-
class LicenseFileDialog : Dialog
36+
sealed class LicenseFileDialog : Dialog
3537
{
3638
RichTextView textView;
3739
LicenseFileText licenseFileText;
@@ -77,5 +79,42 @@ protected override void Dispose (bool disposing)
7779
licenseFileText.PropertyChanged -= LicenseFileTextPropertyChanged;
7880
}
7981
}
82+
83+
public static bool ShowDialog (Uri uri, IReadOnlyList<IText> licenseLinks, Dialog parent)
84+
{
85+
if (!uri.IsFile)
86+
return false;
87+
88+
if (uri.Fragment?.Length > 0) {
89+
if (int.TryParse (uri.Fragment.Substring (1), out int fileNumber)) {
90+
ShowLicenseFile (fileNumber, licenseLinks, parent);
91+
return true;
92+
}
93+
}
94+
return false;
95+
}
96+
97+
static void ShowLicenseFile (int fileNumber, IReadOnlyList<IText> licenseLinks, Dialog parent)
98+
{
99+
LicenseFileText licenseFileText = GetLicenseFile (fileNumber, licenseLinks);
100+
if (licenseFileText != null) {
101+
var dialog = new LicenseFileDialog (licenseFileText);
102+
dialog.Run (parent);
103+
}
104+
}
105+
106+
static LicenseFileText GetLicenseFile (int fileNumber, IReadOnlyList<IText> licenseLinks)
107+
{
108+
int currentFileNumber = 0;
109+
foreach (IText text in licenseLinks) {
110+
if (text is LicenseFileText licenseFileText) {
111+
currentFileNumber++;
112+
if (currentFileNumber == fileNumber) {
113+
return licenseFileText;
114+
}
115+
}
116+
}
117+
return null;
118+
}
80119
}
81120
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// LicenseFileTextExtensions.cs
3+
//
4+
// Author:
5+
// Matt Ward <[email protected]>
6+
//
7+
// Copyright (c) 2019 Microsoft Corporation
8+
//
9+
// Permission is hereby granted, free of charge, to any person obtaining a copy
10+
// of this software and associated documentation files (the "Software"), to deal
11+
// in the Software without restriction, including without limitation the rights
12+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the Software is
14+
// furnished to do so, subject to the following conditions:
15+
//
16+
// The above copyright notice and this permission notice shall be included in
17+
// all copies or substantial portions of the Software.
18+
//
19+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
// THE SOFTWARE.
26+
27+
using System;
28+
using NuGet.PackageManagement.UI;
29+
30+
namespace MonoDevelop.PackageManagement
31+
{
32+
static class LicenseFileTextExtensions
33+
{
34+
public static Uri CreateLicenseFileUri (this LicenseFileText licenseFileText, int licenseCount)
35+
{
36+
return new Uri ($"file:///{licenseFileText.Text}#{licenseCount}");
37+
}
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// LicenseLinkMarkupBuilder.cs
3+
//
4+
// Author:
5+
// Matt Ward <[email protected]>
6+
//
7+
// Copyright (c) 2019 Microsoft Corporation
8+
//
9+
// Permission is hereby granted, free of charge, to any person obtaining a copy
10+
// of this software and associated documentation files (the "Software"), to deal
11+
// in the Software without restriction, including without limitation the rights
12+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the Software is
14+
// furnished to do so, subject to the following conditions:
15+
//
16+
// The above copyright notice and this permission notice shall be included in
17+
// all copies or substantial portions of the Software.
18+
//
19+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
// THE SOFTWARE.
26+
27+
using System;
28+
using System.Collections.Generic;
29+
using System.Linq;
30+
using System.Security;
31+
using MonoDevelop.Core;
32+
using NuGet.PackageManagement.UI;
33+
34+
namespace MonoDevelop.PackageManagement
35+
{
36+
sealed class LicenseLinkMarkupBuilder
37+
{
38+
List<WarningText> warnings;
39+
40+
public string GetMarkup (IReadOnlyList<IText> textLinks)
41+
{
42+
var markupBuilder = StringBuilderCache.Allocate ();
43+
44+
int fileLicenseCount = 0; // Should be one but handle multiple.
45+
foreach (IText textLink in textLinks) {
46+
if (textLink is LicenseText licenseText) {
47+
markupBuilder.Append (GetUriMarkup (licenseText.Link, licenseText.Text));
48+
} else if (textLink is LicenseFileText licenseFileText) {
49+
fileLicenseCount++;
50+
markupBuilder.Append (GetUriMarkup (licenseFileText.CreateLicenseFileUri (fileLicenseCount), licenseFileText.Text));
51+
} else if (textLink is WarningText warning) {
52+
warnings ??= new List<WarningText> ();
53+
warnings.Add (warning);
54+
} else {
55+
markupBuilder.Append (textLink.Text);
56+
}
57+
}
58+
59+
return StringBuilderCache.ReturnAndFree (markupBuilder);
60+
}
61+
62+
public IEnumerable<WarningText> Warnings {
63+
get { return warnings ?? Enumerable.Empty<WarningText> (); }
64+
}
65+
66+
static string GetUriMarkup (Uri uri, string text)
67+
{
68+
return string.Format (
69+
"<a href=\"{0}\">{1}</a>",
70+
uri != null ? SecurityElement.Escape (uri.ToString ()) : string.Empty,
71+
SecurityElement.Escape (text));
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)