From f917e82263dca6581c31f7f568a57f7efcfdbe51 Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Tue, 20 Jun 2017 09:51:44 -0700 Subject: [PATCH 1/2] Add outbound click event tracking for project URL, license URL, and manual download --- src/NuGetGallery/App_Code/ViewHelpers.cshtml | 444 +++++++++--------- src/NuGetGallery/Scripts/nugetgallery.js | 33 ++ .../Views/Packages/DisplayPackage.cshtml | 6 +- 3 files changed, 262 insertions(+), 221 deletions(-) diff --git a/src/NuGetGallery/App_Code/ViewHelpers.cshtml b/src/NuGetGallery/App_Code/ViewHelpers.cshtml index b952035748..9e733a782a 100644 --- a/src/NuGetGallery/App_Code/ViewHelpers.cshtml +++ b/src/NuGetGallery/App_Code/ViewHelpers.cshtml @@ -1,219 +1,227 @@ -@using System.Web.Mvc -@using Microsoft.Web.Helpers -@using NuGetGallery -@using NuGetGallery.Helpers -@using NuGetGallery.Configuration - -@helper PreviousNextPager(IPreviousNextPager pager) -{ - if (pager.HasNextPage || pager.HasPreviousPage) - { - - } -} - -@helper Option(string value, string label, string currentValue) -{ - -} - -@helper UploadSequence(int currentStep) -{ -
    - @SequenceStep(1, "Upload", currentStep) - @SequenceStep(2, "Verify Details", currentStep) -
-
-} - -@helper SequenceStep(int step, string caption, int currentStep) -{ - if (currentStep > step) - { -
  • @caption
  • - } - else if (currentStep == step) - { -
  • @caption
  • - } - else - { -
  • @caption
  • - } -} - -@helper OwnersGravatar(IEnumerable owners, int size, UrlHelper url, bool showName = true) -{ - -} - -@helper OwnerGravatar(User owner, int size, UrlHelper url, bool showName = true) -{ - - @if (!String.IsNullOrEmpty(owner.EmailAddress)) - { - @GravatarImage(owner.EmailAddress, owner.Username, size) - } - @if (showName) - { - @owner.Username - } - -} - -@helper GravatarImage(string email, string username, int size) -{ - var gravatarHtml = Gravatar.GetHtml(email, size, "retro", GravatarRating.G, attributes: new { width = size, height = size, title = username, @class = "owner-image" }); - if (gravatarHtml != null && Request.IsSecureConnection) - { - gravatarHtml = new HtmlString(gravatarHtml.ToHtmlString().Replace("http://www.gravatar.com/", "https://secure.gravatar.com/")); - } - @gravatarHtml -} - -@helper WriteMeta(string name, string val) { - if(!String.IsNullOrEmpty(val)) { - - } -} - -@helper ReleaseMeta() { - // Get Version info - var ver = ApplicationVersionHelper.GetVersion(); - - if(ver.Present) { - WriteMeta("branch", ver.Branch); - WriteMeta("commit", ver.ShortCommit); - WriteMeta("time", ver.BuildDateUtc == DateTime.MinValue ? null : ver.BuildDateUtc.ToString("O")); - } -} - -@helper InstrumentationScript() -{ - // Get instrumentation key - var config = DependencyResolver.Current.GetService(); - var iKey = config == null ? string.Empty : config.Current.AppInsightsInstrumentationKey; - var samplingPct = config == null ? 100 : config.Current.AppInsightsSamplingPercentage; - - if (!string.IsNullOrEmpty(iKey)) - { - - - } -} - -@helper ReleaseTag() -{ - // Get Version info and gallery brand name - var ver = ApplicationVersionHelper.GetVersion(); - var config = DependencyResolver.Current.GetService(); - string brand = config == null ? "" : config.Current.Brand; - -

    - This is the @brand - @if (ver.Present) - { - @: version @ver.Version. - if(!String.IsNullOrEmpty(ver.ShortCommit)) { - - Deployed from - @if(ver.CommitUri != null) { - @: @ver.ShortCommit. - } else { - @: @ver.ShortCommit. - } - - } - - if(!String.IsNullOrEmpty(ver.Branch)) { - - Built on - @if(ver.BranchUri != null) { - @:@ver.Branch. - } else { - @: @ver.Branch. - } - - } - - if(ver.BuildDateUtc != DateTime.MinValue) { - @: Built at @ver.BuildDateUtc.ToNuGetShortDateString(). - } - } else { - @:. - } - - @* A little quick-n-dirty code to display the current machine *@ - @* In Azure, we want the Instance ID. The Machine Name is total garbage *@ - You are on @HostMachine.Name. -

    -} - -@helper AnalyticsScript() -{ - var config = DependencyResolver.Current.GetService(); - if(config != null) { - var propertyId = config.Current.GoogleAnalyticsPropertyId; - if (propertyId != null) - { - @Analytics.GetGoogleAsyncHtml(propertyId) - } - } -} - +@using System.Web.Mvc +@using Microsoft.Web.Helpers +@using NuGetGallery +@using NuGetGallery.Helpers +@using NuGetGallery.Configuration + +@helper PreviousNextPager(IPreviousNextPager pager) +{ + if (pager.HasNextPage || pager.HasPreviousPage) + { +
      + + +
    + } +} + +@helper Option(string value, string label, string currentValue) +{ + +} + +@helper UploadSequence(int currentStep) +{ +
      + @SequenceStep(1, "Upload", currentStep) + @SequenceStep(2, "Verify Details", currentStep) +
    +
    +} + +@helper SequenceStep(int step, string caption, int currentStep) +{ + if (currentStep > step) + { +
  • @caption
  • + } + else if (currentStep == step) + { +
  • @caption
  • + } + else + { +
  • @caption
  • + } +} + +@helper OwnersGravatar(IEnumerable owners, int size, UrlHelper url, bool showName = true) +{ +
      + @foreach (var owner in owners) + { +
    • + @OwnerGravatar(owner, size, url, showName) +
    • + } +
    +} + +@helper OwnerGravatar(User owner, int size, UrlHelper url, bool showName = true) +{ + + @if (!String.IsNullOrEmpty(owner.EmailAddress)) + { + @GravatarImage(owner.EmailAddress, owner.Username, size) + } + @if (showName) + { + @owner.Username + } + +} + +@helper GravatarImage(string email, string username, int size) +{ + var gravatarHtml = Gravatar.GetHtml(email, size, "retro", GravatarRating.G, attributes: new { width = size, height = size, title = username, @class = "owner-image" }); + if (gravatarHtml != null && Request.IsSecureConnection) + { + gravatarHtml = new HtmlString(gravatarHtml.ToHtmlString().Replace("http://www.gravatar.com/", "https://secure.gravatar.com/")); + } + @gravatarHtml +} + +@helper WriteMeta(string name, string val) { + if(!String.IsNullOrEmpty(val)) { + + } +} + +@helper ReleaseMeta() { + // Get Version info + var ver = ApplicationVersionHelper.GetVersion(); + + if(ver.Present) { + WriteMeta("branch", ver.Branch); + WriteMeta("commit", ver.ShortCommit); + WriteMeta("time", ver.BuildDateUtc == DateTime.MinValue ? null : ver.BuildDateUtc.ToString("O")); + } +} + +@helper InstrumentationScript() +{ + // Get instrumentation key + var config = DependencyResolver.Current.GetService(); + var iKey = config == null ? string.Empty : config.Current.AppInsightsInstrumentationKey; + var samplingPct = config == null ? 100 : config.Current.AppInsightsSamplingPercentage; + + if (!string.IsNullOrEmpty(iKey)) + { + + + } +} + +@helper ReleaseTag() +{ + // Get Version info and gallery brand name + var ver = ApplicationVersionHelper.GetVersion(); + var config = DependencyResolver.Current.GetService(); + string brand = config == null ? "" : config.Current.Brand; + +

    + This is the @brand + @if (ver.Present) + { + @: version @ver.Version. + if(!String.IsNullOrEmpty(ver.ShortCommit)) { + + Deployed from + @if(ver.CommitUri != null) { + @: @ver.ShortCommit. + } else { + @: @ver.ShortCommit. + } + + } + + if(!String.IsNullOrEmpty(ver.Branch)) { + + Built on + @if(ver.BranchUri != null) { + @:@ver.Branch. + } else { + @: @ver.Branch. + } + + } + + if(ver.BuildDateUtc != DateTime.MinValue) { + @: Built at @ver.BuildDateUtc.ToNuGetShortDateString(). + } + } else { + @:. + } + + @* A little quick-n-dirty code to display the current machine *@ + @* In Azure, we want the Instance ID. The Machine Name is total garbage *@ + You are on @HostMachine.Name. +

    +} + +@helper AnalyticsScript() +{ + var config = DependencyResolver.Current.GetService(); + if(config != null) { + var propertyId = config.Current.GoogleAnalyticsPropertyId; + if (!string.IsNullOrEmpty(propertyId)) + { + + } + } +} + @helper AccordionBar( string groupName, WebViewPage page, @@ -264,7 +272,7 @@ string dataKey = "___AccordionCounter_" + groupName; int lastId = (int)(HttpContext.Current.Items[dataKey] ?? 0); int id = lastId + 1; HttpContext.Current.Items[dataKey] = id; -string name = groupName + "-" + id.ToString(); +string name = groupName + "-" + id.ToString(); string actionsId = name + "-actions"; var hlp = new AccordionHelper(name, formModelStatePrefix, expanded, page); @@ -293,4 +301,4 @@ var hlp = new AccordionHelper(name, formModelStatePrefix, expanded, page); } -} +} diff --git a/src/NuGetGallery/Scripts/nugetgallery.js b/src/NuGetGallery/Scripts/nugetgallery.js index a341be6229..164b41fdc7 100644 --- a/src/NuGetGallery/Scripts/nugetgallery.js +++ b/src/NuGetGallery/Scripts/nugetgallery.js @@ -13,6 +13,8 @@ attachPlugins(); sniffClickonce(); + + addOutboundTrackingEvent(); }); // Add validator that ensures provided value is NOT equal to a specified value. @@ -24,6 +26,37 @@ $.validator.unobtrusive.adapters.addBool("mandatory", "required"); $.validator.unobtrusive.adapters.addSingleVal('notequal', 'disallowed'); + // Source: https://developers.google.com/analytics/devguides/collection/analyticsjs/sending-hits + function createFunctionWithTimeout(callback, opt_timeout) { + var called = false; + function fn() { + if (!called) { + called = true; + callback(); + } + } + setTimeout(fn, opt_timeout || 1000); + return fn; + }; + + function addOutboundTrackingEvent() { + // Handle Google analytics tracking event on specific links. + $.each($('a[data-track]'), function () { + $(this).click(function (e) { + var href = $(this).attr('href'); + var category = $(this).attr('data-track'); + if (ga && href && category) { + ga('send', 'event', category, 'click', href, { + 'transport': 'beacon', + 'hitCallback': createFunctionWithTimeout(function () { + document.location = href; + }) + }); + } + }); + }); + } + function padInt(i, size) { var s = i.toString(); while (s.length < size) s = "0" + s; diff --git a/src/NuGetGallery/Views/Packages/DisplayPackage.cshtml b/src/NuGetGallery/Views/Packages/DisplayPackage.cshtml index 30782909f1..c1ba9a1f58 100644 --- a/src/NuGetGallery/Views/Packages/DisplayPackage.cshtml +++ b/src/NuGetGallery/Views/Packages/DisplayPackage.cshtml @@ -53,14 +53,14 @@ @if (PackageHelper.ShouldRenderUrl(Model.ProjectUrl)) {
  • - + Project Site
  • } @if (PackageHelper.ShouldRenderUrl(Model.LicenseUrl)) { -
  • License
  • +
  • License
  • }
  • Contact Owners
  • @if ((Model.IsOwner(User) || User.IsAdministrator())) @@ -82,7 +82,7 @@ @if (!Model.Deleted) { -
  • Download (how-to)
  • +
  • Download (how-to)
  • Open in Package Explorer
  • } From a3d7e41864239e10f14a737c434fe068442d9412 Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Tue, 20 Jun 2017 11:47:06 -0700 Subject: [PATCH 2/2] Address comment --- src/NuGetGallery/Scripts/nugetgallery.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NuGetGallery/Scripts/nugetgallery.js b/src/NuGetGallery/Scripts/nugetgallery.js index 164b41fdc7..bbd2e5d8d5 100644 --- a/src/NuGetGallery/Scripts/nugetgallery.js +++ b/src/NuGetGallery/Scripts/nugetgallery.js @@ -46,6 +46,7 @@ var href = $(this).attr('href'); var category = $(this).attr('data-track'); if (ga && href && category) { + e.preventDefault(); ga('send', 'event', category, 'click', href, { 'transport': 'beacon', 'hitCallback': createFunctionWithTimeout(function () {