Small changes around how we work with custom events in the analytics

This commit is contained in:
tidusjar 2016-06-22 12:59:01 +01:00
parent 7b57e3fffc
commit 63e0d0e531
14 changed files with 1498 additions and 1340 deletions

View file

@ -0,0 +1,52 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: DateTimeHelperTests.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System.Collections.Generic;
using NUnit.Framework;
namespace PlexRequests.Helpers.Tests
{
[TestFixture]
public class CookieHelperTests
{
[TestCaseSource(nameof(GetAnalyticsClientId))]
public string TestGetAnalyticsClientId(Dictionary<string,string> cookies)
{
return CookieHelper.GetAnalyticClientId(cookies);
}
private static IEnumerable<TestCaseData> GetAnalyticsClientId
{
get
{
yield return new TestCaseData(new Dictionary<string, string>()).Returns(string.Empty);
yield return new TestCaseData(new Dictionary<string, string> { { "_ga", "GA1.1.306549087.1464005217" } }).Returns("306549087.1464005217");
yield return new TestCaseData(new Dictionary<string,string> { {"_ga", "GA1.1.306549087" } }).Returns(string.Empty);
}
}
}
}

View file

@ -71,6 +71,7 @@
</Otherwise> </Otherwise>
</Choose> </Choose>
<ItemGroup> <ItemGroup>
<Compile Include="CookieHelperTests.cs" />
<Compile Include="DateTimeHelperTests.cs" /> <Compile Include="DateTimeHelperTests.cs" />
<Compile Include="PasswordHasherTests.cs" /> <Compile Include="PasswordHasherTests.cs" />
<Compile Include="HtmlRemoverTests.cs" /> <Compile Include="HtmlRemoverTests.cs" />

View file

@ -34,6 +34,7 @@ namespace PlexRequests.Helpers.Analytics
Create, Create,
Save, Save,
Update, Update,
Start Start,
View
} }
} }

View file

@ -47,52 +47,51 @@ namespace PlexRequests.Helpers.Analytics
private static Logger Log = LogManager.GetCurrentClassLogger(); private static Logger Log = LogManager.GetCurrentClassLogger();
public void TrackEvent(Category category, Action action, string label, string username, int? value = null) public void TrackEvent(Category category, Action action, string label, string username, string clientId, int? value = null)
{ {
var cat = category.ToString(); var cat = category.ToString();
var act = action.ToString(); var act = action.ToString();
Track(HitType.@event, username, cat, act, label, value); Track(HitType.@event, username, cat, act, label, clientId, value);
} }
public async Task TrackEventAsync(Category category, Action action, string label, string username, int? value = null) public async Task TrackEventAsync(Category category, Action action, string label, string username, string clientId, int? value = null)
{ {
var cat = category.ToString(); var cat = category.ToString();
var act = action.ToString(); var act = action.ToString();
await TrackAsync(HitType.@event, username, cat, act, label, value); await TrackAsync(HitType.@event, username, cat, act, clientId, label, value);
} }
public void TrackPageview(Category category, Action action, string label, string username, int? value = null) public void TrackPageview(Category category, Action action, string label, string username, string clientId, int? value = null)
{ {
var cat = category.ToString(); var cat = category.ToString();
var act = action.ToString(); var act = action.ToString();
Track(HitType.@pageview, username, cat, act, label, value); Track(HitType.@pageview, username, cat, act, clientId, label, value);
} }
public async Task TrackPageviewAsync(Category category, Action action, string label, string username, int? value = null) public async Task TrackPageviewAsync(Category category, Action action, string label, string username, string clientId, int? value = null)
{ {
var cat = category.ToString(); var cat = category.ToString();
var act = action.ToString(); var act = action.ToString();
await TrackAsync(HitType.@pageview, username, cat, act, label, value); await TrackAsync(HitType.@pageview, username, cat, act, clientId, label, value);
} }
public void TrackException(string message, string username, bool fatal) public void TrackException(string message, string username, string clientId, bool fatal)
{ {
var fatalInt = fatal ? 1 : 0; var fatalInt = fatal ? 1 : 0;
Track(HitType.exception, message, fatalInt, username); Track(HitType.exception, message, fatalInt, username, clientId);
} }
public async Task TrackExceptionAsync(string message, string username, bool fatal) public async Task TrackExceptionAsync(string message, string username, string clientId, bool fatal)
{ {
var fatalInt = fatal ? 1 : 0; var fatalInt = fatal ? 1 : 0;
await TrackAsync(HitType.exception, message, fatalInt, username); await TrackAsync(HitType.exception, message, fatalInt, username, clientId);
} }
private void Track(HitType type, string username, string category, string action, string label, int? value = null) private void Track(HitType type, string username, string category, string action, string clientId, string label, int? value = null)
{ {
if (string.IsNullOrEmpty(category)) throw new ArgumentNullException(nameof(category)); if (string.IsNullOrEmpty(category)) throw new ArgumentNullException(nameof(category));
if (string.IsNullOrEmpty(action)) throw new ArgumentNullException(nameof(action)); if (string.IsNullOrEmpty(action)) throw new ArgumentNullException(nameof(action));
if (string.IsNullOrEmpty(username)) throw new ArgumentNullException(nameof(username));
var postData = BuildRequestData(type, username, category, action, label, value, null, null); var postData = BuildRequestData(type, username, category, action, clientId, label, value, null, null);
var postDataString = postData var postDataString = postData
.Aggregate("", (data, next) => string.Format($"{data}&{next.Key}={HttpUtility.UrlEncode(next.Value)}")) .Aggregate("", (data, next) => string.Format($"{data}&{next.Key}={HttpUtility.UrlEncode(next.Value)}"))
@ -101,13 +100,12 @@ namespace PlexRequests.Helpers.Analytics
SendRequest(postDataString); SendRequest(postDataString);
} }
private async Task TrackAsync(HitType type, string username, string category, string action, string label, int? value = null) private async Task TrackAsync(HitType type, string username, string category, string action, string clientId, string label, int? value = null)
{ {
if (string.IsNullOrEmpty(category)) throw new ArgumentNullException(nameof(category)); if (string.IsNullOrEmpty(category)) throw new ArgumentNullException(nameof(category));
if (string.IsNullOrEmpty(action)) throw new ArgumentNullException(nameof(action)); if (string.IsNullOrEmpty(action)) throw new ArgumentNullException(nameof(action));
if (string.IsNullOrEmpty(username)) throw new ArgumentNullException(nameof(username));
var postData = BuildRequestData(type, username, category, action, label, value, null, null); var postData = BuildRequestData(type, username, category, action, clientId, label, value, null, null);
var postDataString = postData var postDataString = postData
.Aggregate("", (data, next) => string.Format($"{data}&{next.Key}={HttpUtility.UrlEncode(next.Value)}")) .Aggregate("", (data, next) => string.Format($"{data}&{next.Key}={HttpUtility.UrlEncode(next.Value)}"))
@ -115,12 +113,11 @@ namespace PlexRequests.Helpers.Analytics
await SendRequestAsync(postDataString); await SendRequestAsync(postDataString);
} }
private async Task TrackAsync(HitType type, string message, int fatal, string username) private async Task TrackAsync(HitType type, string message, int fatal, string username, string clientId)
{ {
if (string.IsNullOrEmpty(message)) throw new ArgumentNullException(nameof(message)); if (string.IsNullOrEmpty(message)) throw new ArgumentNullException(nameof(message));
if (string.IsNullOrEmpty(username)) throw new ArgumentNullException(nameof(username));
var postData = BuildRequestData(type, username, null, null, null, null, message, fatal); var postData = BuildRequestData(type, username, null, null, null, clientId, null, message, fatal);
var postDataString = postData var postDataString = postData
.Aggregate("", (data, next) => string.Format($"{data}&{next.Key}={HttpUtility.UrlEncode(next.Value)}")) .Aggregate("", (data, next) => string.Format($"{data}&{next.Key}={HttpUtility.UrlEncode(next.Value)}"))
@ -129,12 +126,12 @@ namespace PlexRequests.Helpers.Analytics
await SendRequestAsync(postDataString); await SendRequestAsync(postDataString);
} }
private void Track(HitType type, string message, int fatal, string username) private void Track(HitType type, string message, int fatal, string username, string clientId)
{ {
if (string.IsNullOrEmpty(message)) throw new ArgumentNullException(nameof(message)); if (string.IsNullOrEmpty(message)) throw new ArgumentNullException(nameof(message));
if (string.IsNullOrEmpty(username)) throw new ArgumentNullException(nameof(username)); if (string.IsNullOrEmpty(username)) throw new ArgumentNullException(nameof(username));
var postData = BuildRequestData(type, username, null, null, null, null, message, fatal); var postData = BuildRequestData(type, username, null, null, null, clientId, null, message, fatal);
var postDataString = postData var postDataString = postData
.Aggregate("", (data, next) => string.Format($"{data}&{next.Key}={HttpUtility.UrlEncode(next.Value)}")) .Aggregate("", (data, next) => string.Format($"{data}&{next.Key}={HttpUtility.UrlEncode(next.Value)}"))
@ -196,20 +193,24 @@ namespace PlexRequests.Helpers.Analytics
} }
} }
private Dictionary<string, string> BuildRequestData(HitType type, string username, string category, string action, string label, int? value, string exceptionDescription, int? fatal) private Dictionary<string, string> BuildRequestData(HitType type, string username, string category, string action, string clientId, string label, int? value, string exceptionDescription, int? fatal)
{ {
var postData = new Dictionary<string, string> var postData = new Dictionary<string, string>
{ {
{ "v", "1" }, { "v", "1" },
{ "tid", TrackingId }, { "tid", TrackingId },
{ "t", type.ToString() }, { "t", type.ToString() }
{"cid", Guid.NewGuid().ToString() }
}; };
if (!string.IsNullOrEmpty(username)) if (!string.IsNullOrEmpty(username))
{ {
postData.Add("uid", username); postData.Add("uid", username);
} }
postData.Add("cid", !string.IsNullOrEmpty(clientId)
? clientId
: Guid.NewGuid().ToString());
if (!string.IsNullOrEmpty(label)) if (!string.IsNullOrEmpty(label))
{ {
postData.Add("el", label); postData.Add("el", label);

View file

@ -37,8 +37,9 @@ namespace PlexRequests.Helpers.Analytics
/// <param name="action">The action.</param> /// <param name="action">The action.</param>
/// <param name="label">The label.</param> /// <param name="label">The label.</param>
/// <param name="username">The username.</param> /// <param name="username">The username.</param>
/// <param name="clientId">The client identifier.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
void TrackEvent(Category category, Action action, string label, string username, int? value = null); void TrackEvent(Category category, Action action, string label, string username, string clientId, int? value = null);
/// <summary> /// <summary>
/// Tracks the event asynchronous. /// Tracks the event asynchronous.
@ -47,9 +48,10 @@ namespace PlexRequests.Helpers.Analytics
/// <param name="action">The action.</param> /// <param name="action">The action.</param>
/// <param name="label">The label.</param> /// <param name="label">The label.</param>
/// <param name="username">The username.</param> /// <param name="username">The username.</param>
/// <param name="clientId">The client identifier.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <returns></returns> /// <returns></returns>
Task TrackEventAsync(Category category, Action action, string label, string username, int? value = null); Task TrackEventAsync(Category category, Action action, string label, string username, string clientId, int? value = null);
/// <summary> /// <summary>
/// Tracks the page view. /// Tracks the page view.
@ -58,8 +60,9 @@ namespace PlexRequests.Helpers.Analytics
/// <param name="action">The action.</param> /// <param name="action">The action.</param>
/// <param name="label">The label.</param> /// <param name="label">The label.</param>
/// <param name="username">The username.</param> /// <param name="username">The username.</param>
/// <param name="clientId">The client identifier.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
void TrackPageview(Category category, Action action, string label, string username, int? value = null); void TrackPageview(Category category, Action action, string label, string username, string clientId, int? value = null);
/// <summary> /// <summary>
/// Tracks the page view asynchronous. /// Tracks the page view asynchronous.
@ -68,25 +71,28 @@ namespace PlexRequests.Helpers.Analytics
/// <param name="action">The action.</param> /// <param name="action">The action.</param>
/// <param name="label">The label.</param> /// <param name="label">The label.</param>
/// <param name="username">The username.</param> /// <param name="username">The username.</param>
/// <param name="clientId">The client identifier.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <returns></returns> /// <returns></returns>
Task TrackPageviewAsync(Category category, Action action, string label, string username, int? value = null); Task TrackPageviewAsync(Category category, Action action, string label, string username, string clientId, int? value = null);
/// <summary> /// <summary>
/// Tracks the exception. /// Tracks the exception.
/// </summary> /// </summary>
/// <param name="message">The message.</param> /// <param name="message">The message.</param>
/// <param name="username">The username.</param> /// <param name="username">The username.</param>
/// <param name="clientId">The client identifier.</param>
/// <param name="fatal">if set to <c>true</c> [fatal].</param> /// <param name="fatal">if set to <c>true</c> [fatal].</param>
void TrackException(string message, string username, bool fatal); void TrackException(string message, string username, string clientId, bool fatal);
/// <summary> /// <summary>
/// Tracks the exception asynchronous. /// Tracks the exception asynchronous.
/// </summary> /// </summary>
/// <param name="message">The message.</param> /// <param name="message">The message.</param>
/// <param name="username">The username.</param> /// <param name="username">The username.</param>
/// <param name="clientId">The client identifier.</param>
/// <param name="fatal">if set to <c>true</c> [fatal].</param> /// <param name="fatal">if set to <c>true</c> [fatal].</param>
/// <returns></returns> /// <returns></returns>
Task TrackExceptionAsync(string message, string username, bool fatal); Task TrackExceptionAsync(string message, string username, string clientId, bool fatal);
} }
} }

View file

@ -0,0 +1,57 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: CookieHelper.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
namespace PlexRequests.Helpers
{
public static class CookieHelper
{
private const string GaCookie = "_ga";
/// <summary>
/// Gets the analytic client identifier.
/// <para>Example: Value = "GA1.1.306549087.1464005217"</para>
/// </summary>
/// <param name="cookies">The cookies.</param>
/// <returns></returns>
public static string GetAnalyticClientId(IDictionary<string, string> cookies)
{
var outString = string.Empty;
if (cookies.TryGetValue(GaCookie, out outString))
{
var split = outString.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
return split.Length < 4
? string.Empty
: $"{split[2]}.{split[3]}";
}
return string.Empty;
}
}
}

View file

@ -69,6 +69,7 @@
<Compile Include="Analytics\IAnalytics.cs" /> <Compile Include="Analytics\IAnalytics.cs" />
<Compile Include="AssemblyHelper.cs" /> <Compile Include="AssemblyHelper.cs" />
<Compile Include="ByteConverterHelper.cs" /> <Compile Include="ByteConverterHelper.cs" />
<Compile Include="CookieHelper.cs" />
<Compile Include="DateTimeHelper.cs" /> <Compile Include="DateTimeHelper.cs" />
<Compile Include="Exceptions\ApiRequestException.cs" /> <Compile Include="Exceptions\ApiRequestException.cs" />
<Compile Include="Exceptions\ApplicationSettingsException.cs" /> <Compile Include="Exceptions\ApplicationSettingsException.cs" />

View file

@ -47,6 +47,7 @@ using PlexRequests.Store.Repository;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
using PlexRequests.UI.Modules; using PlexRequests.UI.Modules;
using PlexRequests.Helpers; using PlexRequests.Helpers;
using PlexRequests.Helpers.Analytics;
using PlexRequests.UI.Helpers; using PlexRequests.UI.Helpers;
namespace PlexRequests.UI.Tests namespace PlexRequests.UI.Tests
@ -78,6 +79,7 @@ namespace PlexRequests.UI.Tests
private Mock<ISettingsService<SlackNotificationSettings>> SlackSettings { get; set; } private Mock<ISettingsService<SlackNotificationSettings>> SlackSettings { get; set; }
private Mock<ISettingsService<LandingPageSettings>> LandingPageSettings { get; set; } private Mock<ISettingsService<LandingPageSettings>> LandingPageSettings { get; set; }
private Mock<ISlackApi> SlackApi { get; set; } private Mock<ISlackApi> SlackApi { get; set; }
private Mock<IAnalytics> IAnalytics { get; set; }
private ConfigurableBootstrapper Bootstrapper { get; set; } private ConfigurableBootstrapper Bootstrapper { get; set; }
@ -115,6 +117,7 @@ namespace PlexRequests.UI.Tests
LandingPageSettings = new Mock<ISettingsService<LandingPageSettings>>(); LandingPageSettings = new Mock<ISettingsService<LandingPageSettings>>();
ScheduledJobsSettingsMock = new Mock<ISettingsService<ScheduledJobsSettings>>(); ScheduledJobsSettingsMock = new Mock<ISettingsService<ScheduledJobsSettings>>();
RecorderMock = new Mock<IJobRecord>(); RecorderMock = new Mock<IJobRecord>();
IAnalytics = new Mock<IAnalytics>();
Bootstrapper = new ConfigurableBootstrapper(with => Bootstrapper = new ConfigurableBootstrapper(with =>
@ -136,6 +139,7 @@ namespace PlexRequests.UI.Tests
with.Dependency(PushoverSettings.Object); with.Dependency(PushoverSettings.Object);
with.Dependency(PushoverApi.Object); with.Dependency(PushoverApi.Object);
with.Dependency(NotificationService.Object); with.Dependency(NotificationService.Object);
with.Dependency(IAnalytics.Object);
with.Dependency(HeadphonesSettings.Object); with.Dependency(HeadphonesSettings.Object);
with.Dependency(Cache.Object); with.Dependency(Cache.Object);
with.Dependency(Log.Object); with.Dependency(Log.Object);

View file

@ -40,6 +40,7 @@ using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Plex; using PlexRequests.Api.Models.Plex;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers.Analytics;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
using PlexRequests.UI.Modules; using PlexRequests.UI.Modules;
@ -53,6 +54,7 @@ namespace PlexRequests.UI.Tests
private Mock<ISettingsService<LandingPageSettings>> LandingPageMock { get; set; } private Mock<ISettingsService<LandingPageSettings>> LandingPageMock { get; set; }
private ConfigurableBootstrapper Bootstrapper { get; set; } private ConfigurableBootstrapper Bootstrapper { get; set; }
private Mock<IPlexApi> PlexMock { get; set; } private Mock<IPlexApi> PlexMock { get; set; }
private Mock<IAnalytics> IAnalytics { get; set; }
[SetUp] [SetUp]
public void Setup() public void Setup()
@ -64,6 +66,7 @@ namespace PlexRequests.UI.Tests
PlexRequestMock.Setup(x => x.GetSettings()).Returns(new PlexRequestSettings()); PlexRequestMock.Setup(x => x.GetSettings()).Returns(new PlexRequestSettings());
PlexRequestMock.Setup(x => x.GetSettingsAsync()).Returns(Task.FromResult(new PlexRequestSettings())); PlexRequestMock.Setup(x => x.GetSettingsAsync()).Returns(Task.FromResult(new PlexRequestSettings()));
LandingPageMock.Setup(x => x.GetSettings()).Returns(new LandingPageSettings()); LandingPageMock.Setup(x => x.GetSettings()).Returns(new LandingPageSettings());
IAnalytics = new Mock<IAnalytics>();
Bootstrapper = new ConfigurableBootstrapper(with => Bootstrapper = new ConfigurableBootstrapper(with =>
{ {
with.Module<UserLoginModule>(); with.Module<UserLoginModule>();
@ -71,6 +74,7 @@ namespace PlexRequests.UI.Tests
with.Dependency(AuthMock.Object); with.Dependency(AuthMock.Object);
with.Dependency(PlexMock.Object); with.Dependency(PlexMock.Object);
with.Dependency(LandingPageMock.Object); with.Dependency(LandingPageMock.Object);
with.Dependency(IAnalytics.Object);
with.RootPathProvider<TestRootPathProvider>(); with.RootPathProvider<TestRootPathProvider>();
}); });
} }

View file

@ -32,7 +32,6 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic; using System.Collections.Generic;
using System.Dynamic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@ -52,6 +51,7 @@ using PlexRequests.Api.Interfaces;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers; using PlexRequests.Helpers;
using PlexRequests.Helpers.Analytics;
using PlexRequests.Helpers.Exceptions; using PlexRequests.Helpers.Exceptions;
using PlexRequests.Services.Interfaces; using PlexRequests.Services.Interfaces;
using PlexRequests.Services.Notification; using PlexRequests.Services.Notification;
@ -60,6 +60,7 @@ using PlexRequests.Store.Repository;
using PlexRequests.UI.Helpers; using PlexRequests.UI.Helpers;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
using Action = PlexRequests.Helpers.Analytics.Action;
namespace PlexRequests.UI.Modules namespace PlexRequests.UI.Modules
{ {
@ -89,6 +90,7 @@ namespace PlexRequests.UI.Modules
private ISettingsService<ScheduledJobsSettings> ScheduledJobSettings { get; } private ISettingsService<ScheduledJobsSettings> ScheduledJobSettings { get; }
private ISlackApi SlackApi { get; } private ISlackApi SlackApi { get; }
private IJobRecord JobRecorder { get; } private IJobRecord JobRecorder { get; }
private IAnalytics Analytics { get; }
private static Logger Log = LogManager.GetCurrentClassLogger(); private static Logger Log = LogManager.GetCurrentClassLogger();
public AdminModule(ISettingsService<PlexRequestSettings> prService, public AdminModule(ISettingsService<PlexRequestSettings> prService,
@ -111,7 +113,7 @@ namespace PlexRequests.UI.Modules
ISettingsService<LogSettings> logs, ISettingsService<LogSettings> logs,
ICacheProvider cache, ISettingsService<SlackNotificationSettings> slackSettings, ICacheProvider cache, ISettingsService<SlackNotificationSettings> slackSettings,
ISlackApi slackApi, ISettingsService<LandingPageSettings> lp, ISlackApi slackApi, ISettingsService<LandingPageSettings> lp,
ISettingsService<ScheduledJobsSettings> scheduler, IJobRecord rec) : base("admin", prService) ISettingsService<ScheduledJobsSettings> scheduler, IJobRecord rec, IAnalytics analytics) : base("admin", prService)
{ {
PrService = prService; PrService = prService;
CpService = cpService; CpService = cpService;
@ -137,6 +139,7 @@ namespace PlexRequests.UI.Modules
LandingSettings = lp; LandingSettings = lp;
ScheduledJobSettings = scheduler; ScheduledJobSettings = scheduler;
JobRecorder = rec; JobRecorder = rec;
Analytics = analytics;
this.RequiresClaims(UserClaims.Admin); this.RequiresClaims(UserClaims.Admin);
@ -237,7 +240,7 @@ namespace PlexRequests.UI.Modules
return View["Settings", settings]; return View["Settings", settings];
} }
private Response SaveAdmin() private async Task<Response> SaveAdmin()
{ {
var model = this.Bind<PlexRequestSettings>(); var model = this.Bind<PlexRequestSettings>();
var valid = this.Validate(model); var valid = this.Validate(model);
@ -254,6 +257,8 @@ namespace PlexRequests.UI.Modules
} }
} }
var result = PrService.SaveSettings(model); var result = PrService.SaveSettings(model);
await Analytics.TrackEventAsync(Category.Admin, Action.Save, "PlexRequestSettings", Username, CookieHelper.GetAnalyticClientId(Cookies));
return Response.AsJson(result return Response.AsJson(result
? new JsonResponseModel { Result = true } ? new JsonResponseModel { Result = true }
: new JsonResponseModel { Result = false, Message = "We could not save to the database, please try again" }); : new JsonResponseModel { Result = false, Message = "We could not save to the database, please try again" });

View file

@ -25,6 +25,7 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Nancy; using Nancy;
@ -81,13 +82,22 @@ namespace PlexRequests.UI.Modules
get get
{ {
if (string.IsNullOrEmpty(_username)) if (string.IsNullOrEmpty(_username))
{
try
{ {
_username = Session[SessionKeys.UsernameKey].ToString(); _username = Session[SessionKeys.UsernameKey].ToString();
} }
catch (Exception)
{
return string.Empty;
}
}
return _username; return _username;
} }
} }
protected IDictionary<string, string> Cookies => Request.Cookies;
protected bool IsAdmin protected bool IsAdmin
{ {
get get

View file

@ -39,16 +39,21 @@ using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Plex; using PlexRequests.Api.Models.Plex;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers;
using PlexRequests.Helpers.Analytics;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
using Action = PlexRequests.Helpers.Analytics.Action;
namespace PlexRequests.UI.Modules namespace PlexRequests.UI.Modules
{ {
public class UserLoginModule : BaseModule public class UserLoginModule : BaseModule
{ {
public UserLoginModule(ISettingsService<AuthenticationSettings> auth, IPlexApi api, ISettingsService<PlexRequestSettings> pr, ISettingsService<LandingPageSettings> lp) : base("userlogin", pr) public UserLoginModule(ISettingsService<AuthenticationSettings> auth, IPlexApi api, ISettingsService<PlexRequestSettings> pr, ISettingsService<LandingPageSettings> lp, IAnalytics a) : base("userlogin", pr)
{ {
AuthService = auth; AuthService = auth;
LandingPageSettings = lp; LandingPageSettings = lp;
Analytics = a;
Api = api; Api = api;
Get["/", true] = async (x, ct) => await Index(); Get["/", true] = async (x, ct) => await Index();
Post["/"] = x => LoginUser(); Post["/"] = x => LoginUser();
@ -58,6 +63,7 @@ namespace PlexRequests.UI.Modules
private ISettingsService<AuthenticationSettings> AuthService { get; } private ISettingsService<AuthenticationSettings> AuthService { get; }
private ISettingsService<LandingPageSettings> LandingPageSettings { get; } private ISettingsService<LandingPageSettings> LandingPageSettings { get; }
private IPlexApi Api { get; } private IPlexApi Api { get; }
private IAnalytics Analytics { get; }
private static Logger Log = LogManager.GetCurrentClassLogger(); private static Logger Log = LogManager.GetCurrentClassLogger();
@ -71,8 +77,17 @@ namespace PlexRequests.UI.Modules
if (landingSettings.Enabled) if (landingSettings.Enabled)
{ {
if (landingSettings.BeforeLogin) if (landingSettings.BeforeLogin)
{ {
await
Analytics.TrackEventAsync(
Category.LandingPage,
Action.View,
"Going To LandingPage before login",
Username,
CookieHelper.GetAnalyticClientId(Cookies));
var model = new LandingPageViewModel var model = new LandingPageViewModel
{ {
Enabled = landingSettings.Enabled, Enabled = landingSettings.Enabled,
@ -168,7 +183,7 @@ namespace PlexRequests.UI.Modules
if (!authenticated) if (!authenticated)
{ {
return Response.AsJson(new JsonResponseModel {Result = false, Message = "Incorrect User or Password"}); return Response.AsJson(new JsonResponseModel { Result = false, Message = "Incorrect User or Password" });
} }
var landingSettings = LandingPageSettings.GetSettings(); var landingSettings = LandingPageSettings.GetSettings();
@ -178,7 +193,7 @@ namespace PlexRequests.UI.Modules
if (!landingSettings.BeforeLogin) if (!landingSettings.BeforeLogin)
return Response.AsJson(new JsonResponseModel { Result = true, Message = "landing" }); return Response.AsJson(new JsonResponseModel { Result = true, Message = "landing" });
} }
return Response.AsJson(new JsonResponseModel {Result = true, Message = "search" }); return Response.AsJson(new JsonResponseModel { Result = true, Message = "search" });
} }

View file

@ -45,6 +45,7 @@ namespace PlexRequests.UI.Validators
RuleFor(x => x.BaseUrl).NotEqual("updatechecker").WithMessage("You cannot use 'updatechecker' as this is reserved by the application."); RuleFor(x => x.BaseUrl).NotEqual("updatechecker").WithMessage("You cannot use 'updatechecker' as this is reserved by the application.");
RuleFor(x => x.BaseUrl).NotEqual("usermanagement").WithMessage("You cannot use 'usermanagement' as this is reserved by the application."); RuleFor(x => x.BaseUrl).NotEqual("usermanagement").WithMessage("You cannot use 'usermanagement' as this is reserved by the application.");
RuleFor(x => x.BaseUrl).NotEqual("api").WithMessage("You cannot use 'api' as this is reserved by the application."); RuleFor(x => x.BaseUrl).NotEqual("api").WithMessage("You cannot use 'api' as this is reserved by the application.");
RuleFor(x => x.BaseUrl).NotEqual("landing").WithMessage("You cannot use 'landing' as this is reserved by the application.");
} }
} }
} }

View file

@ -17,7 +17,7 @@
<title>Plex Requests</title> <title>Plex Requests</title>
<!-- Styles --> <!-- Styles -->
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
@Html.LoadAnalytics()
@Html.LoadAssets() @Html.LoadAssets()
</head> </head>
<body> <body>