Skip to content

Commit d445aa7

Browse files
committed
Add search side-by-side web page (#7177)
Address #7152
1 parent e37915d commit d445aa7

14 files changed

Lines changed: 635 additions & 6 deletions

File tree

src/Bootstrap/Gruntfile.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,23 @@ module.exports = function (grunt) {
165165
expand: true,
166166
src: 'fonts/**',
167167
dest: 'dist/'
168+
},
169+
gallerycss: {
170+
expand: true,
171+
cwd: 'dist/css/',
172+
src: [
173+
'bootstrap.css',
174+
'bootstrap-theme.css'
175+
],
176+
dest: '../NuGetGallery/Content/gallery/css/'
177+
},
178+
galleryjs: {
179+
expand: true,
180+
cwd: 'dist/js/',
181+
src: [
182+
'bootstrap.js'
183+
],
184+
dest: '../NuGetGallery/Scripts/gallery/'
168185
}
169186
},
170187

@@ -187,10 +204,10 @@ module.exports = function (grunt) {
187204
grunt.registerTask('dist-css', ['less:core', 'less:theme', 'postcss:core', 'postcss:theme', 'cssmin:core', 'cssmin:theme']);
188205

189206
// Full distribution task.
190-
grunt.registerTask('dist', ['clean:dist', 'dist-css', 'copy:fonts', 'dist-js']);
207+
grunt.registerTask('dist', ['clean:dist', 'dist-css', 'copy:fonts', 'dist-js', 'copy:gallerycss', 'copy:galleryjs']);
191208

192209
// Default task.
193-
grunt.registerTask('default', ['clean:dist', 'copy:fonts', 'dist-css', 'dist-js']);
210+
grunt.registerTask('default', ['clean:dist', 'copy:fonts', 'dist-css', 'dist-js', 'copy:gallerycss', 'copy:galleryjs']);
194211

195212
grunt.registerTask('build-glyphicons-data', function () {
196213
generateGlyphiconsData.call(this, grunt);

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

Lines changed: 56 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
@@ -26,6 +26,7 @@
2626
@import "page-profile.less";
2727
@import "page-report-abuse.less";
2828
@import "page-reset-password.less";
29+
@import "page-search-sxs.less";
2930
@import "page-sign-in.less";
3031
@import "page-statistics-most-downloaded.less";
3132
@import "page-statistics-overview.less";
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
.page-search-sxs {
2+
h1 {
3+
font-size: @font-size-h2;
4+
border-bottom: 1px solid lightgray;
5+
}
6+
7+
h2 {
8+
font-size: @font-size-h3;
9+
margin-top: 0;
10+
}
11+
12+
.list-packages .package {
13+
padding-top: 0;
14+
}
15+
16+
.list-packages .package .package-details,
17+
.list-packages .package .package-list,
18+
.list-packages .package .package-by {
19+
line-height: 16px;
20+
font-size: 13px;
21+
}
22+
23+
.list-packages .package .package-details,
24+
.list-packages .package .package-list {
25+
white-space: nowrap;
26+
overflow: hidden;
27+
text-overflow: ellipsis;
28+
29+
li {
30+
display: inline;
31+
}
32+
}
33+
34+
.list-packages .package .package-list {
35+
margin-top: 4px;
36+
margin-bottom: 4px;
37+
38+
li + li {
39+
margin-left: @padding-small-horizontal;
40+
padding-left: @padding-small-horizontal;
41+
border-left-style: solid;
42+
border-width: 1px;
43+
border-color: @gray-lighter;
44+
}
45+
}
46+
47+
.list-packages .package .package-header .reserved-indicator {
48+
width: 15px;
49+
}
50+
51+
.radio label {
52+
margin-right: 30px;
53+
}
54+
55+
.list-packages .package .package-header .package-title {
56+
font-size: 20px;
57+
}
58+
59+
.col-package-icon {
60+
padding-right: 0;
61+
}
62+
63+
.package .package-legacy-title {
64+
display: none;
65+
}
66+
67+
.panel-body p:last-child {
68+
margin-bottom: 0;
69+
}
70+
}

src/NuGetGallery/App_Start/Routes.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33
using System.Web.Mvc;
44
using System.Web.Routing;
5+
using NuGetGallery.Controllers;
56
using RouteMagic;
67

78
namespace NuGetGallery
@@ -562,6 +563,11 @@ public static void RegisterUIRoutes(RouteCollection routes)
562563
"downloads",
563564
new { controller = "Pages", action = "Downloads" });
564565

566+
routes.MapRoute(
567+
RouteName.ExperimentsSearchSideBySide,
568+
"experiments/search-sxs",
569+
new { controller = "Experiments", action = nameof(ExperimentsController.SearchSideBySide) });
570+
565571
// TODO : Most of the routes are essentially of the format api/v{x}/*. We should refactor the code to vary them by the version.
566572
// V1 Routes
567573
// If the push url is /api/v1 then NuGet.Core would ping the path to resolve redirection.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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+
using System;
5+
using System.Threading.Tasks;
6+
using System.Web.Mvc;
7+
using NuGet.Services.Messaging.Email;
8+
9+
namespace NuGetGallery.Controllers
10+
{
11+
public class ExperimentsController : AppController
12+
{
13+
private readonly ISearchSideBySideService _searchSideBySideService;
14+
private readonly IFeatureFlagService _featureFlagService;
15+
16+
public ExperimentsController(
17+
ISearchSideBySideService searchSideBySideService,
18+
IFeatureFlagService featureFlagService)
19+
{
20+
_searchSideBySideService = searchSideBySideService ?? throw new ArgumentNullException(nameof(searchSideBySideService));
21+
_featureFlagService = featureFlagService ?? throw new ArgumentNullException(nameof(featureFlagService));
22+
}
23+
24+
[HttpGet]
25+
public async Task<ActionResult> SearchSideBySide(string q = null)
26+
{
27+
var currentUser = GetCurrentUser();
28+
if (!_featureFlagService.IsSearchSideBySideEnabled(currentUser))
29+
{
30+
return new HttpNotFoundResult();
31+
}
32+
33+
var viewModel = await _searchSideBySideService.SearchAsync(q, currentUser);
34+
35+
return View(viewModel);
36+
}
37+
38+
[HttpPost]
39+
[ValidateAntiForgeryToken]
40+
public async Task<ActionResult> SearchSideBySide(SearchSideBySideViewModel input)
41+
{
42+
var currentUser = GetCurrentUser();
43+
if (!_featureFlagService.IsSearchSideBySideEnabled(currentUser))
44+
{
45+
return new HttpNotFoundResult();
46+
}
47+
48+
var searchUrl = Url.SearchSideBySide(searchTerm: input.SearchTerm?.Trim(), relativeUrl: false);
49+
await _searchSideBySideService.RecordFeedbackAsync(input, searchUrl);
50+
51+
TempData["Message"] = "Thank you for providing feedback! Feel free to try some other queries.";
52+
53+
return RedirectToAction(nameof(SearchSideBySide));
54+
}
55+
}
56+
}

src/NuGetGallery/NuGetGallery.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@
229229
<Compile Include="Authentication\Providers\AzureActiveDirectoryV2\AzureActiveDirectoryV2AuthenticatorConfiguration.cs" />
230230
<Compile Include="Authentication\Providers\AzureActiveDirectory\AzureActiveDirectoryAuthenticator.cs" />
231231
<Compile Include="Authentication\Providers\AzureActiveDirectory\AzureActiveDirectoryAuthenticatorConfiguration.cs" />
232+
<Compile Include="Controllers\ExperimentsController.cs" />
232233
<Compile Include="Controllers\ManageDeprecationJsonApiController.cs" />
233234
<Compile Include="Infrastructure\Lucene\Correlation\CorrelatingHttpClientHandler.cs" />
234235
<Compile Include="Infrastructure\Lucene\Correlation\WebApiCorrelationHandler.cs" />
@@ -1968,6 +1969,7 @@
19681969
<Content Include="Views\Packages\_ManageDeprecation.cshtml" />
19691970
<Content Include="Views\Shared\_MultiSelectDropdown.cshtml" />
19701971
<Content Include="Views\Packages\_DisplayPackageDeprecation.cshtml" />
1972+
<Content Include="Views\Experiments\SearchSideBySide.cshtml" />
19711973
</ItemGroup>
19721974
<ItemGroup>
19731975
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />

src/NuGetGallery/RouteName.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,5 +105,6 @@ public static class RouteName
105105
public const string ApiV2CuratedSimulateError = "api-v2curated-simulate-error";
106106
public const string PagesSimulateError = "PagesSimulateError";
107107
public const string ApiSimulateError = "ApiSimulateError";
108+
public const string ExperimentsSearchSideBySide = "ExperimentsSearchSideBySide";
108109
}
109110
}

src/NuGetGallery/UrlHelperExtensions.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,21 @@ public static string Search(this UrlHelper url, string searchTerm, bool relative
465465
});
466466
}
467467

468+
public static string SearchSideBySide(
469+
this UrlHelper url,
470+
string searchTerm = null,
471+
bool relativeUrl = true)
472+
{
473+
return GetRouteLink(
474+
url,
475+
RouteName.ExperimentsSearchSideBySide,
476+
relativeUrl,
477+
routeValues: new RouteValueDictionary
478+
{
479+
{ "q", searchTerm }
480+
});
481+
}
482+
468483
public static string UploadPackage(this UrlHelper url, bool relativeUrl = true)
469484
{
470485
return GetRouteLink(url, RouteName.UploadPackage, relativeUrl);

src/NuGetGallery/ViewModels/SearchSideBySideViewModel.cs

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

44
using System.Collections.Generic;
55
using System.ComponentModel.DataAnnotations;
6+
using System.Web.Mvc;
67

78
namespace NuGetGallery
89
{
@@ -34,6 +35,7 @@ public class SearchSideBySideViewModel
3435
public string ExpectedPackages { get; set; }
3536

3637
[Display(Name = CommentsLabel)]
38+
[AllowHtml]
3739
public string Comments { get; set; }
3840

3941
[Display(Name = EmailLabel)]

0 commit comments

Comments
 (0)