diff --git a/PlexRequests.Core.Tests/AuthenticationSettingsTests.cs b/PlexRequests.Core.Tests/AuthenticationSettingsTests.cs index 784117cfa..ba4a666da 100644 --- a/PlexRequests.Core.Tests/AuthenticationSettingsTests.cs +++ b/PlexRequests.Core.Tests/AuthenticationSettingsTests.cs @@ -1,59 +1,116 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: AuthenticationSettingsTests.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 NUnit.Framework; - -using PlexRequests.Core.SettingModels; - -namespace PlexRequests.Core.Tests -{ - [TestFixture] - public class AuthenticationSettingsTests - { - [Test, TestCaseSource(nameof(UserData))] - public void DeniedUserListTest(string users, string[] expected) - { - var model = new AuthenticationSettings { DeniedUsers = users }; - - var result = model.DeniedUserList; - - Assert.That(result.Count, Is.EqualTo(expected.Length)); - for (var i = 0; i < expected.Length; i++) - { - Assert.That(result[i], Is.EqualTo(expected[i])); - } - } - - static readonly object[] UserData = - { - new object[] { "john", new [] {"john"} }, - new object[] { "john , abc ,", new [] {"john", "abc"} }, - new object[] { "john,, cde", new [] {"john", "cde"} }, - new object[] { "john,,, aaa , baaa , ", new [] {"john","aaa","baaa"} }, - new object[] { "john, aaa , baaa , maaa, caaa", new [] {"john","aaa","baaa", "maaa", "caaa"} }, - }; - } +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: AuthenticationSettingsTests.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; + +using NUnit.Framework; + +using PlexRequests.Core.Models; +using PlexRequests.Core.SettingModels; + +namespace PlexRequests.Core.Tests +{ + [TestFixture] + public class NotificationMessageResolverTests + { + [TestCaseSource(nameof(MessageResolver))] + public string Resolve(Dictionary message, Dictionary param) + { + var n = new NotificationMessageResolver(); + var s = new NotificationSettings + { + Message = message, + CustomParamaters = param + }; + + return n.ParseMessage(s, NotificationType.NewRequest); + } + + private static IEnumerable MessageResolver + { + get + { + yield return new TestCaseData( + new Dictionary { { NotificationType.NewRequest, "There has been a new request from {Username}, Title: {Title} for {Type}" } }, + new Dictionary{{"Username", "Jamie" },{"Title", "Finding Dory" },{"Type", "Movie" }}) + .Returns("There has been a new request from Jamie, Title: Finding Dory for Movie") + .SetName("FindingDory"); + + yield return new TestCaseData( + new Dictionary { { NotificationType.NewRequest, string.Empty } }, + new Dictionary()) + .Returns(string.Empty) + .SetName("Empty Message"); + + yield return new TestCaseData( + new Dictionary { { NotificationType.NewRequest, "{{Wowwzer}} Damn}{{son}}}}" } }, + new Dictionary { {"son","HEY!"} }) + .Returns("{{Wowwzer}} Damn}{HEY!}}}") + .SetName("Multiple Curlys"); + + + yield return new TestCaseData( + new Dictionary { { NotificationType.NewRequest, "This is a message with no curlys" } }, + new Dictionary { { "son", "HEY!" } }) + .Returns("This is a message with no curlys") + .SetName("No Curlys"); + + yield return new TestCaseData( + new Dictionary { { NotificationType.NewRequest, new string(')', 5000)} }, + new Dictionary { { "son", "HEY!" } }) + .Returns(new string(')', 5000)) + .SetName("Long String"); + + + yield return new TestCaseData( + new Dictionary { { NotificationType.NewRequest, "This is a {Username} and {Username} Because {Curly}{Curly}" } }, + new Dictionary { { "Username", "HEY!" }, {"Curly","Bob"} }) + .Returns("This is a HEY! and HEY! Because BobBob") + .SetName("Double Curly"); + + yield return new TestCaseData( + new Dictionary { { NotificationType.NewRequest, "This is a {Username} and {Username} Because {Curly}{Curly}" } }, + new Dictionary { { "username", "HEY!" }, { "Curly", "Bob" } }) + .Returns("This is a {Username} and {Username} Because BobBob") + .SetName("Case sensitive"); + + yield return new TestCaseData( + new Dictionary { { NotificationType.NewRequest, "{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}{a}" } }, + new Dictionary { { "a", "b" } }) + .Returns("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") + .SetName("Lots of curlys"); + + yield return new TestCaseData( + new Dictionary { { NotificationType.NewRequest, $"{{{new string('b', 10000)}}}" } }, + new Dictionary { { new string('b', 10000), "Hello" } }) + .Returns("Hello") + .SetName("Very long Curly"); + } + } + + } } \ No newline at end of file diff --git a/PlexRequests.Core.Tests/NotificationMessageResolverTests.cs b/PlexRequests.Core.Tests/NotificationMessageResolverTests.cs new file mode 100644 index 000000000..62b03470c --- /dev/null +++ b/PlexRequests.Core.Tests/NotificationMessageResolverTests.cs @@ -0,0 +1,59 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: AuthenticationSettingsTests.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 NUnit.Framework; + +using PlexRequests.Core.SettingModels; + +namespace PlexRequests.Core.Tests +{ + [TestFixture] + public class AuthenticationSettingsTests + { + [Test, TestCaseSource(nameof(UserData))] + public void DeniedUserListTest(string users, string[] expected) + { + var model = new AuthenticationSettings { DeniedUsers = users }; + + var result = model.DeniedUserList; + + Assert.That(result.Count, Is.EqualTo(expected.Length)); + for (var i = 0; i < expected.Length; i++) + { + Assert.That(result[i], Is.EqualTo(expected[i])); + } + } + + static readonly object[] UserData = + { + new object[] { "john", new [] {"john"} }, + new object[] { "john , abc ,", new [] {"john", "abc"} }, + new object[] { "john,, cde", new [] {"john", "cde"} }, + new object[] { "john,,, aaa , baaa , ", new [] {"john","aaa","baaa"} }, + new object[] { "john, aaa , baaa , maaa, caaa", new [] {"john","aaa","baaa", "maaa", "caaa"} }, + }; + } +} \ No newline at end of file diff --git a/PlexRequests.Core.Tests/PlexRequests.Core.Tests.csproj b/PlexRequests.Core.Tests/PlexRequests.Core.Tests.csproj index b0d9a25ef..3a456e243 100644 --- a/PlexRequests.Core.Tests/PlexRequests.Core.Tests.csproj +++ b/PlexRequests.Core.Tests/PlexRequests.Core.Tests.csproj @@ -1,107 +1,108 @@ - - - - Debug - AnyCPU - {FCFECD5D-47F6-454D-8692-E27A921BE655} - Library - Properties - PlexRequests.Core.Tests - PlexRequests.Core.Tests - v4.5.2 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll - True - - - ..\packages\NUnit.3.0.1\lib\net45\nunit.framework.dll - True - - - ..\packages\AutoFixture.3.40.0\lib\net40\Ploeh.AutoFixture.dll - True - - - - - - - - - - - - - - - - - - - - - - - {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} - PlexRequests.Core - - - {1252336D-42A3-482A-804C-836E60173DFA} - PlexRequests.Helpers - - - - - - - False - - - False - - - False - - - False - - - - - - - + + + + Debug + AnyCPU + {FCFECD5D-47F6-454D-8692-E27A921BE655} + Library + Properties + PlexRequests.Core.Tests + PlexRequests.Core.Tests + v4.5.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll + True + + + ..\packages\NUnit.3.0.1\lib\net45\nunit.framework.dll + True + + + ..\packages\AutoFixture.3.40.0\lib\net40\Ploeh.AutoFixture.dll + True + + + + + + + + + + + + + + + + + + + + + + + + {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} + PlexRequests.Core + + + {1252336D-42A3-482A-804C-836E60173DFA} + PlexRequests.Helpers + + + + + + + False + + + False + + + False + + + False + + + + + + + \ No newline at end of file diff --git a/PlexRequests.Core.Tests/app.config b/PlexRequests.Core.Tests/app.config index 44b249bff..afa1b4c43 100644 --- a/PlexRequests.Core.Tests/app.config +++ b/PlexRequests.Core.Tests/app.config @@ -1,11 +1,11 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/PlexRequests.Services/Notification/NotificationType.cs b/PlexRequests.Core/Models/NotificationType.cs similarity index 94% rename from PlexRequests.Services/Notification/NotificationType.cs rename to PlexRequests.Core/Models/NotificationType.cs index 5e7b32370..a01d153dd 100644 --- a/PlexRequests.Services/Notification/NotificationType.cs +++ b/PlexRequests.Core/Models/NotificationType.cs @@ -1,39 +1,39 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: NotificationType.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 -namespace PlexRequests.Services.Notification -{ - public enum NotificationType - { - NewRequest, - Issue, - RequestAvailable, - RequestApproved, - AdminNote, - Test, - - } -} +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: NotificationType.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 +namespace PlexRequests.Core.Models +{ + public enum NotificationType + { + NewRequest, + Issue, + RequestAvailable, + RequestApproved, + AdminNote, + Test, + + } +} diff --git a/PlexRequests.Core/NotificationMessageResolver.cs b/PlexRequests.Core/NotificationMessageResolver.cs new file mode 100644 index 000000000..6dd4006c8 --- /dev/null +++ b/PlexRequests.Core/NotificationMessageResolver.cs @@ -0,0 +1,104 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: CustomNotificationService.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 System.Linq; + +using PlexRequests.Core.Models; +using PlexRequests.Core.SettingModels; + +namespace PlexRequests.Core +{ + public class NotificationMessageResolver + { + private const char StartChar = (char)123; + private const char EndChar = (char)125; + public string ParseMessage(T notification, NotificationType type) where T : NotificationSettings + { + var notificationToParse = notification.Message.FirstOrDefault(x => x.Key == type).Value; + if (string.IsNullOrEmpty(notificationToParse)) + return string.Empty; + + return Resolve(notificationToParse, notification.CustomParamaters); + } + + private string Resolve(string message, Dictionary paramaters) + { + var fields = FindCurlyFields(message); + + + foreach (var f in fields) + { + string outString; + if (paramaters.TryGetValue(f, out outString)) + { + message = message.Replace($"{{{f}}}", outString); + } + } + + return message; + } + + private IEnumerable FindCurlyFields(string message) + { + var insideCurly = false; + var fields = new List(); + var currentWord = string.Empty; + var chars = message.ToCharArray(); + + foreach (var c in chars) + { + if (char.IsWhiteSpace(c)) + { + currentWord = string.Empty; + continue; + } + + if (c == StartChar) // Start of curly '{' + { + insideCurly = true; + continue; + } + + if (c == EndChar) // End of curly '}' + { + fields.Add(currentWord); // We have finished the curly, add the word into the list + currentWord = string.Empty; + insideCurly = false; + continue; + } + + if (insideCurly) + { + currentWord += c.ToString(); // Add the character onto the word. + } + + } + + return fields; + } + } +} \ No newline at end of file diff --git a/PlexRequests.Core/PlexRequests.Core.csproj b/PlexRequests.Core/PlexRequests.Core.csproj index 495a09ed8..964bb6d01 100644 --- a/PlexRequests.Core/PlexRequests.Core.csproj +++ b/PlexRequests.Core/PlexRequests.Core.csproj @@ -1,134 +1,137 @@ - - - - - Debug - AnyCPU - {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} - Library - Properties - PlexRequests.Core - PlexRequests.Core - v4.5 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\Assemblies\Mono.Data.Sqlite.dll - - - ..\packages\NLog.4.3.4\lib\net45\NLog.dll - True - - - - - - - - - - - ..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll - - - ..\packages\Nancy.1.4.3\lib\net40\Nancy.dll - - - ..\packages\Nancy.Authentication.Forms.1.4.1\lib\net40\Nancy.Authentication.Forms.dll - - - ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll - - - ..\packages\Octokit.0.19.0\lib\net45\Octokit.dll - - - ..\packages\valueinjecter.3.1.1.2\lib\net40\Omu.ValueInjecter.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {95834072-A675-415D-AA8F-877C91623810} - PlexRequests.Api.Interfaces - - - {CB37A5F8-6DFC-4554-99D3-A42B502E4591} - PlexRequests.Api.Models - - - {8CB8D235-2674-442D-9C6A-35FCAEEB160D} - PlexRequests.Api - - - {1252336D-42A3-482A-804C-836E60173DFA} - PlexRequests.Helpers - - - {92433867-2B7B-477B-A566-96C382427525} - PlexRequests.Store - - - - + + + + + Debug + AnyCPU + {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} + Library + Properties + PlexRequests.Core + PlexRequests.Core + v4.5 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\Assemblies\Mono.Data.Sqlite.dll + + + ..\packages\NLog.4.3.4\lib\net45\NLog.dll + True + + + + + + + + + + + ..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll + + + ..\packages\Nancy.1.4.3\lib\net40\Nancy.dll + + + ..\packages\Nancy.Authentication.Forms.1.4.1\lib\net40\Nancy.Authentication.Forms.dll + + + ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll + + + ..\packages\Octokit.0.19.0\lib\net45\Octokit.dll + + + ..\packages\valueinjecter.3.1.1.2\lib\net40\Omu.ValueInjecter.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {95834072-A675-415D-AA8F-877C91623810} + PlexRequests.Api.Interfaces + + + {CB37A5F8-6DFC-4554-99D3-A42B502E4591} + PlexRequests.Api.Models + + + {8CB8D235-2674-442D-9C6A-35FCAEEB160D} + PlexRequests.Api + + + {1252336D-42A3-482A-804C-836E60173DFA} + PlexRequests.Helpers + + + {92433867-2B7B-477B-A566-96C382427525} + PlexRequests.Store + + + + \ No newline at end of file diff --git a/PlexRequests.Core/SettingModels/EmailNotificationSettings.cs b/PlexRequests.Core/SettingModels/EmailNotificationSettings.cs index 2e3db9451..c00342789 100644 --- a/PlexRequests.Core/SettingModels/EmailNotificationSettings.cs +++ b/PlexRequests.Core/SettingModels/EmailNotificationSettings.cs @@ -1,40 +1,40 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: EmailNotificationSettings.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 -namespace PlexRequests.Core.SettingModels -{ - public class EmailNotificationSettings : Settings - { - public string EmailHost { get; set; } - public string EmailPassword { get; set; } - public int EmailPort { get; set; } - public string EmailSender { get; set; } - public string EmailUsername { get; set; } - public bool Enabled { get; set; } - public bool EnableUserEmailNotifications { get; set; } - public string RecipientEmail { get; set; } - } +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: EmailNotificationSettings.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 +namespace PlexRequests.Core.SettingModels +{ + public class EmailNotificationSettings : NotificationSettings + { + public string EmailHost { get; set; } + public string EmailPassword { get; set; } + public int EmailPort { get; set; } + public string EmailSender { get; set; } + public string EmailUsername { get; set; } + public bool Enabled { get; set; } + public bool EnableUserEmailNotifications { get; set; } + public string RecipientEmail { get; set; } + } } \ No newline at end of file diff --git a/PlexRequests.Core/SettingModels/NotificationSettings.cs b/PlexRequests.Core/SettingModels/NotificationSettings.cs new file mode 100644 index 000000000..0ba781958 --- /dev/null +++ b/PlexRequests.Core/SettingModels/NotificationSettings.cs @@ -0,0 +1,38 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: NotificationSettings.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 PlexRequests.Core.Models; + +namespace PlexRequests.Core.SettingModels +{ + public class NotificationSettings : Settings + { + public Dictionary Message { get; set; } + public Dictionary CustomParamaters { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.Core/SettingModels/PlexRequestSettings.cs b/PlexRequests.Core/SettingModels/PlexRequestSettings.cs index 4e986c555..6e65951d1 100644 --- a/PlexRequests.Core/SettingModels/PlexRequestSettings.cs +++ b/PlexRequests.Core/SettingModels/PlexRequestSettings.cs @@ -32,6 +32,13 @@ namespace PlexRequests.Core.SettingModels { public class PlexRequestSettings : Settings { + public PlexRequestSettings() + { + TvWeeklyRequestLimit = 0; + MovieWeeklyRequestLimit = 0; + AlbumWeeklyRequestLimit = 0; + } + public int Port { get; set; } public string BaseUrl { get; set; } public bool SearchForMovies { get; set; } @@ -42,7 +49,9 @@ namespace PlexRequests.Core.SettingModels public bool RequireMusicApproval { get; set; } public bool UsersCanViewOnlyOwnRequests { get; set; } public bool UsersCanViewOnlyOwnIssues { get; set; } - public int WeeklyRequestLimit { get; set; } + public int MovieWeeklyRequestLimit { get; set; } + public int TvWeeklyRequestLimit { get; set; } + public int AlbumWeeklyRequestLimit { get; set; } public string NoApprovalUsers { get; set; } public bool CollectAnalyticData { get; set; } public bool IgnoreNotifyForAutoApprovedRequests { get; set; } diff --git a/PlexRequests.Core/SettingModels/PushBulletNotificationSettings.cs b/PlexRequests.Core/SettingModels/PushBulletNotificationSettings.cs index 1b3ce26f5..a8cc43ef9 100644 --- a/PlexRequests.Core/SettingModels/PushBulletNotificationSettings.cs +++ b/PlexRequests.Core/SettingModels/PushBulletNotificationSettings.cs @@ -1,9 +1,9 @@ -namespace PlexRequests.Core.SettingModels -{ - public class PushbulletNotificationSettings : Settings - { - public bool Enabled { get; set; } - public string AccessToken { get; set; } - public string DeviceIdentifier { get; set; } - } +namespace PlexRequests.Core.SettingModels +{ + public class PushbulletNotificationSettings : NotificationSettings + { + public bool Enabled { get; set; } + public string AccessToken { get; set; } + public string DeviceIdentifier { get; set; } + } } \ No newline at end of file diff --git a/PlexRequests.Core/SettingModels/PushoverNotificationSettings.cs b/PlexRequests.Core/SettingModels/PushoverNotificationSettings.cs index ac6c4c435..87772ffa2 100644 --- a/PlexRequests.Core/SettingModels/PushoverNotificationSettings.cs +++ b/PlexRequests.Core/SettingModels/PushoverNotificationSettings.cs @@ -1,9 +1,9 @@ -namespace PlexRequests.Core.SettingModels -{ - public class PushoverNotificationSettings : Settings - { - public bool Enabled { get; set; } - public string AccessToken { get; set; } - public string UserToken { get; set; } - } +namespace PlexRequests.Core.SettingModels +{ + public class PushoverNotificationSettings : NotificationSettings + { + public bool Enabled { get; set; } + public string AccessToken { get; set; } + public string UserToken { get; set; } + } } \ No newline at end of file diff --git a/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs b/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs index 216f54f88..6d895f6ef 100644 --- a/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs +++ b/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs @@ -36,6 +36,7 @@ namespace PlexRequests.Core.SettingModels CouchPotatoCacher = 10; StoreBackup = 24; StoreCleanup = 24; + UserRequestLimitResetter = 12; } public int PlexAvailabilityChecker { get; set; } public int SickRageCacher { get; set; } @@ -43,5 +44,6 @@ namespace PlexRequests.Core.SettingModels public int CouchPotatoCacher { get; set; } public int StoreBackup { get; set; } public int StoreCleanup { get; set; } + public int UserRequestLimitResetter { get; set; } } } \ No newline at end of file diff --git a/PlexRequests.Core/SettingModels/SlackNotificationSettings.cs b/PlexRequests.Core/SettingModels/SlackNotificationSettings.cs index 2b412ed3e..93562e8a1 100644 --- a/PlexRequests.Core/SettingModels/SlackNotificationSettings.cs +++ b/PlexRequests.Core/SettingModels/SlackNotificationSettings.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace PlexRequests.Core.SettingModels { - public class SlackNotificationSettings : Settings + public class SlackNotificationSettings : NotificationSettings { public bool Enabled { get; set; } public string WebhookUrl { get; set; } diff --git a/PlexRequests.Core/Setup.cs b/PlexRequests.Core/Setup.cs index e66a15cec..a3a407aa1 100644 --- a/PlexRequests.Core/Setup.cs +++ b/PlexRequests.Core/Setup.cs @@ -100,7 +100,6 @@ namespace PlexRequests.Core RequireMovieApproval = true, SearchForMovies = true, SearchForTvShows = true, - WeeklyRequestLimit = 0, BaseUrl = baseUrl ?? string.Empty, CollectAnalyticData = true, }; diff --git a/PlexRequests.Core/app.config b/PlexRequests.Core/app.config index 44b249bff..afa1b4c43 100644 --- a/PlexRequests.Core/app.config +++ b/PlexRequests.Core/app.config @@ -1,11 +1,11 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/PlexRequests.Core/packages.config b/PlexRequests.Core/packages.config index 41cff5672..9f54b7236 100644 --- a/PlexRequests.Core/packages.config +++ b/PlexRequests.Core/packages.config @@ -1,9 +1,9 @@ - - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/PlexRequests.Helpers.Tests/CookieHelperTests.cs b/PlexRequests.Helpers.Tests/CookieHelperTests.cs index c9a8205fb..6acf94e85 100644 --- a/PlexRequests.Helpers.Tests/CookieHelperTests.cs +++ b/PlexRequests.Helpers.Tests/CookieHelperTests.cs @@ -43,9 +43,9 @@ namespace PlexRequests.Helpers.Tests { get { - yield return new TestCaseData(new Dictionary()).Returns(string.Empty); - yield return new TestCaseData(new Dictionary { { "_ga", "GA1.1.306549087.1464005217" } }).Returns("306549087.1464005217"); - yield return new TestCaseData(new Dictionary { {"_ga", "GA1.1.306549087" } }).Returns(string.Empty); + yield return new TestCaseData(new Dictionary()).Returns(string.Empty).SetName("Empty"); + yield return new TestCaseData(new Dictionary { { "_ga", "GA1.1.306549087.1464005217" } }).Returns("306549087.1464005217").SetName("Returns correctly"); + yield return new TestCaseData(new Dictionary { {"_ga", "GA1.1.306549087" } }).Returns(string.Empty).SetName("Invalid Cookie"); } } } diff --git a/PlexRequests.Helpers/Analytics/Action.cs b/PlexRequests.Helpers/Analytics/Action.cs index 4a42a45be..d91356747 100644 --- a/PlexRequests.Helpers/Analytics/Action.cs +++ b/PlexRequests.Helpers/Analytics/Action.cs @@ -35,6 +35,11 @@ namespace PlexRequests.Helpers.Analytics Save, Update, Start, - View + View, + Movie, + TvShow, + Album, + Request, + Language } } \ No newline at end of file diff --git a/PlexRequests.Helpers/Analytics/Category.cs b/PlexRequests.Helpers/Analytics/Category.cs index 7bedb16a5..252c6fdc8 100644 --- a/PlexRequests.Helpers/Analytics/Category.cs +++ b/PlexRequests.Helpers/Analytics/Category.cs @@ -37,5 +37,6 @@ namespace PlexRequests.Helpers.Analytics Issues, UserLogin, Services, + Navbar } } \ No newline at end of file diff --git a/PlexRequests.Resources/PlexRequests.Resources.csproj b/PlexRequests.Resources/PlexRequests.Resources.csproj new file mode 100644 index 000000000..2179f207a --- /dev/null +++ b/PlexRequests.Resources/PlexRequests.Resources.csproj @@ -0,0 +1,53 @@ + + + + + Debug + AnyCPU + {9C266462-BE82-461A-87A2-9EDCFB95D732} + Library + Properties + PlexRequests.Resources + PlexRequests.Resources + v4.5.2 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PlexRequests.Resources/Properties/AssemblyInfo.cs b/PlexRequests.Resources/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..1f1c2d1b7 --- /dev/null +++ b/PlexRequests.Resources/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PlexRequests.Resources")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PlexRequests.Resources")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9c266462-be82-461a-87a2-9edcfb95d732")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/PlexRequests.Services.Tests/NotificationServiceTests.cs b/PlexRequests.Services.Tests/NotificationServiceTests.cs index 940bcedda..a7df1c791 100644 --- a/PlexRequests.Services.Tests/NotificationServiceTests.cs +++ b/PlexRequests.Services.Tests/NotificationServiceTests.cs @@ -1,137 +1,137 @@ -#region Copyright -/************************************************************************ - Copyright (c) 2016 Jamie Rees - File: NotificationServiceTests.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.Threading.Tasks; - -using Moq; - -using NUnit.Framework; - -using PlexRequests.Services.Interfaces; -using PlexRequests.Services.Notification; - -namespace PlexRequests.Services.Tests -{ - [TestFixture] - public class NotificationServiceTests - { - public NotificationService NotificationService { get; set; } - - [SetUp] - public void Setup() - { - NotificationService = new NotificationService(); - } - - [Test] - public void SubscribeNewNotifier() - { - var notificationMock = new Mock(); - notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1"); - NotificationService.Subscribe(notificationMock.Object); - - Assert.That(NotificationService.Observers["Notification1"], Is.Not.Null); - Assert.That(NotificationService.Observers.Count, Is.EqualTo(1)); - } - - [Test] - public void SubscribeExistingNotifier() - { - var notificationMock1 = new Mock(); - var notificationMock2 = new Mock(); - notificationMock1.SetupGet(x => x.NotificationName).Returns("Notification1"); - notificationMock2.SetupGet(x => x.NotificationName).Returns("Notification1"); - NotificationService.Subscribe(notificationMock1.Object); - - Assert.That(NotificationService.Observers["Notification1"], Is.Not.Null); - Assert.That(NotificationService.Observers.Count, Is.EqualTo(1)); - - NotificationService.Subscribe(notificationMock2.Object); - - Assert.That(NotificationService.Observers["Notification1"], Is.Not.Null); - Assert.That(NotificationService.Observers.Count, Is.EqualTo(1)); - } - - [Test] - public void UnSubscribeMissingNotifier() - { - var notificationMock = new Mock(); - notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1"); - NotificationService.UnSubscribe(notificationMock.Object); - - Assert.That(NotificationService.Observers.Count, Is.EqualTo(0)); - } - - [Test] - public void UnSubscribeNotifier() - { - var notificationMock = new Mock(); - notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1"); - NotificationService.Subscribe(notificationMock.Object); - Assert.That(NotificationService.Observers.Count, Is.EqualTo(1)); - - NotificationService.UnSubscribe(notificationMock.Object); - Assert.That(NotificationService.Observers.Count, Is.EqualTo(0)); - } - - [Test] - public void PublishWithNoObservers() - { - Assert.DoesNotThrowAsync( - async() => - { await NotificationService.Publish(new NotificationModel()); }); - } - - [Test] - public async Task PublishAllNotifiers() - { - var notificationMock1 = new Mock(); - var notificationMock2 = new Mock(); - notificationMock1.SetupGet(x => x.NotificationName).Returns("Notification1"); - notificationMock2.SetupGet(x => x.NotificationName).Returns("Notification2"); - NotificationService.Subscribe(notificationMock1.Object); - NotificationService.Subscribe(notificationMock2.Object); - - Assert.That(NotificationService.Observers.Count, Is.EqualTo(2)); - var model = new NotificationModel { Title = "abc", Body = "test" }; - await NotificationService.Publish(model); - - notificationMock1.Verify(x => x.NotifyAsync(model), Times.Once); - notificationMock2.Verify(x => x.NotifyAsync(model), Times.Once); - } - - [Test] - public async Task PublishWithException() - { - var notificationMock = new Mock(); - notificationMock.Setup(x => x.NotifyAsync(It.IsAny())).Throws(); - notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1"); - NotificationService.Subscribe(notificationMock.Object); - await NotificationService.Publish(new NotificationModel()); - } - } +#region Copyright +/************************************************************************ + Copyright (c) 2016 Jamie Rees + File: NotificationServiceTests.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.Threading.Tasks; + +using Moq; + +using NUnit.Framework; + +using PlexRequests.Services.Interfaces; +using PlexRequests.Services.Notification; + +namespace PlexRequests.Services.Tests +{ + [TestFixture] + public class NotificationServiceTests + { + public NotificationService NotificationService { get; set; } + + [SetUp] + public void Setup() + { + NotificationService = new NotificationService(); + } + + [Test] + public void SubscribeNewNotifier() + { + var notificationMock = new Mock(); + notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1"); + NotificationService.Subscribe(notificationMock.Object); + + Assert.That(NotificationService.Observers["Notification1"], Is.Not.Null); + Assert.That(NotificationService.Observers.Count, Is.EqualTo(1)); + } + + [Test] + public void SubscribeExistingNotifier() + { + var notificationMock1 = new Mock(); + var notificationMock2 = new Mock(); + notificationMock1.SetupGet(x => x.NotificationName).Returns("Notification1"); + notificationMock2.SetupGet(x => x.NotificationName).Returns("Notification1"); + NotificationService.Subscribe(notificationMock1.Object); + + Assert.That(NotificationService.Observers["Notification1"], Is.Not.Null); + Assert.That(NotificationService.Observers.Count, Is.EqualTo(1)); + + NotificationService.Subscribe(notificationMock2.Object); + + Assert.That(NotificationService.Observers["Notification1"], Is.Not.Null); + Assert.That(NotificationService.Observers.Count, Is.EqualTo(1)); + } + + [Test] + public void UnSubscribeMissingNotifier() + { + var notificationMock = new Mock(); + notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1"); + NotificationService.UnSubscribe(notificationMock.Object); + + Assert.That(NotificationService.Observers.Count, Is.EqualTo(0)); + } + + [Test] + public void UnSubscribeNotifier() + { + var notificationMock = new Mock(); + notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1"); + NotificationService.Subscribe(notificationMock.Object); + Assert.That(NotificationService.Observers.Count, Is.EqualTo(1)); + + NotificationService.UnSubscribe(notificationMock.Object); + Assert.That(NotificationService.Observers.Count, Is.EqualTo(0)); + } + + [Test] + public void PublishWithNoObservers() + { + Assert.DoesNotThrowAsync( + async() => + { await NotificationService.Publish(new NotificationModel()); }); + } + + [Test] + public async Task PublishAllNotifiers() + { + var notificationMock1 = new Mock(); + var notificationMock2 = new Mock(); + notificationMock1.SetupGet(x => x.NotificationName).Returns("Notification1"); + notificationMock2.SetupGet(x => x.NotificationName).Returns("Notification2"); + NotificationService.Subscribe(notificationMock1.Object); + NotificationService.Subscribe(notificationMock2.Object); + + Assert.That(NotificationService.Observers.Count, Is.EqualTo(2)); + var model = new NotificationModel { Title = "abc", Body = "test" }; + await NotificationService.Publish(model); + + notificationMock1.Verify(x => x.NotifyAsync(model), Times.Once); + notificationMock2.Verify(x => x.NotifyAsync(model), Times.Once); + } + + [Test] + public async Task PublishWithException() + { + var notificationMock = new Mock(); + notificationMock.Setup(x => x.NotifyAsync(It.IsAny())).Throws(); + notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1"); + NotificationService.Subscribe(notificationMock.Object); + await NotificationService.Publish(new NotificationModel()); + } + } } \ No newline at end of file diff --git a/PlexRequests.Services.Tests/PlexRequests.Services.Tests.csproj b/PlexRequests.Services.Tests/PlexRequests.Services.Tests.csproj index 298d5e64d..dc79ac405 100644 --- a/PlexRequests.Services.Tests/PlexRequests.Services.Tests.csproj +++ b/PlexRequests.Services.Tests/PlexRequests.Services.Tests.csproj @@ -1,136 +1,141 @@ - - - - Debug - AnyCPU - {EAADB4AC-064F-4D3A-AFF9-64A33131A9A7} - Library - Properties - PlexRequests.Services.Tests - PlexRequests.Services.Tests - v4.5.2 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll - True - - - ..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll - True - - - ..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll - True - - - ..\packages\NUnit.3.2.0\lib\net45\nunit.framework.dll - True - - - ..\packages\Quartz.2.3.3\lib\net40\Quartz.dll - True - - - - - - - - - - - - - - - - - - - Designer - - - Designer - - - - - - {95834072-A675-415D-AA8F-877C91623810} - PlexRequests.Api.Interfaces - - - {CB37A5F8-6DFC-4554-99D3-A42B502E4591} - PlexRequests.Api.Models - - - {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} - PlexRequests.Core - - - {1252336D-42A3-482A-804C-836E60173DFA} - PlexRequests.Helpers - - - {566EFA49-68F8-4716-9693-A6B3F2624DEA} - PlexRequests.Services - - - {92433867-2B7B-477B-A566-96C382427525} - PlexRequests.Store - - - - - - - False - - - False - - - False - - - False - - - - - - - + + + + Debug + AnyCPU + {EAADB4AC-064F-4D3A-AFF9-64A33131A9A7} + Library + Properties + PlexRequests.Services.Tests + PlexRequests.Services.Tests + v4.5.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll + True + + + ..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll + True + + + ..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll + True + + + ..\packages\NUnit.3.2.0\lib\net45\nunit.framework.dll + True + + + ..\packages\AutoFixture.3.40.0\lib\net40\Ploeh.AutoFixture.dll + True + + + ..\packages\Quartz.2.3.3\lib\net40\Quartz.dll + True + + + + + + + + + + + + + + + + + + + + Designer + + + Designer + + + + + + {95834072-A675-415D-AA8F-877C91623810} + PlexRequests.Api.Interfaces + + + {CB37A5F8-6DFC-4554-99D3-A42B502E4591} + PlexRequests.Api.Models + + + {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} + PlexRequests.Core + + + {1252336D-42A3-482A-804C-836E60173DFA} + PlexRequests.Helpers + + + {566EFA49-68F8-4716-9693-A6B3F2624DEA} + PlexRequests.Services + + + {92433867-2B7B-477B-A566-96C382427525} + PlexRequests.Store + + + + + + + False + + + False + + + False + + + False + + + + + + + \ No newline at end of file diff --git a/PlexRequests.Services.Tests/UserRequestLimitResetterTests.cs b/PlexRequests.Services.Tests/UserRequestLimitResetterTests.cs new file mode 100644 index 000000000..be437c7fe --- /dev/null +++ b/PlexRequests.Services.Tests/UserRequestLimitResetterTests.cs @@ -0,0 +1,145 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UserRequestLimitResetterTests.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; + +using Moq; + +using NUnit.Framework; + +using PlexRequests.Core; +using PlexRequests.Core.SettingModels; +using PlexRequests.Services.Interfaces; +using PlexRequests.Services.Jobs; +using PlexRequests.Store; +using PlexRequests.Store.Models; +using PlexRequests.Store.Repository; + +using Ploeh.AutoFixture; + +using Quartz; + +namespace PlexRequests.Services.Tests +{ + [TestFixture] + public class UserRequestLimitResetterTests + { + [SetUp] + public void Setup() + { + F = new Fixture(); + JobMock = new Mock(); + RepoMock = new Mock>(); + SettingsMock = new Mock>(); + ContextMock = new Mock(); + + SettingsMock.Setup(x => x.GetSettings()).Returns(new PlexRequestSettings()); + + Resetter = new UserRequestLimitResetter(JobMock.Object, RepoMock.Object, SettingsMock.Object); + } + + [TearDown] + public void Teardown() + { + SettingsMock.Verify(x => x.GetSettings(), Times.Once); + JobMock.Verify(x => x.Record(It.IsAny()), Times.Once()); + } + + public UserRequestLimitResetter Resetter { get; set; } + private Mock JobMock { get; set; } + private Mock> RepoMock { get; set; } + private Mock> SettingsMock { get; set; } + private Mock ContextMock { get; set; } + private Fixture F { get; set; } + + [TestCaseSource(nameof(ResetData))] + public void Reset(int movie, int tv, int album, RequestType type) + { + SetupSettings(movie, tv, album); + RepoMock.Setup(x => x.GetAll()) + .Returns(F.Build().With(x => x.FirstRequestDate, DateTime.Now.AddDays(-8)).With(x => x.RequestType, type).CreateMany()); + + Resetter = new UserRequestLimitResetter(JobMock.Object, RepoMock.Object, SettingsMock.Object); + + Resetter.Execute(ContextMock.Object); + + RepoMock.Verify(x => x.Delete(It.IsAny()), Times.Exactly(3)); + } + + [TestCaseSource(nameof(DoNotResetData))] + public void DoNotReset(int days, RequestType type) + { + SetupSettings(1, 1, 1); + RepoMock.Setup(x => x.GetAll()) + .Returns(F.Build().With(x => x.FirstRequestDate, DateTime.Now.AddDays(days)).With(x => x.RequestType, type).CreateMany()); + + Resetter = new UserRequestLimitResetter(JobMock.Object, RepoMock.Object, SettingsMock.Object); + + Resetter.Execute(ContextMock.Object); + + RepoMock.Verify(x => x.Delete(It.IsAny()), Times.Never); + } + + static readonly IEnumerable ResetData = new List + { + new TestCaseData(1, 0, 0, RequestType.Movie).SetName("Reset Movies"), + new TestCaseData(0, 1, 0, RequestType.TvShow).SetName("Reset TV Shows"), + new TestCaseData(0, 0, 1, RequestType.Album).SetName("Reset Albums"), + new TestCaseData(1, 1, 1, RequestType.Album).SetName("Reset Albums with all enabled") + }; + + private static readonly IEnumerable DoNotResetData = new List + { + new TestCaseData(1, RequestType.Movie).SetName("1 Day(s)"), + new TestCaseData(100, RequestType.Movie).SetName("100 Day(s)"), + new TestCaseData(-6, RequestType.TvShow).SetName("-6 Day(s)"), + new TestCaseData(-1, RequestType.TvShow).SetName("-1 Day(s)"), + new TestCaseData(-2, RequestType.Album).SetName("-2 Day(s)"), + new TestCaseData(-3, RequestType.TvShow).SetName("-3 Day(s)"), + new TestCaseData(-4, RequestType.Movie).SetName("-4 Day(s)"), + new TestCaseData(-5, RequestType.TvShow).SetName("-5 Day(s)"), + new TestCaseData(0, RequestType.TvShow).SetName("0 Day(s)") + }; + + private void SetupSettings(int movie, int tv, int album) + { + SettingsMock.Setup(x => x.GetSettings()) + .Returns(new PlexRequestSettings { MovieWeeklyRequestLimit = movie, TvWeeklyRequestLimit = tv, AlbumWeeklyRequestLimit = album }); + } + + [Test] + public void ResetTurnedOff() + { + SetupSettings(0, 0, 0); + Resetter = new UserRequestLimitResetter(JobMock.Object, RepoMock.Object, SettingsMock.Object); + + Resetter.Execute(ContextMock.Object); + + RepoMock.Verify(x => x.Delete(It.IsAny()), Times.Never); + } + } +} \ No newline at end of file diff --git a/PlexRequests.Services.Tests/app.config b/PlexRequests.Services.Tests/app.config index 44b249bff..afa1b4c43 100644 --- a/PlexRequests.Services.Tests/app.config +++ b/PlexRequests.Services.Tests/app.config @@ -1,11 +1,11 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/PlexRequests.Services.Tests/packages.config b/PlexRequests.Services.Tests/packages.config index 50f0bcc7b..73921e638 100644 --- a/PlexRequests.Services.Tests/packages.config +++ b/PlexRequests.Services.Tests/packages.config @@ -1,8 +1,9 @@ - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/PlexRequests.Services/Jobs/CouchPotatoCacher.cs b/PlexRequests.Services/Jobs/CouchPotatoCacher.cs index 3fb831b12..be36d004f 100644 --- a/PlexRequests.Services/Jobs/CouchPotatoCacher.cs +++ b/PlexRequests.Services/Jobs/CouchPotatoCacher.cs @@ -24,6 +24,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // ************************************************************************/ #endregion + +using System; using System.Linq; using NLog; @@ -86,8 +88,18 @@ namespace PlexRequests.Services.Jobs // we do not want to set here... public int[] QueuedIds() { - var movies = Cache.Get(CacheKeys.CouchPotatoQueued); - return movies?.movies?.Select(x => x.info.tmdb_id).ToArray() ?? new int[] { }; + try + { + var movies = Cache.Get(CacheKeys.CouchPotatoQueued); + + var items = movies?.movies?.Select(x => x.info?.tmdb_id).Cast().ToArray(); + return items ?? new int[] { }; + } + catch (Exception e) + { + Log.Error(e); + return new int[] {}; + } } public void Execute(IJobExecutionContext context) diff --git a/PlexRequests.Services/Jobs/JobNames.cs b/PlexRequests.Services/Jobs/JobNames.cs index ff6791d92..8832d2c2b 100644 --- a/PlexRequests.Services/Jobs/JobNames.cs +++ b/PlexRequests.Services/Jobs/JobNames.cs @@ -34,5 +34,6 @@ namespace PlexRequests.Services.Jobs public const string SrCacher = "SickRage Cacher"; public const string PlexChecker = "Plex Availability Cacher"; public const string StoreCleanup = "Database Cleanup"; + public const string RequestLimitReset = "Request Limit Reset"; } } \ No newline at end of file diff --git a/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs b/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs index 68b8da01b..dfadadd80 100644 --- a/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs +++ b/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs @@ -33,6 +33,7 @@ using NLog; using PlexRequests.Api.Interfaces; using PlexRequests.Api.Models.Plex; using PlexRequests.Core; +using PlexRequests.Core.Models; using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; using PlexRequests.Helpers.Analytics; diff --git a/PlexRequests.Services/Jobs/UserRequestLimitResetter.cs b/PlexRequests.Services/Jobs/UserRequestLimitResetter.cs new file mode 100644 index 000000000..96d613b72 --- /dev/null +++ b/PlexRequests.Services/Jobs/UserRequestLimitResetter.cs @@ -0,0 +1,121 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UserRequestLimitResetter.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; +using System.Linq; + +using NLog; + +using PlexRequests.Core; +using PlexRequests.Core.SettingModels; +using PlexRequests.Services.Interfaces; +using PlexRequests.Store; +using PlexRequests.Store.Models; +using PlexRequests.Store.Repository; + +using Quartz; + +namespace PlexRequests.Services.Jobs +{ + public class UserRequestLimitResetter : IJob + { + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + + public UserRequestLimitResetter(IJobRecord record, IRepository repo, ISettingsService settings) + { + Record = record; + Repo = repo; + Settings = settings; + } + + private IJobRecord Record { get; } + private IRepository Repo { get; } + private ISettingsService Settings { get; } + + public void AlbumLimit(PlexRequestSettings s, IEnumerable allUsers) + { + if (s.AlbumWeeklyRequestLimit == 0) + { + return; // The limit has not been set + } + CheckAndDelete(allUsers, RequestType.Album); + } + + public void MovieLimit(PlexRequestSettings s, IEnumerable allUsers) + { + if (s.MovieWeeklyRequestLimit == 0) + { + return; // The limit has not been set + } + CheckAndDelete(allUsers, RequestType.Movie); + } + + public void TvLimit(PlexRequestSettings s, IEnumerable allUsers) + { + if (s.TvWeeklyRequestLimit == 0) + { + return; // The limit has not been set + } + CheckAndDelete(allUsers, RequestType.TvShow); + } + + private void CheckAndDelete(IEnumerable allUsers, RequestType type) + { + var users = allUsers.Where(x => x.RequestType == type); + foreach (var u in users) + { + var daysDiff = (u.FirstRequestDate - DateTime.UtcNow.AddDays(-7)).TotalDays; + if (daysDiff <= 0) + { + Repo.Delete(u); + } + } + } + + public void Execute(IJobExecutionContext context) + { + try + { + var settings = Settings.GetSettings(); + var users = Repo.GetAll(); + var requestLimits = users as RequestLimit[] ?? users.ToArray(); + + MovieLimit(settings, requestLimits); + TvLimit(settings, requestLimits); + AlbumLimit(settings, requestLimits); + } + catch (Exception e) + { + Log.Error(e); + } + finally + { + Record.Record(JobNames.RequestLimitReset); + } + } + } +} \ No newline at end of file diff --git a/PlexRequests.Services/Notification/EmailMessageNotification.cs b/PlexRequests.Services/Notification/EmailMessageNotification.cs index 466ff7a24..e67e42e5f 100644 --- a/PlexRequests.Services/Notification/EmailMessageNotification.cs +++ b/PlexRequests.Services/Notification/EmailMessageNotification.cs @@ -33,6 +33,7 @@ using MimeKit; using NLog; using PlexRequests.Core; +using PlexRequests.Core.Models; using PlexRequests.Core.SettingModels; using PlexRequests.Services.Interfaces; using SmtpClient = MailKit.Net.Smtp.SmtpClient; @@ -120,8 +121,8 @@ namespace PlexRequests.Services.Notification { var message = new MimeMessage { - Body = new TextPart("plain") { Text = $"Hello! The user '{model.User}' has requested the {RequestTypeDisplay.Get(model.RequestType)?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime.ToString("f")}" }, - Subject = $"Plex Requests: New {RequestTypeDisplay.Get(model.RequestType)?.ToLower()} request for {model.Title}!" + Body = new TextPart("plain") { Text = $"Hello! The user '{model.User}' has requested the {model.RequestType.GetString()?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime.ToString("f")}" }, + Subject = $"Plex Requests: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!" }; message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender)); message.To.Add(new MailboxAddress(settings.RecipientEmail, settings.RecipientEmail)); diff --git a/PlexRequests.Services/Notification/NotificationModel.cs b/PlexRequests.Services/Notification/NotificationModel.cs index e74f90dd4..11ebcbae3 100644 --- a/PlexRequests.Services/Notification/NotificationModel.cs +++ b/PlexRequests.Services/Notification/NotificationModel.cs @@ -27,6 +27,8 @@ using PlexRequests.Store; using System; +using PlexRequests.Core.Models; + namespace PlexRequests.Services.Notification { public class NotificationModel diff --git a/PlexRequests.Services/Notification/PushbulletNotification.cs b/PlexRequests.Services/Notification/PushbulletNotification.cs index 8eda8d0b9..e2ae3c4c4 100644 --- a/PlexRequests.Services/Notification/PushbulletNotification.cs +++ b/PlexRequests.Services/Notification/PushbulletNotification.cs @@ -31,6 +31,7 @@ using NLog; using PlexRequests.Api.Interfaces; using PlexRequests.Core; +using PlexRequests.Core.Models; using PlexRequests.Core.SettingModels; using PlexRequests.Services.Interfaces; using PlexRequests.Store; @@ -105,8 +106,8 @@ namespace PlexRequests.Services.Notification private async Task PushNewRequestAsync(NotificationModel model, PushbulletNotificationSettings settings) { - var message = $"The {RequestTypeDisplay.Get(model.RequestType)?.ToLower()} '{model.Title}' has been requested by user: {model.User}"; - var pushTitle = $"Plex Requests: The {RequestTypeDisplay.Get(model.RequestType)?.ToLower()} {model.Title} has been requested!"; + var message = $"The {model.RequestType.GetString()?.ToLower()} '{model.Title}' has been requested by user: {model.User}"; + var pushTitle = $"Plex Requests: The {model.RequestType.GetString()?.ToLower()} {model.Title} has been requested!"; await Push(settings, message, pushTitle); } diff --git a/PlexRequests.Services/Notification/PushoverNotification.cs b/PlexRequests.Services/Notification/PushoverNotification.cs index 9831e6562..2adafb11e 100644 --- a/PlexRequests.Services/Notification/PushoverNotification.cs +++ b/PlexRequests.Services/Notification/PushoverNotification.cs @@ -31,6 +31,7 @@ using NLog; using PlexRequests.Api.Interfaces; using PlexRequests.Core; +using PlexRequests.Core.Models; using PlexRequests.Core.SettingModels; using PlexRequests.Services.Interfaces; using PlexRequests.Store; @@ -105,7 +106,7 @@ namespace PlexRequests.Services.Notification private async Task PushNewRequestAsync(NotificationModel model, PushoverNotificationSettings settings) { - var message = $"Plex Requests: The {RequestTypeDisplay.Get(model.RequestType)?.ToLower()} '{model.Title}' has been requested by user: {model.User}"; + var message = $"Plex Requests: The {model.RequestType.GetString()?.ToLower()} '{model.Title}' has been requested by user: {model.User}"; await Push(settings, message); } diff --git a/PlexRequests.Services/Notification/SlackNotification.cs b/PlexRequests.Services/Notification/SlackNotification.cs index 5da0c156a..f8c90d3ca 100644 --- a/PlexRequests.Services/Notification/SlackNotification.cs +++ b/PlexRequests.Services/Notification/SlackNotification.cs @@ -32,6 +32,7 @@ using NLog; using PlexRequests.Api.Interfaces; using PlexRequests.Api.Models.Notifications; using PlexRequests.Core; +using PlexRequests.Core.Models; using PlexRequests.Core.SettingModels; using PlexRequests.Services.Interfaces; diff --git a/PlexRequests.Services/PlexRequests.Services.csproj b/PlexRequests.Services/PlexRequests.Services.csproj index fc61aab55..4f50fcac0 100644 --- a/PlexRequests.Services/PlexRequests.Services.csproj +++ b/PlexRequests.Services/PlexRequests.Services.csproj @@ -1,139 +1,139 @@ - - - - - Debug - AnyCPU - {566EFA49-68F8-4716-9693-A6B3F2624DEA} - Library - Properties - PlexRequests.Services - PlexRequests.Services - v4.5 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - ..\packages\NLog.4.3.4\lib\net45\NLog.dll - True - - - - - - - - - - - - - - ..\packages\MimeKit.1.2.22\lib\net45\BouncyCastle.dll - - - ..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll - - - ..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll - - - ..\packages\MailKit.1.2.21\lib\net451\MailKit.dll - - - ..\packages\MimeKit.1.2.22\lib\net45\MimeKit.dll - - - ..\Assemblies\Mono.Data.Sqlite.dll - - - ..\packages\Quartz.2.3.3\lib\net40\Quartz.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {95834072-A675-415D-AA8F-877C91623810} - PlexRequests.Api.Interfaces - - - {CB37A5F8-6DFC-4554-99D3-A42B502E4591} - PlexRequests.Api.Models - - - {8CB8D235-2674-442D-9C6A-35FCAEEB160D} - PlexRequests.Api - - - {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} - PlexRequests.Core - - - {1252336D-42A3-482A-804C-836E60173DFA} - PlexRequests.Helpers - - - {92433867-2B7B-477B-A566-96C382427525} - PlexRequests.Store - - - - + + + + + Debug + AnyCPU + {566EFA49-68F8-4716-9693-A6B3F2624DEA} + Library + Properties + PlexRequests.Services + PlexRequests.Services + v4.5 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + ..\packages\NLog.4.3.4\lib\net45\NLog.dll + True + + + + + + + + + + + + + + ..\packages\MimeKit.1.2.22\lib\net45\BouncyCastle.dll + + + ..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll + + + ..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll + + + ..\packages\MailKit.1.2.21\lib\net451\MailKit.dll + + + ..\packages\MimeKit.1.2.22\lib\net45\MimeKit.dll + + + ..\Assemblies\Mono.Data.Sqlite.dll + + + ..\packages\Quartz.2.3.3\lib\net40\Quartz.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {95834072-A675-415D-AA8F-877C91623810} + PlexRequests.Api.Interfaces + + + {CB37A5F8-6DFC-4554-99D3-A42B502E4591} + PlexRequests.Api.Models + + + {8CB8D235-2674-442D-9C6A-35FCAEEB160D} + PlexRequests.Api + + + {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} + PlexRequests.Core + + + {1252336D-42A3-482A-804C-836E60173DFA} + PlexRequests.Helpers + + + {92433867-2B7B-477B-A566-96C382427525} + PlexRequests.Store + + + + \ No newline at end of file diff --git a/PlexRequests.Services/app.config b/PlexRequests.Services/app.config index 44b249bff..afa1b4c43 100644 --- a/PlexRequests.Services/app.config +++ b/PlexRequests.Services/app.config @@ -1,11 +1,11 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/PlexRequests.Store/Models/PlexUsers.cs b/PlexRequests.Store/Models/PlexUsers.cs new file mode 100644 index 000000000..884fe1d4d --- /dev/null +++ b/PlexRequests.Store/Models/PlexUsers.cs @@ -0,0 +1,34 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: PlexUsers.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 +namespace PlexRequests.Store.Models +{ + public class PlexUsers : Entity + { + public int PlexUserId { get; set; } + public string UserAlias { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.Store/Models/RequestLimit.cs b/PlexRequests.Store/Models/RequestLimit.cs new file mode 100644 index 000000000..1c86986c6 --- /dev/null +++ b/PlexRequests.Store/Models/RequestLimit.cs @@ -0,0 +1,41 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UsersToNotify.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 Dapper.Contrib.Extensions; + +namespace PlexRequests.Store.Models +{ + [Table("RequestLimit")] + public class RequestLimit : Entity + { + public string Username { get; set; } + public DateTime FirstRequestDate { get; set; } + public int RequestCount { get; set; } + public RequestType RequestType { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.Store/PlexRequests.Store.csproj b/PlexRequests.Store/PlexRequests.Store.csproj index 335ea07db..dba4d4ef1 100644 --- a/PlexRequests.Store/PlexRequests.Store.csproj +++ b/PlexRequests.Store/PlexRequests.Store.csproj @@ -1,125 +1,127 @@ - - - - - Debug - AnyCPU - {92433867-2B7B-477B-A566-96C382427525} - Library - Properties - PlexRequests.Store - PlexRequests.Store - v4.5 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Dapper.1.50.0-beta8\lib\net45\Dapper.dll - True - - - ..\packages\Dapper.Contrib.1.50.0-beta8\lib\net45\Dapper.Contrib.dll - True - - - ..\Assemblies\Mono.Data.Sqlite.dll - - - ..\packages\NLog.4.3.4\lib\net45\NLog.dll - True - - - - - - - - - - - - - ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Sql.resx - - - - - - - - Always - - - Always - - - - - ResXFileCodeGenerator - Sql.Designer.cs - - - - - - - - {1252336D-42A3-482A-804C-836E60173DFA} - PlexRequests.Helpers - - - - + + + + + Debug + AnyCPU + {92433867-2B7B-477B-A566-96C382427525} + Library + Properties + PlexRequests.Store + PlexRequests.Store + v4.5 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Dapper.1.50.0-beta8\lib\net45\Dapper.dll + True + + + ..\packages\Dapper.Contrib.1.50.0-beta8\lib\net45\Dapper.Contrib.dll + True + + + ..\Assemblies\Mono.Data.Sqlite.dll + + + ..\packages\NLog.4.3.4\lib\net45\NLog.dll + True + + + + + + + + + + + + + ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Sql.resx + + + + + + + + Always + + + Always + + + + + ResXFileCodeGenerator + Sql.Designer.cs + + + + + + + + {1252336D-42A3-482A-804C-836E60173DFA} + PlexRequests.Helpers + + + + \ No newline at end of file diff --git a/PlexRequests.Store/RequestedModel.cs b/PlexRequests.Store/RequestedModel.cs index 254b4f7be..b203db7d3 100644 --- a/PlexRequests.Store/RequestedModel.cs +++ b/PlexRequests.Store/RequestedModel.cs @@ -80,7 +80,7 @@ namespace PlexRequests.Store public static class RequestTypeDisplay { - public static string Get(RequestType type) + public static string GetString(this RequestType type) { switch (type) { diff --git a/PlexRequests.Store/SqlTables.sql b/PlexRequests.Store/SqlTables.sql index 6e89f589c..5a5ba22a9 100644 --- a/PlexRequests.Store/SqlTables.sql +++ b/PlexRequests.Store/SqlTables.sql @@ -1,84 +1,102 @@ ---Any DB changes need to be made in this file. - -CREATE TABLE IF NOT EXISTS Users -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - UserGuid varchar(50) NOT NULL , - UserName varchar(50) NOT NULL, - Salt BLOB NOT NULL, - Hash BLOB NOT NULL, - Claims BLOB NOT NULL, - UserProperties BLOB -); - - -CREATE TABLE IF NOT EXISTS GlobalSettings -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - SettingsName varchar(50) NOT NULL, - Content varchar(100) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS GlobalSettings_Id ON GlobalSettings (Id); - -CREATE TABLE IF NOT EXISTS RequestBlobs -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - ProviderId INTEGER NOT NULL, - Type INTEGER NOT NULL, - Content BLOB NOT NULL, - MusicId TEXT -); -CREATE UNIQUE INDEX IF NOT EXISTS RequestBlobs_Id ON RequestBlobs (Id); - -CREATE TABLE IF NOT EXISTS Logs -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Date varchar(100) NOT NULL, - Level varchar(100) NOT NULL, - Logger varchar(100) NOT NULL, - Message varchar(100) NOT NULL, - CallSite varchar(100) NOT NULL, - Exception varchar(100) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS Logs_Id ON Logs (Id); - -CREATE TABLE IF NOT EXISTS Audit -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Date varchar(100) NOT NULL, - Username varchar(100) NOT NULL, - ChangeType varchar(100) NOT NULL, - OldValue varchar(100), - NewValue varchar(100) -); -CREATE UNIQUE INDEX IF NOT EXISTS Audit_Id ON Audit (Id); - - -CREATE TABLE IF NOT EXISTS DBInfo -( - SchemaVersion INTEGER -); - -CREATE TABLE IF NOT EXISTS ScheduledJobs -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Name varchar(100) NOT NULL, - LastRun varchar(100) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS ScheduledJobs_Id ON ScheduledJobs (Id); - -CREATE TABLE IF NOT EXISTS UsersToNotify -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Username varchar(100) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UsersToNotify_Id ON UsersToNotify (Id); - -CREATE TABLE IF NOT EXISTS IssueBlobs -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - RequestId INTEGER, - Type INTEGER NOT NULL, - Content BLOB NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS IssueBlobs_Id ON IssueBlobs (Id); \ No newline at end of file +--Any DB changes need to be made in this file. + +CREATE TABLE IF NOT EXISTS Users +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + UserGuid varchar(50) NOT NULL , + UserName varchar(50) NOT NULL, + Salt BLOB NOT NULL, + Hash BLOB NOT NULL, + Claims BLOB NOT NULL, + UserProperties BLOB +); + + +CREATE TABLE IF NOT EXISTS GlobalSettings +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + SettingsName varchar(50) NOT NULL, + Content varchar(100) NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS GlobalSettings_Id ON GlobalSettings (Id); + +CREATE TABLE IF NOT EXISTS RequestBlobs +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + ProviderId INTEGER NOT NULL, + Type INTEGER NOT NULL, + Content BLOB NOT NULL, + MusicId TEXT +); +CREATE UNIQUE INDEX IF NOT EXISTS RequestBlobs_Id ON RequestBlobs (Id); + +CREATE TABLE IF NOT EXISTS Logs +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + Date varchar(100) NOT NULL, + Level varchar(100) NOT NULL, + Logger varchar(100) NOT NULL, + Message varchar(100) NOT NULL, + CallSite varchar(100) NOT NULL, + Exception varchar(100) NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS Logs_Id ON Logs (Id); + +CREATE TABLE IF NOT EXISTS Audit +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + Date varchar(100) NOT NULL, + Username varchar(100) NOT NULL, + ChangeType varchar(100) NOT NULL, + OldValue varchar(100), + NewValue varchar(100) +); +CREATE UNIQUE INDEX IF NOT EXISTS Audit_Id ON Audit (Id); + + +CREATE TABLE IF NOT EXISTS DBInfo +( + SchemaVersion INTEGER +); + +CREATE TABLE IF NOT EXISTS ScheduledJobs +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + Name varchar(100) NOT NULL, + LastRun varchar(100) NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS ScheduledJobs_Id ON ScheduledJobs (Id); + +CREATE TABLE IF NOT EXISTS UsersToNotify +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + Username varchar(100) NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS UsersToNotify_Id ON UsersToNotify (Id); + +CREATE TABLE IF NOT EXISTS IssueBlobs +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + RequestId INTEGER, + Type INTEGER NOT NULL, + Content BLOB NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS IssueBlobs_Id ON IssueBlobs (Id); + +CREATE TABLE IF NOT EXISTS RequestLimit +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + Username varchar(100) NOT NULL, + FirstRequestDate varchar(100) NOT NULL, + RequestCount INTEGER NOT NULL, + RequestType INTEGER NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS RequestLimit_Id ON RequestLimit (Id); + +CREATE TABLE IF NOT EXISTS PlexUsers +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + PlexUserId INTEGER NOT NULL, + UserAlias varchar(100) NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS PlexUsers_Id ON RequestLimit (Id); \ No newline at end of file diff --git a/PlexRequests.UI.Tests/StringHelperTests.cs b/PlexRequests.UI.Tests/StringHelperTests.cs index e1cbb811a..546c8ee3c 100644 --- a/PlexRequests.UI.Tests/StringHelperTests.cs +++ b/PlexRequests.UI.Tests/StringHelperTests.cs @@ -52,9 +52,9 @@ namespace PlexRequests.UI.Tests { get { - yield return new TestCaseData("abcCba").Returns("AbcCba"); - yield return new TestCaseData("").Returns(""); - yield return new TestCaseData("12DSAda").Returns("12DSAda"); + yield return new TestCaseData("abcCba").Returns("AbcCba").SetName("pascalCase"); + yield return new TestCaseData("").Returns("").SetName("Empty"); + yield return new TestCaseData("12DSAda").Returns("12DSAda").SetName("With numbers"); } } @@ -62,14 +62,13 @@ namespace PlexRequests.UI.Tests { get { - yield return new TestCaseData("abcCba").Returns("Abc Cba"); - yield return new TestCaseData("").Returns(""); - yield return new TestCaseData("JamieRees").Returns("Jamie Rees"); - yield return new TestCaseData("Jamierees").Returns("Jamierees"); - yield return new TestCaseData("ThisIsANewString").Returns("This Is A New String"); - yield return new TestCaseData("").Returns(""); - yield return new TestCaseData(IssueStatus.PendingIssue.ToString()).Returns("Pending Issue"); - yield return new TestCaseData(IssueStatus.ResolvedIssue.ToString()).Returns("Resolved Issue"); + yield return new TestCaseData("abcCba").Returns("Abc Cba").SetName("spaces"); + yield return new TestCaseData("").Returns("").SetName("empty"); + yield return new TestCaseData("JamieRees").Returns("Jamie Rees").SetName("Name"); + yield return new TestCaseData("Jamierees").Returns("Jamierees").SetName("single word"); + yield return new TestCaseData("ThisIsANewString").Returns("This Is A New String").SetName("longer string"); + yield return new TestCaseData(IssueStatus.PendingIssue.ToString()).Returns("Pending Issue").SetName("enum pending"); + yield return new TestCaseData(IssueStatus.ResolvedIssue.ToString()).Returns("Resolved Issue").SetName("enum resolved"); } } } diff --git a/PlexRequests.UI.Tests/app.config b/PlexRequests.UI.Tests/app.config index d1c064e34..3799f3a1a 100644 --- a/PlexRequests.UI.Tests/app.config +++ b/PlexRequests.UI.Tests/app.config @@ -1,28 +1,28 @@ - - - - -
- - - - - - - - - - - - - - - - - - - - - - + + + + +
+ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PlexRequests.UI/Bootstrapper.cs b/PlexRequests.UI/Bootstrapper.cs index 176404b02..d29c9c324 100644 --- a/PlexRequests.UI/Bootstrapper.cs +++ b/PlexRequests.UI/Bootstrapper.cs @@ -1,228 +1,230 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: Bootstrapper.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.Net; - -using Mono.Data.Sqlite; - -using Nancy; -using Nancy.Authentication.Forms; -using Nancy.Bootstrapper; -using Nancy.Conventions; -using Nancy.Cryptography; -using Nancy.Diagnostics; -using Nancy.Session; -using Nancy.TinyIoc; - -using PlexRequests.Api; -using PlexRequests.Api.Interfaces; -using PlexRequests.Core; -using PlexRequests.Core.SettingModels; -using PlexRequests.Helpers; -using PlexRequests.Services; -using PlexRequests.Services.Interfaces; -using PlexRequests.Services.Notification; -using PlexRequests.Store; -using PlexRequests.Store.Models; -using PlexRequests.Store.Repository; -using PlexRequests.UI.Helpers; -using Nancy.Json; - -using PlexRequests.Helpers.Analytics; -using PlexRequests.Services.Jobs; -using PlexRequests.UI.Jobs; - -using Quartz; -using Quartz.Impl; -using Quartz.Spi; - -namespace PlexRequests.UI -{ - public class Bootstrapper : DefaultNancyBootstrapper - { - // The bootstrapper enables you to reconfigure the composition of the framework, - // by overriding the various methods and properties. - // For more information https://github.com/NancyFx/Nancy/wiki/Bootstrapper - - protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) - { - ConfigureContainer(container); - - JsonSettings.MaxJsonLength = int.MaxValue; - - CookieBasedSessions.Enable(pipelines, CryptographyConfiguration.Default); - StaticConfiguration.DisableErrorTraces = false; - - base.ApplicationStartup(container, pipelines); - - var settings = new SettingsServiceV2(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider())); - var baseUrl = settings.GetSettings().BaseUrl; - var redirect = string.IsNullOrEmpty(baseUrl) ? "~/login" : $"~/{baseUrl}/login"; - - // Enable forms auth - var formsAuthConfiguration = new FormsAuthenticationConfiguration - { - RedirectUrl = redirect, - UserMapper = container.Resolve() - }; - - FormsAuthentication.Enable(pipelines, formsAuthConfiguration); - - ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls; - ServicePointManager.ServerCertificateValidationCallback += - (sender, certificate, chain, sslPolicyErrors) => true; - - SubscribeAllObservers(container); - - } - - protected override void ConfigureConventions(NancyConventions nancyConventions) - { - base.ConfigureConventions(nancyConventions); - - var settings = new SettingsServiceV2(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider())); - var assetLocation = settings.GetSettings().BaseUrl; - nancyConventions.StaticContentsConventions.Add( - StaticContentConventionBuilder.AddDirectory($"{assetLocation}/Content", "Content") - ); - - nancyConventions.StaticContentsConventions.AddDirectory($"{assetLocation}/docs", "swagger-ui"); - } - - protected override DiagnosticsConfiguration DiagnosticsConfiguration => new DiagnosticsConfiguration { Password = @"password" }; - - private void SubscribeAllObservers(TinyIoCContainer container) - { - var notificationService = container.Resolve(); - - var emailSettingsService = container.Resolve>(); - var emailSettings = emailSettingsService.GetSettings(); - if (emailSettings.Enabled) - { - notificationService.Subscribe(new EmailMessageNotification(emailSettingsService)); - } - - var pushbulletService = container.Resolve>(); - var pushbulletSettings = pushbulletService.GetSettings(); - if (pushbulletSettings.Enabled) - { - notificationService.Subscribe(new PushbulletNotification(container.Resolve(), pushbulletService)); - } - - var pushoverService = container.Resolve>(); - var pushoverSettings = pushoverService.GetSettings(); - if (pushoverSettings.Enabled) - { - notificationService.Subscribe(new PushoverNotification(container.Resolve(), pushoverService)); - } - - var slackService = container.Resolve>(); - var slackSettings = slackService.GetSettings(); - if (slackSettings.Enabled) - { - notificationService.Subscribe(new SlackNotification(container.Resolve(), slackService)); - } - } - - protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context) - { - //CORS Enable - pipelines.AfterRequest.AddItemToEndOfPipeline((ctx) => - { - ctx.Response.WithHeader("Access-Control-Allow-Origin", "*") - .WithHeader("Access-Control-Allow-Methods", "POST,GET") - .WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type"); - - }); - base.RequestStartup(container, pipelines, context); - } - - private void ConfigureContainer(TinyIoCContainer container) - { - container.Register().AsSingleton(); - container.Register(new DbConfiguration(new SqliteFactory())); - container.Register, UserRepository>(); - container.Register(); - container.Register(); - container.Register, SettingsServiceV2>(); - container.Register, SettingsServiceV2>(); - container.Register, SettingsServiceV2>(); - container.Register, SettingsServiceV2>(); - container.Register, SettingsServiceV2>(); - - // Notification Service - container.Register().AsSingleton(); - // Settings - container.Register, SettingsServiceV2>(); - container.Register, SettingsServiceV2>(); - container.Register, SettingsServiceV2>(); - container.Register, SettingsServiceV2>(); - container.Register, SettingsServiceV2>(); - container.Register, SettingsServiceV2>(); - - container.Register, SettingsServiceV2>(); - container.Register, SettingsServiceV2>(); - container.Register, SettingsServiceV2>(); - - // Repo's - container.Register, GenericRepository>(); - container.Register, GenericRepository>(); - container.Register, GenericRepository>(); - container.Register, GenericRepository>(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - - // Services - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - - container.Register(); - container.Register(); - container.Register(); - - - // Api - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - - var loc = ServiceLocator.Instance; - loc.SetContainer(container); - } - } +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: Bootstrapper.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.Net; + +using Mono.Data.Sqlite; + +using Nancy; +using Nancy.Authentication.Forms; +using Nancy.Bootstrapper; +using Nancy.Conventions; +using Nancy.Cryptography; +using Nancy.Diagnostics; +using Nancy.Session; +using Nancy.TinyIoc; + +using PlexRequests.Api; +using PlexRequests.Api.Interfaces; +using PlexRequests.Core; +using PlexRequests.Core.SettingModels; +using PlexRequests.Helpers; +using PlexRequests.Services; +using PlexRequests.Services.Interfaces; +using PlexRequests.Services.Notification; +using PlexRequests.Store; +using PlexRequests.Store.Models; +using PlexRequests.Store.Repository; +using PlexRequests.UI.Helpers; +using Nancy.Json; + +using PlexRequests.Helpers.Analytics; +using PlexRequests.Services.Jobs; +using PlexRequests.UI.Jobs; + +using Quartz; +using Quartz.Impl; +using Quartz.Spi; + +namespace PlexRequests.UI +{ + public class Bootstrapper : DefaultNancyBootstrapper + { + // The bootstrapper enables you to reconfigure the composition of the framework, + // by overriding the various methods and properties. + // For more information https://github.com/NancyFx/Nancy/wiki/Bootstrapper + + protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) + { + ConfigureContainer(container); + + JsonSettings.MaxJsonLength = int.MaxValue; + + CookieBasedSessions.Enable(pipelines, CryptographyConfiguration.Default); + StaticConfiguration.DisableErrorTraces = false; + + base.ApplicationStartup(container, pipelines); + + var settings = new SettingsServiceV2(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider())); + var baseUrl = settings.GetSettings().BaseUrl; + var redirect = string.IsNullOrEmpty(baseUrl) ? "~/login" : $"~/{baseUrl}/login"; + + // Enable forms auth + var formsAuthConfiguration = new FormsAuthenticationConfiguration + { + RedirectUrl = redirect, + UserMapper = container.Resolve() + }; + + FormsAuthentication.Enable(pipelines, formsAuthConfiguration); + + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls; + ServicePointManager.ServerCertificateValidationCallback += + (sender, certificate, chain, sslPolicyErrors) => true; + + SubscribeAllObservers(container); + + } + + protected override void ConfigureConventions(NancyConventions nancyConventions) + { + base.ConfigureConventions(nancyConventions); + + var settings = new SettingsServiceV2(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider())); + var assetLocation = settings.GetSettings().BaseUrl; + nancyConventions.StaticContentsConventions.Add( + StaticContentConventionBuilder.AddDirectory($"{assetLocation}/Content", "Content") + ); + + nancyConventions.StaticContentsConventions.AddDirectory($"{assetLocation}/docs", "swagger-ui"); + } + + protected override DiagnosticsConfiguration DiagnosticsConfiguration => new DiagnosticsConfiguration { Password = @"password" }; + + private void SubscribeAllObservers(TinyIoCContainer container) + { + var notificationService = container.Resolve(); + + var emailSettingsService = container.Resolve>(); + var emailSettings = emailSettingsService.GetSettings(); + if (emailSettings.Enabled) + { + notificationService.Subscribe(new EmailMessageNotification(emailSettingsService)); + } + + var pushbulletService = container.Resolve>(); + var pushbulletSettings = pushbulletService.GetSettings(); + if (pushbulletSettings.Enabled) + { + notificationService.Subscribe(new PushbulletNotification(container.Resolve(), pushbulletService)); + } + + var pushoverService = container.Resolve>(); + var pushoverSettings = pushoverService.GetSettings(); + if (pushoverSettings.Enabled) + { + notificationService.Subscribe(new PushoverNotification(container.Resolve(), pushoverService)); + } + + var slackService = container.Resolve>(); + var slackSettings = slackService.GetSettings(); + if (slackSettings.Enabled) + { + notificationService.Subscribe(new SlackNotification(container.Resolve(), slackService)); + } + } + + protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context) + { + //CORS Enable + pipelines.AfterRequest.AddItemToEndOfPipeline((ctx) => + { + ctx.Response.WithHeader("Access-Control-Allow-Origin", "*") + .WithHeader("Access-Control-Allow-Methods", "POST,GET") + .WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type"); + + }); + base.RequestStartup(container, pipelines, context); + } + + private void ConfigureContainer(TinyIoCContainer container) + { + container.Register().AsSingleton(); + container.Register(new DbConfiguration(new SqliteFactory())); + container.Register, UserRepository>(); + container.Register(); + container.Register(); + container.Register, SettingsServiceV2>(); + container.Register, SettingsServiceV2>(); + container.Register, SettingsServiceV2>(); + container.Register, SettingsServiceV2>(); + container.Register, SettingsServiceV2>(); + + // Notification Service + container.Register().AsSingleton(); + // Settings + container.Register, SettingsServiceV2>(); + container.Register, SettingsServiceV2>(); + container.Register, SettingsServiceV2>(); + container.Register, SettingsServiceV2>(); + container.Register, SettingsServiceV2>(); + container.Register, SettingsServiceV2>(); + + container.Register, SettingsServiceV2>(); + container.Register, SettingsServiceV2>(); + container.Register, SettingsServiceV2>(); + + // Repo's + container.Register, GenericRepository>(); + container.Register, GenericRepository>(); + container.Register, GenericRepository>(); + container.Register, GenericRepository>(); + container.Register, GenericRepository>(); + container.Register, GenericRepository>(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + + // Services + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + + container.Register(); + container.Register(); + container.Register(); + + + // Api + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + + var loc = ServiceLocator.Instance; + loc.SetContainer(container); + } + } } \ No newline at end of file diff --git a/PlexRequests.UI/Content/issues.js b/PlexRequests.UI/Content/issues.js index f97ea0ba0..87410c45c 100644 --- a/PlexRequests.UI/Content/issues.js +++ b/PlexRequests.UI/Content/issues.js @@ -264,8 +264,9 @@ function buildIssueContext(result) { requestId: result.requestId, type: result.type, title: result.title, - issues: result.issues - }; + issues: result.issues, + admin: result.admin +}; return context; } diff --git a/PlexRequests.UI/Content/requests-1.7.js b/PlexRequests.UI/Content/requests-1.7.js index 041b67bcc..a65bc3425 100644 --- a/PlexRequests.UI/Content/requests-1.7.js +++ b/PlexRequests.UI/Content/requests-1.7.js @@ -159,7 +159,7 @@ $('#approveTVShows').click(function (e) { $('#deleteMovies').click(function (e) { e.preventDefault(); - if (!confirm("Are you sure you want to delete all TV show requests?")) return; + if (!confirm("Are you sure you want to delete all Movie requests?")) return; var buttonId = e.target.id; var origHtml = $(this).html(); diff --git a/PlexRequests.UI/Helpers/CultureHelper.cs b/PlexRequests.UI/Helpers/CultureHelper.cs new file mode 100644 index 000000000..d933d758f --- /dev/null +++ b/PlexRequests.UI/Helpers/CultureHelper.cs @@ -0,0 +1,117 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: CultureHelper.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; +using System.Linq; +using System.Threading; + +namespace PlexRequests.UI.Helpers +{ + public class CultureHelper + { + private static readonly List ValidCultures = new List { "en-US", "de-DE", "fr-FR", "es-ES", "de", "en", "fr", "es","da","sv","it","nl" }; + + private static readonly List ImplimentedCultures = new List { + "en-US", + "en", + "de", + "fr", + "es", + "da", + "sv", + "it", + "nl" + }; + + /// + /// Returns true if the language is a right-to-left language. + /// + public static bool IsRighToLeft() + { + return Thread.CurrentThread.CurrentCulture.TextInfo.IsRightToLeft; + } + + /// + /// Returns a valid culture name based on "name" parameter. If "name" is not valid, it returns the default culture "en-US" + /// + /// Culture's name (e.g. en-US) + public static string GetImplementedCulture(string name) + { + if (string.IsNullOrEmpty(name)) + { + return GetDefaultCulture(); + } + + // Validate the culture + if (!ValidCultures.Any(c => c.Equals(name, StringComparison.InvariantCultureIgnoreCase))) + { + return GetDefaultCulture(); + } + + if (ImplimentedCultures.Any(c => c.Equals(name, StringComparison.InvariantCultureIgnoreCase))) + { + return name; // We do have the culture, lets go with it + } + + // Find a close match. + var match = GetNeutralCulture(name); + + foreach (var c in ImplimentedCultures.Where(c => c.StartsWith(match, StringComparison.InvariantCultureIgnoreCase))) + { + return c; + } + + return GetDefaultCulture(); // return Default culture since we cannot find anything + } + + /// + /// Returns default culture name which is the first name declared (e.g. en-US) + /// + /// Culture string e.g. en-US + public static string GetDefaultCulture() + { + return ImplimentedCultures[0]; // return first culture + } + + public static string GetCurrentCulture() + { + return Thread.CurrentThread.CurrentCulture.Name; + } + + public static string GetCurrentNeutralCulture() + { + return GetNeutralCulture(Thread.CurrentThread.CurrentCulture.Name); + } + + public static string GetNeutralCulture(string name) + { + if (!name.Contains("-")) return name; + + return name.Split('-')[0]; // Read first part only + } + } +} \ No newline at end of file diff --git a/PlexRequests.UI/Jobs/Scheduler.cs b/PlexRequests.UI/Jobs/Scheduler.cs index dc17a07d5..6e151f2bf 100644 --- a/PlexRequests.UI/Jobs/Scheduler.cs +++ b/PlexRequests.UI/Jobs/Scheduler.cs @@ -62,6 +62,7 @@ namespace PlexRequests.UI.Jobs var cp = JobBuilder.Create().WithIdentity("CouchPotatoCacher", "Cache").Build(); var store = JobBuilder.Create().WithIdentity("StoreBackup", "Database").Build(); var storeClean = JobBuilder.Create().WithIdentity("StoreCleanup", "Database").Build(); + var userRequestLimitReset = JobBuilder.Create().WithIdentity("UserRequestLimiter", "Request").Build(); jobs.Add(plex); jobs.Add(sickrage); @@ -69,6 +70,7 @@ namespace PlexRequests.UI.Jobs jobs.Add(cp); jobs.Add(store); jobs.Add(storeClean); + jobs.Add(userRequestLimitReset); return jobs; } @@ -150,6 +152,13 @@ namespace PlexRequests.UI.Jobs .WithSimpleSchedule(x => x.WithIntervalInHours(s.StoreCleanup).RepeatForever()) .Build(); + var userRequestLimiter = + TriggerBuilder.Create() + .WithIdentity("UserRequestLimiter", "Request") + .StartAt(DateTimeOffset.Now.AddMinutes(5)) // Everything has started on application start, lets wait 5 minutes + .WithSimpleSchedule(x => x.WithIntervalInHours(s.UserRequestLimitResetter).RepeatForever()) + .Build(); + triggers.Add(plexAvailabilityChecker); triggers.Add(srCacher); @@ -157,6 +166,7 @@ namespace PlexRequests.UI.Jobs triggers.Add(cpCacher); triggers.Add(storeBackup); triggers.Add(storeCleanup); + triggers.Add(userRequestLimiter); return triggers; } diff --git a/PlexRequests.UI/Models/IssuesViewMOdel.cs b/PlexRequests.UI/Models/IssuesViewMOdel.cs index d05b2a8e8..00f3c5082 100644 --- a/PlexRequests.UI/Models/IssuesViewMOdel.cs +++ b/PlexRequests.UI/Models/IssuesViewMOdel.cs @@ -36,6 +36,7 @@ namespace PlexRequests.UI.Models public string Title { get; set; } public string Issues { get; set; } public string Type { get; set; } + public bool Admin { get; set; } } } \ No newline at end of file diff --git a/PlexRequests.UI/Modules/AdminModule.cs b/PlexRequests.UI/Modules/AdminModule.cs index 8e0ff9363..7ad308735 100644 --- a/PlexRequests.UI/Modules/AdminModule.cs +++ b/PlexRequests.UI/Modules/AdminModule.cs @@ -46,9 +46,12 @@ using NLog; using MarkdownSharp; +using Nancy.Responses; + using PlexRequests.Api; using PlexRequests.Api.Interfaces; using PlexRequests.Core; +using PlexRequests.Core.Models; using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; using PlexRequests.Helpers.Analytics; @@ -204,6 +207,8 @@ namespace PlexRequests.UI.Modules Get["/scheduledjobs", true] = async (x, ct) => await GetScheduledJobs(); Post["/scheduledjobs", true] = async (x, ct) => await SaveScheduledJobs(); + + Post["/clearlogs", true] = async (x, ct) => await ClearLogs(); } private async Task Authentication() @@ -256,9 +261,13 @@ namespace PlexRequests.UI.Modules model.BaseUrl = model.BaseUrl.Remove(0, 1); } } + if (!model.CollectAnalyticData) + { + Analytics.TrackEventAsync(Category.Admin, Action.Save, "CollectAnalyticData turned off", Username, CookieHelper.GetAnalyticClientId(Cookies)); + } var result = PrService.SaveSettings(model); - await Analytics.TrackEventAsync(Category.Admin, Action.Save, "PlexRequestSettings", Username, CookieHelper.GetAnalyticClientId(Cookies)); + Analytics.TrackEventAsync(Category.Admin, Action.Save, "PlexRequestSettings", Username, CookieHelper.GetAnalyticClientId(Cookies)); return Response.AsJson(result ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = "We could not save to the database, please try again" }); @@ -710,7 +719,7 @@ namespace PlexRequests.UI.Modules private Response UpdateLogLevels(int level) { var settings = LogService.GetSettings(); - + Analytics.TrackEventAsync(Category.Admin, Action.Update, "Updated Log Levels", Username, CookieHelper.GetAnalyticClientId(Cookies), level); // apply the level var newLevel = LogLevel.FromOrdinal(level); LoggingHelper.ReconfigureLogLevel(newLevel); @@ -751,6 +760,7 @@ namespace PlexRequests.UI.Modules private Response CreateApiKey() { this.RequiresClaims(UserClaims.Admin); + Analytics.TrackEventAsync(Category.Admin, Action.Create, "Created API Key", Username, CookieHelper.GetAnalyticClientId(Cookies)); var apiKey = Guid.NewGuid().ToString("N"); var settings = PrService.GetSettings(); @@ -838,6 +848,7 @@ namespace PlexRequests.UI.Modules { var settings = this.Bind(); + Analytics.TrackEventAsync(Category.Admin, Action.Update, "Update Landing Page", Username, CookieHelper.GetAnalyticClientId(Cookies)); var plexSettings = await PlexService.GetSettingsAsync(); if (string.IsNullOrEmpty(plexSettings.Ip)) { @@ -876,6 +887,8 @@ namespace PlexRequests.UI.Modules private async Task SaveScheduledJobs() { + + Analytics.TrackEventAsync(Category.Admin, Action.Update, "Update ScheduledJobs", Username, CookieHelper.GetAnalyticClientId(Cookies)); var settings = this.Bind(); var result = await ScheduledJobSettings.SaveSettingsAsync(settings); @@ -884,5 +897,24 @@ namespace PlexRequests.UI.Modules ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = "Could not save to Db Please check the logs" }); } + + private async Task ClearLogs() + { + try + { + Analytics.TrackEventAsync(Category.Admin, Action.Delete, "Clear Logs", Username, CookieHelper.GetAnalyticClientId(Cookies)); + var allLogs = await LogsRepo.GetAllAsync(); + foreach (var logEntity in allLogs) + { + await LogsRepo.DeleteAsync(logEntity); + } + return Response.AsJson(new JsonResponseModel { Result = true, Message = "Logs cleared successfully."}); + } + catch (Exception e) + { + Log.Error(e); + return Response.AsJson(new JsonResponseModel { Result = false, Message = e.Message }); + } + } } } \ No newline at end of file diff --git a/PlexRequests.UI/Modules/BaseModule.cs b/PlexRequests.UI/Modules/BaseModule.cs index 14b24141e..7bfa7eeb6 100644 --- a/PlexRequests.UI/Modules/BaseModule.cs +++ b/PlexRequests.UI/Modules/BaseModule.cs @@ -27,12 +27,14 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using Nancy; using PlexRequests.Core; using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; +using PlexRequests.UI.Helpers; using PlexRequests.UI.Models; namespace PlexRequests.UI.Modules @@ -40,9 +42,11 @@ namespace PlexRequests.UI.Modules public abstract class BaseModule : NancyModule { protected string BaseUrl { get; set; } + protected BaseModule(ISettingsService settingsService) { + var settings = settingsService.GetSettings(); var baseUrl = settings.BaseUrl; BaseUrl = baseUrl; @@ -50,10 +54,13 @@ namespace PlexRequests.UI.Modules var modulePath = string.IsNullOrEmpty(baseUrl) ? string.Empty : baseUrl; ModulePath = modulePath; + + Before += (ctx) => SetCookie(); } protected BaseModule(string modulePath, ISettingsService settingsService) { + var settings = settingsService.GetSettings(); var baseUrl = settings.BaseUrl; BaseUrl = baseUrl; @@ -61,6 +68,8 @@ namespace PlexRequests.UI.Modules var settingModulePath = string.IsNullOrEmpty(baseUrl) ? modulePath : $"{baseUrl}/{modulePath}"; ModulePath = settingModulePath; + + Before += (ctx) => SetCookie(); } private int _dateTimeOffset = -1; @@ -96,7 +105,7 @@ namespace PlexRequests.UI.Modules } } - protected IDictionary Cookies => Request.Cookies; + protected IDictionary Cookies => Request?.Cookies; protected bool IsAdmin { @@ -110,6 +119,41 @@ namespace PlexRequests.UI.Modules return claims.Contains(UserClaims.Admin) || claims.Contains(UserClaims.PowerUser); } } + protected string Culture { get; set; } + protected const string CultureCookieName = "_culture"; + protected Response SetCookie() + { + try + { + string cultureName; + + // Attempt to read the culture cookie from Request + var outCookie = string.Empty; + if (Cookies.TryGetValue(CultureCookieName, out outCookie)) + { + cultureName = outCookie; + } + else + { + cultureName = Request.Headers?.AcceptLanguage?.FirstOrDefault()?.Item1; + } + + // Validate culture name + cultureName = CultureHelper.GetImplementedCulture(cultureName); // This is safe + + + // Modify current thread's cultures + Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName); + Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; + + Culture = Thread.CurrentThread.CurrentCulture.Name; + } + catch (Exception) + { + // Couldn't Set the culture + } + return null; + } } } \ No newline at end of file diff --git a/PlexRequests.UI/Modules/CultureModule.cs b/PlexRequests.UI/Modules/CultureModule.cs new file mode 100644 index 000000000..8d093d32a --- /dev/null +++ b/PlexRequests.UI/Modules/CultureModule.cs @@ -0,0 +1,79 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: CultureModule.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.Threading.Tasks; + +using Nancy; +using Nancy.Extensions; +using Nancy.Responses; + +using PlexRequests.Core; +using PlexRequests.Core.SettingModels; +using PlexRequests.Helpers; +using PlexRequests.Helpers.Analytics; +using PlexRequests.UI.Helpers; + +namespace PlexRequests.UI.Modules +{ + public class CultureModule : BaseModule + { + public CultureModule(ISettingsService pr, IAnalytics a) : base("culture",pr) + { + Analytics = a; + + Get["/", true] = async(x,c) => await SetCulture(); + } + + private IAnalytics Analytics { get; } + + public async Task SetCulture() + { + var culture = (string)Request.Query["l"]; + var returnUrl = (string)Request.Query["u"]; + + // Validate + culture = CultureHelper.GetImplementedCulture(culture); + + var outCookie = string.Empty; + if (Cookies.TryGetValue(CultureCookieName, out outCookie)) + { + Cookies[CultureCookieName] = culture; + } + else + { + Cookies.Add(CultureCookieName, culture); + } + var cookie = Cookies[CultureCookieName]; + var response = Context.GetRedirect(returnUrl); + + response.WithCookie(CultureCookieName, cookie ?? culture, DateTime.Now.AddYears(1)); + Analytics.TrackEventAsync(Category.Navbar, PlexRequests.Helpers.Analytics.Action.Language, culture, Username, CookieHelper.GetAnalyticClientId(Cookies)); + + return response; + } + } +} \ No newline at end of file diff --git a/PlexRequests.UI/Modules/IssuesModule.cs b/PlexRequests.UI/Modules/IssuesModule.cs index d5129f303..e2ed05aae 100644 --- a/PlexRequests.UI/Modules/IssuesModule.cs +++ b/PlexRequests.UI/Modules/IssuesModule.cs @@ -78,7 +78,7 @@ namespace PlexRequests.UI.Modules foreach (var i in issuesModels) { - var model = new IssuesViewModel { Id = i.Id, RequestId = i.RequestId, Title = i.Title, Type = i.Type.ToString().ToCamelCaseWords(), }; + var model = new IssuesViewModel { Id = i.Id, RequestId = i.RequestId, Title = i.Title, Type = i.Type.ToString().ToCamelCaseWords(), Admin = IsAdmin }; // Create a string with all of the current issue states with a "," delimiter in e.g. Wrong Content, Playback Issues var state = i.Issues.Select(x => x.Issue).ToArray(); diff --git a/PlexRequests.UI/Modules/LandingPageModule.cs b/PlexRequests.UI/Modules/LandingPageModule.cs index 4564e32ef..9468662e6 100644 --- a/PlexRequests.UI/Modules/LandingPageModule.cs +++ b/PlexRequests.UI/Modules/LandingPageModule.cs @@ -58,6 +58,7 @@ namespace PlexRequests.UI.Modules private async Task Index() { + var s = await LandingSettings.GetSettingsAsync(); var model = new LandingPageViewModel { diff --git a/PlexRequests.UI/Modules/PlexUsersModule.cs b/PlexRequests.UI/Modules/PlexUsersModule.cs new file mode 100644 index 000000000..901199505 --- /dev/null +++ b/PlexRequests.UI/Modules/PlexUsersModule.cs @@ -0,0 +1,98 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: PlexUsersModule.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.Linq; +using System.Threading.Tasks; + +using Nancy; +using Nancy.Responses.Negotiation; + +using PlexRequests.Api.Interfaces; +using PlexRequests.Api.Models.Music; +using PlexRequests.Core; +using PlexRequests.Core.SettingModels; +using PlexRequests.Store.Models; +using PlexRequests.Store.Repository; +using PlexRequests.UI.Models; + +namespace PlexRequests.UI.Modules +{ + public class PlexUsersModule : BaseAuthModule + { + public PlexUsersModule(ISettingsService pr, IPlexApi plexApi, ISettingsService auth, + IRepository repo) : base("plexusers", pr) + { + PlexApi = plexApi; + AuthSettings = auth; + Repo = repo; + + Get["/"] = x => Index(); + Get["/users", true] = async (x, ct) => await GetPlexUsers(); + + Post["/alias", true] = async (x, ct) => await Alias(); + } + + private IPlexApi PlexApi { get; } + private ISettingsService AuthSettings { get; } + private IRepository Repo { get; } + + + private Negotiator Index() + { + return View["Index"]; + } + + private async Task GetPlexUsers() + { + var authSettings = await AuthSettings.GetSettingsAsync(); + var users = PlexApi.GetUsers(authSettings.PlexAuthToken); + + return Response.AsJson(users.User); + } + + private async Task Alias() + { + var plexUserId = (int)Request.Form["plexid"]; + var alias = (string)Request.Form["alias"]; + var allUsers = await Repo.GetAllAsync(); + var existingUser = allUsers.FirstOrDefault(x => x.PlexUserId == plexUserId); + if (existingUser == null) + { + // Create a new mapping + existingUser = new PlexUsers { PlexUserId = plexUserId, UserAlias = alias }; + } + else + { + // Modify existing alias + existingUser.UserAlias = alias; + } + + await Repo.InsertAsync(existingUser); + + return Response.AsJson(new JsonResponseModel { Result = true }); + } + } +} \ No newline at end of file diff --git a/PlexRequests.UI/Modules/RequestsModule.cs b/PlexRequests.UI/Modules/RequestsModule.cs index 8d73c0bb0..c538fa409 100644 --- a/PlexRequests.UI/Modules/RequestsModule.cs +++ b/PlexRequests.UI/Modules/RequestsModule.cs @@ -1,377 +1,387 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: RequestsModule.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.Linq; - -using Nancy; -using Nancy.Responses.Negotiation; -using Nancy.Security; - -using PlexRequests.Core; -using PlexRequests.Core.SettingModels; -using PlexRequests.Services.Interfaces; -using PlexRequests.Services.Notification; -using PlexRequests.Store; -using PlexRequests.UI.Models; -using PlexRequests.Helpers; -using PlexRequests.UI.Helpers; -using System.Collections.Generic; -using PlexRequests.Api.Interfaces; -using System.Threading.Tasks; - -using NLog; - -namespace PlexRequests.UI.Modules -{ - public class RequestsModule : BaseAuthModule - { - public RequestsModule( - IRequestService service, - ISettingsService prSettings, - ISettingsService plex, - INotificationService notify, - ISettingsService sonarrSettings, - ISettingsService sickRageSettings, - ISettingsService cpSettings, - ICouchPotatoApi cpApi, - ISonarrApi sonarrApi, - ISickRageApi sickRageApi, - ICacheProvider cache) : base("requests", prSettings) - { - Service = service; - PrSettings = prSettings; - PlexSettings = plex; - NotificationService = notify; - SonarrSettings = sonarrSettings; - SickRageSettings = sickRageSettings; - CpSettings = cpSettings; - SonarrApi = sonarrApi; - SickRageApi = sickRageApi; - CpApi = cpApi; - Cache = cache; - - Get["/", true] = async (x, ct) => await LoadRequests(); - Get["/movies", true] = async (x, ct) => await GetMovies(); - Get["/tvshows", true] = async (c, ct) => await GetTvShows(); - Get["/albums", true] = async (x, ct) => await GetAlbumRequests(); - Post["/delete", true] = async (x, ct) => await DeleteRequest((int)Request.Form.id); - Post["/reportissue", true] = async (x, ct) => await ReportIssue((int)Request.Form.requestId, (IssueState)(int)Request.Form.issue, null); - Post["/reportissuecomment", true] = async (x, ct) => await ReportIssue((int)Request.Form.requestId, IssueState.Other, (string)Request.Form.commentArea); - - Post["/clearissues", true] = async (x, ct) => await ClearIssue((int)Request.Form.Id); - - Post["/changeavailability", true] = async (x, ct) => await ChangeRequestAvailability((int)Request.Form.Id, (bool)Request.Form.Available); - } - - private static Logger Log = LogManager.GetCurrentClassLogger(); - private IRequestService Service { get; } - private INotificationService NotificationService { get; } - private ISettingsService PrSettings { get; } - private ISettingsService PlexSettings { get; } - private ISettingsService SonarrSettings { get; } - private ISettingsService SickRageSettings { get; } - private ISettingsService CpSettings { get; } - private ISonarrApi SonarrApi { get; } - private ISickRageApi SickRageApi { get; } - private ICouchPotatoApi CpApi { get; } - private ICacheProvider Cache { get; } - - private async Task LoadRequests() - { - var settings = await PrSettings.GetSettingsAsync(); - return View["Index", settings]; - } - - private async Task GetMovies() - { - var settings = PrSettings.GetSettings(); - - var allRequests = await Service.GetAllAsync(); - allRequests = allRequests.Where(x => x.Type == RequestType.Movie); - - var dbMovies = allRequests.ToList(); - - if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin) - { - dbMovies = dbMovies.Where(x => x.UserHasRequested(Username)).ToList(); - } - - List qualities = new List(); - - if (IsAdmin) - { - var cpSettings = CpSettings.GetSettings(); - if (cpSettings.Enabled) - { - try - { - var result = await Cache.GetOrSetAsync(CacheKeys.CouchPotatoQualityProfiles, async () => - { - return await Task.Run(() => CpApi.GetProfiles(cpSettings.FullUri, cpSettings.ApiKey)).ConfigureAwait(false); - }); - - qualities = result.list.Select(x => new QualityModel() { Id = x._id, Name = x.label }).ToList(); - - } - catch (Exception e) - { - Log.Info(e); - } - } - } - - var viewModel = dbMovies.Select(movie => new RequestViewModel - { - ProviderId = movie.ProviderId, - Type = movie.Type, - Status = movie.Status, - ImdbId = movie.ImdbId, - Id = movie.Id, - PosterPath = movie.PosterPath, - ReleaseDate = movie.ReleaseDate, - ReleaseDateTicks = movie.ReleaseDate.Ticks, - RequestedDate = movie.RequestedDate, - Released = DateTime.Now > movie.ReleaseDate, - RequestedDateTicks = DateTimeHelper.OffsetUTCDateTime(movie.RequestedDate, DateTimeOffset).Ticks, - Approved = movie.Available || movie.Approved, - Title = movie.Title, - Overview = movie.Overview, - RequestedUsers = IsAdmin ? movie.AllUsers.ToArray() : new string[] { }, - ReleaseYear = movie.ReleaseDate.Year.ToString(), - Available = movie.Available, - Admin = IsAdmin, - IssueId = movie.IssueId, - Qualities = qualities.ToArray() - }).ToList(); - - return Response.AsJson(viewModel); - } - - private async Task GetTvShows() - { - var settings = PrSettings.GetSettings(); - - var requests = await Service.GetAllAsync(); - requests = requests.Where(x => x.Type == RequestType.TvShow); - - var dbTv = requests; - - if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin) - { - dbTv = dbTv.Where(x => x.UserHasRequested(Username)).ToList(); - } - - IEnumerable qualities = new List(); - if (IsAdmin) - { - try - { - var sonarrSettings = SonarrSettings.GetSettings(); - if (sonarrSettings.Enabled) - { - var result = Cache.GetOrSetAsync(CacheKeys.SonarrQualityProfiles, async () => - { - return await Task.Run(() => SonarrApi.GetProfiles(sonarrSettings.ApiKey, sonarrSettings.FullUri)); - }); - qualities = result.Result.Select(x => new QualityModel() { Id = x.id.ToString(), Name = x.name }).ToList(); - } - else - { - var sickRageSettings = SickRageSettings.GetSettings(); - if (sickRageSettings.Enabled) - { - qualities = sickRageSettings.Qualities.Select(x => new QualityModel() { Id = x.Key, Name = x.Value }).ToList(); - } - } - } - catch (Exception e) - { - Log.Info(e); - } - - } - - var viewModel = dbTv.Select(tv => - { - return new RequestViewModel - { - ProviderId = tv.ProviderId, - Type = tv.Type, - Status = tv.Status, - ImdbId = tv.ImdbId, - Id = tv.Id, - PosterPath = tv.PosterPath, - ReleaseDate = tv.ReleaseDate, - ReleaseDateTicks = tv.ReleaseDate.Ticks, - RequestedDate = tv.RequestedDate, - RequestedDateTicks = DateTimeHelper.OffsetUTCDateTime(tv.RequestedDate, DateTimeOffset).Ticks, - Released = DateTime.Now > tv.ReleaseDate, - Approved = tv.Available || tv.Approved, - Title = tv.Title, - Overview = tv.Overview, - RequestedUsers = IsAdmin ? tv.AllUsers.ToArray() : new string[] { }, - ReleaseYear = tv.ReleaseDate.Year.ToString(), - Available = tv.Available, - Admin = IsAdmin, - IssueId = tv.IssueId, - TvSeriesRequestType = tv.SeasonsRequested, - Qualities = qualities.ToArray() - }; - }).ToList(); - - return Response.AsJson(viewModel); - } - - private async Task GetAlbumRequests() - { - var settings = PrSettings.GetSettings(); - var dbAlbum = await Service.GetAllAsync(); - dbAlbum = dbAlbum.Where(x => x.Type == RequestType.Album); - if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin) - { - dbAlbum = dbAlbum.Where(x => x.UserHasRequested(Username)); - } - - var viewModel = dbAlbum.Select(album => - { - return new RequestViewModel - { - ProviderId = album.ProviderId, - Type = album.Type, - Status = album.Status, - ImdbId = album.ImdbId, - Id = album.Id, - PosterPath = album.PosterPath, - ReleaseDate = album.ReleaseDate, - ReleaseDateTicks = album.ReleaseDate.Ticks, - RequestedDate = album.RequestedDate, - RequestedDateTicks = DateTimeHelper.OffsetUTCDateTime(album.RequestedDate, DateTimeOffset).Ticks, - Released = DateTime.Now > album.ReleaseDate, - Approved = album.Available || album.Approved, - Title = album.Title, - Overview = album.Overview, - RequestedUsers = IsAdmin ? album.AllUsers.ToArray() : new string[] { }, - ReleaseYear = album.ReleaseDate.Year.ToString(), - Available = album.Available, - Admin = IsAdmin, - IssueId = album.IssueId, - TvSeriesRequestType = album.SeasonsRequested, - MusicBrainzId = album.MusicBrainzId, - ArtistName = album.ArtistName - - }; - }).ToList(); - - return Response.AsJson(viewModel); - } - - private async Task DeleteRequest(int requestid) - { - this.RequiresClaims(UserClaims.Admin); - - var currentEntity = await Service.GetAsync(requestid); - await Service.DeleteRequestAsync(currentEntity); - return Response.AsJson(new JsonResponseModel { Result = true }); - } - - /// - /// Reports the issue. - /// Comment can be null if the IssueState != Other - /// - /// The request identifier. - /// The issue. - /// The comment. - /// - private async Task ReportIssue(int requestId, IssueState issue, string comment) - { - var originalRequest = await Service.GetAsync(requestId); - if (originalRequest == null) - { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not add issue, please try again or contact the administrator!" }); - } - originalRequest.Issues = issue; - originalRequest.OtherMessage = !string.IsNullOrEmpty(comment) - ? $"{Username} - {comment}" - : string.Empty; - - - var result = await Service.UpdateRequestAsync(originalRequest); - - var model = new NotificationModel - { - User = Username, - NotificationType = NotificationType.Issue, - Title = originalRequest.Title, - DateTime = DateTime.Now, - Body = issue == IssueState.Other ? comment : issue.ToString().ToCamelCaseWords() - }; - await NotificationService.Publish(model); - - return Response.AsJson(result - ? new JsonResponseModel { Result = true } - : new JsonResponseModel { Result = false, Message = "Could not add issue, please try again or contact the administrator!" }); - } - - private async Task ClearIssue(int requestId) - { - this.RequiresClaims(UserClaims.Admin); - - var originalRequest = await Service.GetAsync(requestId); - if (originalRequest == null) - { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to clear it!" }); - } - originalRequest.Issues = IssueState.None; - originalRequest.OtherMessage = string.Empty; - - var result = await Service.UpdateRequestAsync(originalRequest); - return Response.AsJson(result - ? new JsonResponseModel { Result = true } - : new JsonResponseModel { Result = false, Message = "Could not clear issue, please try again or check the logs" }); - } - - private async Task ChangeRequestAvailability(int requestId, bool available) - { - this.RequiresClaims(UserClaims.Admin); - var originalRequest = await Service.GetAsync(requestId); - if (originalRequest == null) - { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to change the availability!" }); - } - - originalRequest.Available = available; - - var result = await Service.UpdateRequestAsync(originalRequest); - return Response.AsJson(result - ? new { Result = true, Available = available, Message = string.Empty } - : new { Result = false, Available = false, Message = "Could not update the availability, please try again or check the logs" }); - } - - - } -} +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: RequestsModule.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.Linq; + +using Nancy; +using Nancy.Responses.Negotiation; +using Nancy.Security; + +using PlexRequests.Core; +using PlexRequests.Core.SettingModels; +using PlexRequests.Services.Interfaces; +using PlexRequests.Services.Notification; +using PlexRequests.Store; +using PlexRequests.UI.Models; +using PlexRequests.Helpers; +using PlexRequests.UI.Helpers; +using System.Collections.Generic; +using PlexRequests.Api.Interfaces; +using System.Threading.Tasks; + +using NLog; + +using PlexRequests.Core.Models; +using PlexRequests.Helpers.Analytics; + +using Action = PlexRequests.Helpers.Analytics.Action; + +namespace PlexRequests.UI.Modules +{ + public class RequestsModule : BaseAuthModule + { + public RequestsModule( + IRequestService service, + ISettingsService prSettings, + ISettingsService plex, + INotificationService notify, + ISettingsService sonarrSettings, + ISettingsService sickRageSettings, + ISettingsService cpSettings, + ICouchPotatoApi cpApi, + ISonarrApi sonarrApi, + ISickRageApi sickRageApi, + ICacheProvider cache, + IAnalytics an) : base("requests", prSettings) + { + Service = service; + PrSettings = prSettings; + PlexSettings = plex; + NotificationService = notify; + SonarrSettings = sonarrSettings; + SickRageSettings = sickRageSettings; + CpSettings = cpSettings; + SonarrApi = sonarrApi; + SickRageApi = sickRageApi; + CpApi = cpApi; + Cache = cache; + Analytics = an; + + Get["/", true] = async (x, ct) => await LoadRequests(); + Get["/movies", true] = async (x, ct) => await GetMovies(); + Get["/tvshows", true] = async (c, ct) => await GetTvShows(); + Get["/albums", true] = async (x, ct) => await GetAlbumRequests(); + Post["/delete", true] = async (x, ct) => await DeleteRequest((int)Request.Form.id); + Post["/reportissue", true] = async (x, ct) => await ReportIssue((int)Request.Form.requestId, (IssueState)(int)Request.Form.issue, null); + Post["/reportissuecomment", true] = async (x, ct) => await ReportIssue((int)Request.Form.requestId, IssueState.Other, (string)Request.Form.commentArea); + + Post["/clearissues", true] = async (x, ct) => await ClearIssue((int)Request.Form.Id); + + Post["/changeavailability", true] = async (x, ct) => await ChangeRequestAvailability((int)Request.Form.Id, (bool)Request.Form.Available); + } + + private static Logger Log = LogManager.GetCurrentClassLogger(); + private IRequestService Service { get; } + private IAnalytics Analytics { get; } + private INotificationService NotificationService { get; } + private ISettingsService PrSettings { get; } + private ISettingsService PlexSettings { get; } + private ISettingsService SonarrSettings { get; } + private ISettingsService SickRageSettings { get; } + private ISettingsService CpSettings { get; } + private ISonarrApi SonarrApi { get; } + private ISickRageApi SickRageApi { get; } + private ICouchPotatoApi CpApi { get; } + private ICacheProvider Cache { get; } + + private async Task LoadRequests() + { + var settings = await PrSettings.GetSettingsAsync(); + return View["Index", settings]; + } + + private async Task GetMovies() + { + var settings = PrSettings.GetSettings(); + + var allRequests = await Service.GetAllAsync(); + allRequests = allRequests.Where(x => x.Type == RequestType.Movie); + + var dbMovies = allRequests.ToList(); + + if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin) + { + dbMovies = dbMovies.Where(x => x.UserHasRequested(Username)).ToList(); + } + + List qualities = new List(); + + if (IsAdmin) + { + var cpSettings = CpSettings.GetSettings(); + if (cpSettings.Enabled) + { + try + { + var result = await Cache.GetOrSetAsync(CacheKeys.CouchPotatoQualityProfiles, async () => + { + return await Task.Run(() => CpApi.GetProfiles(cpSettings.FullUri, cpSettings.ApiKey)).ConfigureAwait(false); + }); + + qualities = result.list.Select(x => new QualityModel() { Id = x._id, Name = x.label }).ToList(); + + } + catch (Exception e) + { + Log.Info(e); + } + } + } + + var viewModel = dbMovies.Select(movie => new RequestViewModel + { + ProviderId = movie.ProviderId, + Type = movie.Type, + Status = movie.Status, + ImdbId = movie.ImdbId, + Id = movie.Id, + PosterPath = movie.PosterPath, + ReleaseDate = movie.ReleaseDate, + ReleaseDateTicks = movie.ReleaseDate.Ticks, + RequestedDate = movie.RequestedDate, + Released = DateTime.Now > movie.ReleaseDate, + RequestedDateTicks = DateTimeHelper.OffsetUTCDateTime(movie.RequestedDate, DateTimeOffset).Ticks, + Approved = movie.Available || movie.Approved, + Title = movie.Title, + Overview = movie.Overview, + RequestedUsers = IsAdmin ? movie.AllUsers.ToArray() : new string[] { }, + ReleaseYear = movie.ReleaseDate.Year.ToString(), + Available = movie.Available, + Admin = IsAdmin, + IssueId = movie.IssueId, + Qualities = qualities.ToArray() + }).ToList(); + + return Response.AsJson(viewModel); + } + + private async Task GetTvShows() + { + var settings = PrSettings.GetSettings(); + + var requests = await Service.GetAllAsync(); + requests = requests.Where(x => x.Type == RequestType.TvShow); + + var dbTv = requests; + + if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin) + { + dbTv = dbTv.Where(x => x.UserHasRequested(Username)).ToList(); + } + + IEnumerable qualities = new List(); + if (IsAdmin) + { + try + { + var sonarrSettings = SonarrSettings.GetSettings(); + if (sonarrSettings.Enabled) + { + var result = Cache.GetOrSetAsync(CacheKeys.SonarrQualityProfiles, async () => + { + return await Task.Run(() => SonarrApi.GetProfiles(sonarrSettings.ApiKey, sonarrSettings.FullUri)); + }); + qualities = result.Result.Select(x => new QualityModel() { Id = x.id.ToString(), Name = x.name }).ToList(); + } + else + { + var sickRageSettings = SickRageSettings.GetSettings(); + if (sickRageSettings.Enabled) + { + qualities = sickRageSettings.Qualities.Select(x => new QualityModel() { Id = x.Key, Name = x.Value }).ToList(); + } + } + } + catch (Exception e) + { + Log.Info(e); + } + + } + + var viewModel = dbTv.Select(tv => + { + return new RequestViewModel + { + ProviderId = tv.ProviderId, + Type = tv.Type, + Status = tv.Status, + ImdbId = tv.ImdbId, + Id = tv.Id, + PosterPath = tv.PosterPath, + ReleaseDate = tv.ReleaseDate, + ReleaseDateTicks = tv.ReleaseDate.Ticks, + RequestedDate = tv.RequestedDate, + RequestedDateTicks = DateTimeHelper.OffsetUTCDateTime(tv.RequestedDate, DateTimeOffset).Ticks, + Released = DateTime.Now > tv.ReleaseDate, + Approved = tv.Available || tv.Approved, + Title = tv.Title, + Overview = tv.Overview, + RequestedUsers = IsAdmin ? tv.AllUsers.ToArray() : new string[] { }, + ReleaseYear = tv.ReleaseDate.Year.ToString(), + Available = tv.Available, + Admin = IsAdmin, + IssueId = tv.IssueId, + TvSeriesRequestType = tv.SeasonsRequested, + Qualities = qualities.ToArray() + }; + }).ToList(); + + return Response.AsJson(viewModel); + } + + private async Task GetAlbumRequests() + { + var settings = PrSettings.GetSettings(); + var dbAlbum = await Service.GetAllAsync(); + dbAlbum = dbAlbum.Where(x => x.Type == RequestType.Album); + if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin) + { + dbAlbum = dbAlbum.Where(x => x.UserHasRequested(Username)); + } + + var viewModel = dbAlbum.Select(album => + { + return new RequestViewModel + { + ProviderId = album.ProviderId, + Type = album.Type, + Status = album.Status, + ImdbId = album.ImdbId, + Id = album.Id, + PosterPath = album.PosterPath, + ReleaseDate = album.ReleaseDate, + ReleaseDateTicks = album.ReleaseDate.Ticks, + RequestedDate = album.RequestedDate, + RequestedDateTicks = DateTimeHelper.OffsetUTCDateTime(album.RequestedDate, DateTimeOffset).Ticks, + Released = DateTime.Now > album.ReleaseDate, + Approved = album.Available || album.Approved, + Title = album.Title, + Overview = album.Overview, + RequestedUsers = IsAdmin ? album.AllUsers.ToArray() : new string[] { }, + ReleaseYear = album.ReleaseDate.Year.ToString(), + Available = album.Available, + Admin = IsAdmin, + IssueId = album.IssueId, + TvSeriesRequestType = album.SeasonsRequested, + MusicBrainzId = album.MusicBrainzId, + ArtistName = album.ArtistName + + }; + }).ToList(); + + return Response.AsJson(viewModel); + } + + private async Task DeleteRequest(int requestid) + { + this.RequiresClaims(UserClaims.Admin); + Analytics.TrackEventAsync(Category.Requests, Action.Delete, "Delete Request", Username, CookieHelper.GetAnalyticClientId(Cookies)); + + var currentEntity = await Service.GetAsync(requestid); + await Service.DeleteRequestAsync(currentEntity); + return Response.AsJson(new JsonResponseModel { Result = true }); + } + + /// + /// Reports the issue. + /// Comment can be null if the IssueState != Other + /// + /// The request identifier. + /// The issue. + /// The comment. + /// + private async Task ReportIssue(int requestId, IssueState issue, string comment) + { + var originalRequest = await Service.GetAsync(requestId); + if (originalRequest == null) + { + return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not add issue, please try again or contact the administrator!" }); + } + originalRequest.Issues = issue; + originalRequest.OtherMessage = !string.IsNullOrEmpty(comment) + ? $"{Username} - {comment}" + : string.Empty; + + + var result = await Service.UpdateRequestAsync(originalRequest); + + var model = new NotificationModel + { + User = Username, + NotificationType = NotificationType.Issue, + Title = originalRequest.Title, + DateTime = DateTime.Now, + Body = issue == IssueState.Other ? comment : issue.ToString().ToCamelCaseWords() + }; + await NotificationService.Publish(model); + + return Response.AsJson(result + ? new JsonResponseModel { Result = true } + : new JsonResponseModel { Result = false, Message = "Could not add issue, please try again or contact the administrator!" }); + } + + private async Task ClearIssue(int requestId) + { + this.RequiresClaims(UserClaims.Admin); + + var originalRequest = await Service.GetAsync(requestId); + if (originalRequest == null) + { + return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to clear it!" }); + } + originalRequest.Issues = IssueState.None; + originalRequest.OtherMessage = string.Empty; + + var result = await Service.UpdateRequestAsync(originalRequest); + return Response.AsJson(result + ? new JsonResponseModel { Result = true } + : new JsonResponseModel { Result = false, Message = "Could not clear issue, please try again or check the logs" }); + } + + private async Task ChangeRequestAvailability(int requestId, bool available) + { + this.RequiresClaims(UserClaims.Admin); + Analytics.TrackEventAsync(Category.Requests, Action.Update, available ? "Make request available" : "Make request unavailable", Username, CookieHelper.GetAnalyticClientId(Cookies)); + var originalRequest = await Service.GetAsync(requestId); + if (originalRequest == null) + { + return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to change the availability!" }); + } + + originalRequest.Available = available; + + var result = await Service.UpdateRequestAsync(originalRequest); + return Response.AsJson(result + ? new { Result = true, Available = available, Message = string.Empty } + : new { Result = false, Available = false, Message = "Could not update the availability, please try again or check the logs" }); + } + + + } +} diff --git a/PlexRequests.UI/Modules/SearchModule.cs b/PlexRequests.UI/Modules/SearchModule.cs index f02a417ea..79bbd9d7d 100644 --- a/PlexRequests.UI/Modules/SearchModule.cs +++ b/PlexRequests.UI/Modules/SearchModule.cs @@ -53,11 +53,14 @@ using Nancy.Responses; using PlexRequests.Api.Models.Tv; using PlexRequests.Core.Models; +using PlexRequests.Helpers.Analytics; using PlexRequests.Store.Models; using PlexRequests.Store.Repository; using TMDbLib.Objects.General; +using Action = PlexRequests.Helpers.Analytics.Action; + namespace PlexRequests.UI.Modules { public class SearchModule : BaseAuthModule @@ -69,7 +72,7 @@ namespace PlexRequests.UI.Modules INotificationService notify, IMusicBrainzApi mbApi, IHeadphonesApi hpApi, ISettingsService hpService, ICouchPotatoCacher cpCacher, ISonarrCacher sonarrCacher, ISickRageCacher sickRageCacher, IPlexApi plexApi, ISettingsService plexService, ISettingsService auth, IRepository u, ISettingsService email, - IIssueService issue) : base("search", prSettings) + IIssueService issue, IAnalytics a, IRepository rl) : base("search", prSettings) { Auth = auth; PlexService = plexService; @@ -95,6 +98,8 @@ namespace PlexRequests.UI.Modules UsersToNotifyRepo = u; EmailNotificationSettings = email; IssueService = issue; + Analytics = a; + RequestLimitRepo = rl; Get["/", true] = async (x, ct) => await RequestLoad(); @@ -140,31 +145,32 @@ namespace PlexRequests.UI.Modules private IHeadphonesApi HeadphonesApi { get; } private IRepository UsersToNotifyRepo { get; } private IIssueService IssueService { get; } + private IAnalytics Analytics { get; } + private IRepository RequestLimitRepo { get; } private static Logger Log = LogManager.GetCurrentClassLogger(); private async Task RequestLoad() { var settings = await PrService.GetSettingsAsync(); - Log.Trace("Loading Index"); return View["Search/Index", settings]; } private async Task UpcomingMovies() { - Log.Trace("Loading upcoming movies"); + Analytics.TrackEventAsync(Category.Search, Action.Movie, "Upcoming", Username, CookieHelper.GetAnalyticClientId(Cookies)); return await ProcessMovies(MovieSearchType.Upcoming, string.Empty); } private async Task CurrentlyPlayingMovies() { - Log.Trace("Loading currently playing movies"); + Analytics.TrackEventAsync(Category.Search, Action.Movie, "CurrentlyPlaying", Username, CookieHelper.GetAnalyticClientId(Cookies)); return await ProcessMovies(MovieSearchType.CurrentlyPlaying, string.Empty); } private async Task SearchMovie(string searchTerm) { - Log.Trace("Searching for Movie {0}", searchTerm); + Analytics.TrackEventAsync(Category.Search, Action.Movie, searchTerm, Username, CookieHelper.GetAnalyticClientId(Cookies)); return await ProcessMovies(MovieSearchType.Search, searchTerm); } @@ -279,8 +285,8 @@ namespace PlexRequests.UI.Modules private async Task SearchTvShow(string searchTerm) { + Analytics.TrackEventAsync(Category.Search, Action.TvShow, searchTerm, Username, CookieHelper.GetAnalyticClientId(Cookies)); var plexSettings = await PlexService.GetSettingsAsync(); - Log.Trace("Searching for TV Show {0}", searchTerm); var apiTv = new List(); await Task.Factory.StartNew(() => new TvMazeApi().Search(searchTerm)).ContinueWith((t) => @@ -295,7 +301,6 @@ namespace PlexRequests.UI.Modules if (!apiTv.Any()) { - Log.Trace("TV Show data is null"); return Response.AsJson(""); } @@ -366,6 +371,7 @@ namespace PlexRequests.UI.Modules private async Task SearchMusic(string searchTerm) { + Analytics.TrackEventAsync(Category.Search, Action.Album, searchTerm, Username, CookieHelper.GetAnalyticClientId(Cookies)); var apiAlbums = new List(); await Task.Run(() => MusicBrainzApi.SearchAlbum(searchTerm)).ContinueWith((t) => { @@ -417,14 +423,16 @@ namespace PlexRequests.UI.Modules private async Task RequestMovie(int movieId) { - var movieInfo = MovieApi.GetMovieInformation(movieId).Result; - var fullMovieName = $"{movieInfo.Title}{(movieInfo.ReleaseDate.HasValue ? $" ({movieInfo.ReleaseDate.Value.Year})" : string.Empty)}"; - Log.Trace("Getting movie info from TheMovieDb"); - var settings = await PrService.GetSettingsAsync(); + if (!await CheckRequestLimit(settings, RequestType.Movie)) + { + return Response.AsJson(new JsonResponseModel { Result = false, Message = "You have reached your weekly request limit for Movies! Please contact your admin." }); + } - // check if the movie has already been requested - Log.Info("Requesting movie with id {0}", movieId); + Analytics.TrackEventAsync(Category.Search, Action.Request, "Movie", Username, CookieHelper.GetAnalyticClientId(Cookies)); + var movieInfo = MovieApi.GetMovieInformation(movieId).Result; + var fullMovieName = $"{movieInfo.Title}{(movieInfo.ReleaseDate.HasValue ? $" ({movieInfo.ReleaseDate.Value.Year})" : string.Empty)}"; + var existingRequest = await RequestService.CheckRequestAsync(movieId); if (existingRequest != null) { @@ -435,11 +443,9 @@ namespace PlexRequests.UI.Modules await RequestService.UpdateRequestAsync(existingRequest); } - return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{fullMovieName} was successfully added!" : $"{fullMovieName} has already been requested!" }); + return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}" : $"{fullMovieName} {Resources.UI.Search_AlreadyRequested}" }); } - - Log.Debug("movie with id {0} doesnt exists", movieId); - + try { var movies = Checker.GetPlexMovies(); @@ -451,7 +457,7 @@ namespace PlexRequests.UI.Modules catch (Exception e) { Log.Error(e); - return Response.AsJson(new JsonResponseModel { Result = false, Message = $"We could not check if {fullMovieName} is in Plex, are you sure it's correctly setup?" }); + return Response.AsJson(new JsonResponseModel { Result = false, Message = string.Format(Resources.UI.Search_CouldNotCheckPlex, fullMovieName) }); } //#endif @@ -484,70 +490,28 @@ namespace PlexRequests.UI.Modules Log.Debug("Adding movie to CP result {0}", result); if (result) { - model.Approved = true; - Log.Info("Adding movie to database (No approval required)"); - await RequestService.AddRequestAsync(model); - - if (ShouldSendNotification(RequestType.Movie, settings)) - { - var notificationModel = new NotificationModel - { - Title = model.Title, - User = Username, - DateTime = DateTime.Now, - NotificationType = NotificationType.NewRequest, - RequestType = RequestType.Movie - }; - await NotificationService.Publish(notificationModel); - } - return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullMovieName} was successfully added!" }); + return await AddRequest(model, settings, $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}"); } - return - Response.AsJson(new JsonResponseModel - { - Result = false, - Message = - "Something went wrong adding the movie to CouchPotato! Please check your settings." - }); - } - else - { - model.Approved = true; - Log.Info("Adding movie to database (No approval required)"); - await RequestService.AddRequestAsync(model); - if (ShouldSendNotification(RequestType.Movie, settings)) + return Response.AsJson(new JsonResponseModel { - var notificationModel = new NotificationModel - { - Title = model.Title, - User = Username, - DateTime = DateTime.Now, - NotificationType = NotificationType.NewRequest, - RequestType = RequestType.Movie - }; - await NotificationService.Publish(notificationModel); - } - - return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullMovieName} was successfully added!" }); + Result = false, + Message = Resources.UI.Search_CouchPotatoError + }); } + + return await AddRequest(model, settings, $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}"); } try { - Log.Info("Adding movie to database"); - var id = await RequestService.AddRequestAsync(model); - - var notificationModel = new NotificationModel { Title = model.Title, User = Username, DateTime = DateTime.Now, NotificationType = NotificationType.NewRequest, RequestType = RequestType.Movie }; - await NotificationService.Publish(notificationModel); - - return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullMovieName} was successfully added!" }); + return await AddRequest(model, settings, $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}"); } catch (Exception e) { Log.Fatal(e); - return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something went wrong adding the movie to CouchPotato! Please check your settings." }); + return Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_CouchPotatoError }); } } @@ -559,6 +523,12 @@ namespace PlexRequests.UI.Modules /// private async Task RequestTvShow(int showId, string seasons) { + var settings = await PrService.GetSettingsAsync(); + if (!await CheckRequestLimit(settings, RequestType.TvShow)) + { + return Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_WeeklyRequestLimitTVShow }); + } + Analytics.TrackEventAsync(Category.Search, Action.Request, "TvShow", Username, CookieHelper.GetAnalyticClientId(Cookies)); var tvApi = new TvMazeApi(); var showInfo = tvApi.ShowLookupByTheTvDbId(showId); @@ -567,10 +537,8 @@ namespace PlexRequests.UI.Modules string fullShowName = $"{showInfo.name} ({firstAir.Year})"; //#if !DEBUG - var settings = await PrService.GetSettingsAsync(); // check if the show has already been requested - Log.Info("Requesting tv show with id {0}", showId); var existingRequest = await RequestService.CheckRequestAsync(showId); if (existingRequest != null) { @@ -580,7 +548,7 @@ namespace PlexRequests.UI.Modules existingRequest.RequestedUsers.Add(Username); await RequestService.UpdateRequestAsync(existingRequest); } - return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{fullShowName} was successfully added!" : $"{fullShowName} has already been requested!" }); + return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}" : $"{fullShowName} {Resources.UI.Search_AlreadyRequested}" }); } try @@ -594,12 +562,12 @@ namespace PlexRequests.UI.Modules } if (Checker.IsTvShowAvailable(shows.ToArray(), showInfo.name, showInfo.premiered?.Substring(0, 4), providerId)) { - return Response.AsJson(new JsonResponseModel { Result = false, Message = $"{fullShowName} is already in Plex!" }); + return Response.AsJson(new JsonResponseModel { Result = false, Message = $"{fullShowName} {Resources.UI.Search_AlreadyInPlex}" }); } } catch (Exception) { - return Response.AsJson(new JsonResponseModel { Result = false, Message = $"We could not check if {fullShowName} is in Plex, are you sure it's correctly setup?" }); + return Response.AsJson(new JsonResponseModel { Result = false, Message = string.Format(Resources.UI.Search_CouldNotCheckPlex, fullShowName) }); } //#endif @@ -661,28 +629,10 @@ namespace PlexRequests.UI.Modules var result = sender.SendToSonarr(sonarrSettings, model); if (!string.IsNullOrEmpty(result?.title)) { - model.Approved = true; - Log.Debug("Adding tv to database requests (No approval required & Sonarr)"); - await RequestService.AddRequestAsync(model); - - if (ShouldSendNotification(RequestType.TvShow, settings)) - { - var notify1 = new NotificationModel - { - Title = model.Title, - User = Username, - DateTime = DateTime.Now, - NotificationType = NotificationType.NewRequest, - RequestType = RequestType.TvShow - }; - await NotificationService.Publish(notify1); - } - return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" }); + return await AddRequest(model, settings, $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}"); } - return Response.AsJson(ValidationHelper.SendSonarrError(result?.ErrorMessages)); - } var srSettings = SickRageService.GetSettings(); @@ -691,57 +641,20 @@ namespace PlexRequests.UI.Modules var result = sender.SendToSickRage(srSettings, model); if (result?.result == "success") { - model.Approved = true; - Log.Debug("Adding tv to database requests (No approval required & SickRage)"); - await RequestService.AddRequestAsync(model); - if (ShouldSendNotification(RequestType.TvShow, settings)) - { - var notify2 = new NotificationModel - { - Title = model.Title, - User = Username, - DateTime = DateTime.Now, - NotificationType = NotificationType.NewRequest, - RequestType = RequestType.TvShow - }; - await NotificationService.Publish(notify2); - } - - return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" }); + return await AddRequest(model, settings, $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}"); } - return Response.AsJson(new JsonResponseModel { Result = false, Message = result?.message != null ? "Message From SickRage: " + result.message : "Something went wrong adding the movie to SickRage! Please check your settings." }); + return Response.AsJson(new JsonResponseModel { Result = false, Message = result?.message ?? Resources.UI.Search_SickrageError }); } if (!srSettings.Enabled && !sonarrSettings.Enabled) { - model.Approved = true; - Log.Debug("Adding tv to database requests (No approval required) and Sonarr/Sickrage not setup"); - await RequestService.AddRequestAsync(model); - if (ShouldSendNotification(RequestType.TvShow, settings)) - { - var notify2 = new NotificationModel - { - Title = model.Title, - User = Username, - DateTime = DateTime.Now, - NotificationType = NotificationType.NewRequest, - RequestType = RequestType.TvShow - }; - await NotificationService.Publish(notify2); - } - return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" }); + return await AddRequest(model, settings, $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}"); } - return Response.AsJson(new JsonResponseModel { Result = false, Message = "The request of TV Shows is not correctly set up. Please contact your admin." }); + return Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_TvNotSetUp }); } - - await RequestService.AddRequestAsync(model); - - var notificationModel = new NotificationModel { Title = model.Title, User = Username, DateTime = DateTime.Now, NotificationType = NotificationType.NewRequest, RequestType = RequestType.TvShow }; - await NotificationService.Publish(notificationModel); - - return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" }); + return await AddRequest(model, settings, $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}"); } private bool ShouldSendNotification(RequestType type, PlexRequestSettings prSettings) @@ -763,24 +676,23 @@ namespace PlexRequests.UI.Modules private async Task RequestAlbum(string releaseId) { var settings = await PrService.GetSettingsAsync(); + if (!await CheckRequestLimit(settings, RequestType.Album)) + { + return Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_WeeklyRequestLimitAlbums }); + } + Analytics.TrackEventAsync(Category.Search, Action.Request, "Album", Username, CookieHelper.GetAnalyticClientId(Cookies)); var existingRequest = await RequestService.CheckRequestAsync(releaseId); - Log.Debug("Checking for an existing request"); - + if (existingRequest != null) { - Log.Debug("We do have an existing album request"); if (!existingRequest.UserHasRequested(Username)) { - - Log.Debug("Not in the requested list so adding them and updating the request. User: {0}", Username); existingRequest.RequestedUsers.Add(Username); await RequestService.UpdateRequestAsync(existingRequest); } - return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{existingRequest.Title} was successfully added!" : $"{existingRequest.Title} has already been requested!" }); + return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{existingRequest.Title} {Resources.UI.Search_SuccessfullyAdded}" : $"{existingRequest.Title} {Resources.UI.Search_AlreadyRequested}" }); } - - Log.Debug("This is a new request"); - + var albumInfo = MusicBrainzApi.GetAlbum(releaseId); DateTime release; DateTimeHelper.CustomParse(albumInfo.ReleaseEvents?.FirstOrDefault()?.date, out release); @@ -788,7 +700,7 @@ namespace PlexRequests.UI.Modules var artist = albumInfo.ArtistCredits?.FirstOrDefault()?.artist; if (artist == null) { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "We could not find the artist on MusicBrainz. Please try again later or contact your admin" }); + return Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_MusicBrainzError }); } var albums = Checker.GetPlexAlbums(); @@ -799,7 +711,7 @@ namespace PlexRequests.UI.Modules return Response.AsJson(new JsonResponseModel { Result = false, - Message = $"{albumInfo.title} is already in Plex!" + Message = $"{albumInfo.title} {Resources.UI.Search_AlreadyInPlex}" }); } @@ -824,7 +736,6 @@ namespace PlexRequests.UI.Modules if (ShouldAutoApprove(RequestType.Album, settings)) { - Log.Debug("We don't require approval OR the user is in the whitelist"); var hpSettings = HeadphonesService.GetSettings(); if (!hpSettings.Enabled) @@ -834,54 +745,16 @@ namespace PlexRequests.UI.Modules Response.AsJson(new JsonResponseModel { Result = true, - Message = $"{model.Title} was successfully added!" + Message = $"{model.Title} {Resources.UI.Search_SuccessfullyAdded}" }); } var sender = new HeadphonesSender(HeadphonesApi, hpSettings, RequestService); await sender.AddAlbum(model); - model.Approved = true; - await RequestService.AddRequestAsync(model); - - if (ShouldSendNotification(RequestType.Album, settings)) - { - var notify2 = new NotificationModel - { - Title = model.Title, - User = Username, - DateTime = DateTime.Now, - NotificationType = NotificationType.NewRequest, - RequestType = RequestType.Album - }; - await NotificationService.Publish(notify2); - } - - return - Response.AsJson(new JsonResponseModel - { - Result = true, - Message = $"{model.Title} was successfully added!" - }); + return await AddRequest(model, settings, $"{model.Title} {Resources.UI.Search_SuccessfullyAdded}"); } - if (ShouldSendNotification(RequestType.Album, settings)) - { - var notify2 = new NotificationModel - { - Title = model.Title, - User = Username, - DateTime = DateTime.Now, - NotificationType = NotificationType.NewRequest, - RequestType = RequestType.Album - }; - await NotificationService.Publish(notify2); - } - await RequestService.AddRequestAsync(model); - return Response.AsJson(new JsonResponseModel - { - Result = true, - Message = $"{model.Title} was successfully added!" - }); + return await AddRequest(model, settings, $"{model.Title} {Resources.UI.Search_SuccessfullyAdded}"); } private string GetMusicBrainzCoverArt(string id) @@ -919,17 +792,18 @@ namespace PlexRequests.UI.Modules private async Task NotifyUser(bool notify) { + Analytics.TrackEventAsync(Category.Search, Action.Save, "NotifyUser", Username, CookieHelper.GetAnalyticClientId(Cookies), notify ? 1 : 0); var authSettings = await Auth.GetSettingsAsync(); var auth = authSettings.UserAuthentication; var emailSettings = await EmailNotificationSettings.GetSettingsAsync(); var email = emailSettings.EnableUserEmailNotifications; if (!auth) { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "Sorry, but this functionality is currently only for users with Plex accounts" }); + return Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_ErrorPlexAccountOnly }); } if (!email) { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "Sorry, but your administrator has not yet enabled this functionality." }); + return Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_ErrorNotEnabled }); } var username = Username; var originalList = await UsersToNotifyRepo.GetAllAsync(); @@ -937,7 +811,7 @@ namespace PlexRequests.UI.Modules { if (originalList == null) { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "We could not remove this notification because you never had it!" }); + return Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_NotificationError }); } var userToRemove = originalList.FirstOrDefault(x => x.Username == username); if (userToRemove != null) @@ -952,7 +826,7 @@ namespace PlexRequests.UI.Modules { var userModel = new UsersToNotify { Username = username }; var insertResult = await UsersToNotifyRepo.InsertAsync(userModel); - return Response.AsJson(insertResult != -1 ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = "Could not save, please try again" }); + return Response.AsJson(insertResult != -1 ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = Resources.UI.Common_CouldNotSave }); } var existingUser = originalList.FirstOrDefault(x => x.Username == username); @@ -964,7 +838,7 @@ namespace PlexRequests.UI.Modules { var userModel = new UsersToNotify { Username = username }; var insertResult = await UsersToNotifyRepo.InsertAsync(userModel); - return Response.AsJson(insertResult != -1 ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = "Could not save, please try again" }); + return Response.AsJson(insertResult != -1 ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = Resources.UI.Common_CouldNotSave }); } } @@ -985,6 +859,89 @@ namespace PlexRequests.UI.Modules return Response.AsJson(model); } + private async Task CheckRequestLimit(PlexRequestSettings s, RequestType type) + { + if (IsAdmin) + return true; + + if (s.ApprovalWhiteList.Contains(Username)) + return true; + + var requestLimit = GetRequestLimitForType(type, s); + if (requestLimit == 0) + { + return true; + } + + var limit = await RequestLimitRepo.GetAllAsync(); + var usersLimit = limit.FirstOrDefault(x => x.Username == Username && x.RequestType == type); + if (usersLimit == null) + { + // Have not set a requestLimit yet + return true; + } + + return usersLimit.RequestCount >= requestLimit; + } + + private int GetRequestLimitForType(RequestType type, PlexRequestSettings s) + { + int requestLimit; + switch (type) + { + case RequestType.Movie: + requestLimit = s.MovieWeeklyRequestLimit; + break; + case RequestType.TvShow: + requestLimit = s.TvWeeklyRequestLimit; + break; + case RequestType.Album: + requestLimit = s.AlbumWeeklyRequestLimit; + break; + default: + throw new ArgumentOutOfRangeException(nameof(type), type, null); + } + return requestLimit; + } + + private async Task AddRequest(RequestedModel model, PlexRequestSettings settings, string message) + { + + model.Approved = true; + await RequestService.AddRequestAsync(model); + + if (ShouldSendNotification(RequestType.Movie, settings)) + { + var notificationModel = new NotificationModel + { + Title = model.Title, + User = Username, + DateTime = DateTime.Now, + NotificationType = NotificationType.NewRequest, + RequestType = RequestType.Movie + }; + await NotificationService.Publish(notificationModel); + } + + var limit = await RequestLimitRepo.GetAllAsync(); + var usersLimit = limit.FirstOrDefault(x => x.Username == Username && x.RequestType == model.Type); + if (usersLimit == null) + { + await RequestLimitRepo.InsertAsync(new RequestLimit + { + Username = Username, + RequestType = model.Type, + FirstRequestDate = DateTime.UtcNow, + RequestCount = 1 + }); + } + else + { + usersLimit.RequestCount++; + await RequestLimitRepo.UpdateAsync(usersLimit); + } + return Response.AsJson(new JsonResponseModel { Result = true, Message = message }); + } } } diff --git a/PlexRequests.UI/Modules/UserLoginModule.cs b/PlexRequests.UI/Modules/UserLoginModule.cs index 2cb97ff11..fb0d5400d 100644 --- a/PlexRequests.UI/Modules/UserLoginModule.cs +++ b/PlexRequests.UI/Modules/UserLoginModule.cs @@ -80,13 +80,14 @@ namespace PlexRequests.UI.Modules if (landingSettings.BeforeLogin) { - await - Analytics.TrackEventAsync( - Category.LandingPage, - Action.View, - "Going To LandingPage before login", - Username, - CookieHelper.GetAnalyticClientId(Cookies)); +#pragma warning disable 4014 + Analytics.TrackEventAsync( +#pragma warning restore 4014 + Category.LandingPage, + Action.View, + "Going To LandingPage before login", + Username, + CookieHelper.GetAnalyticClientId(Cookies)); var model = new LandingPageViewModel { diff --git a/PlexRequests.UI/PlexRequests.UI.csproj b/PlexRequests.UI/PlexRequests.UI.csproj index 99b5f8641..0495bb18b 100644 --- a/PlexRequests.UI/PlexRequests.UI.csproj +++ b/PlexRequests.UI/PlexRequests.UI.csproj @@ -1,656 +1,677 @@ - - - - - Debug - AnyCPU - {68F5F5F3-B8BB-4911-875F-6F00AAE04EA6} - Exe - Properties - PlexRequests.UI - PlexRequests - v4.5 - 512 - ..\..\ - true - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - true - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Nancy.1.4.3\lib\net40\Nancy.dll - True - - - ..\packages\Nancy.Metadata.Modules.1.4.1\lib\net40\Nancy.Metadata.Modules.dll - True - - - ..\packages\Nancy.Swagger.0.1.0-alpha3\lib\net40\Nancy.Swagger.dll - True - - - ..\packages\NLog.4.3.4\lib\net45\NLog.dll - True - - - ..\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll - True - - - ..\packages\Swagger.ObjectModel.0.1.0-alpha3\lib\net40\Swagger.ObjectModel.dll - True - - - - - - - - - - - - ..\packages\CommandLineParser.2.0.275-beta\lib\net45\CommandLine.dll - - - ..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll - - - ..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll - - - ..\packages\Dapper.1.42\lib\net45\Dapper.dll - - - ..\packages\FluentValidation.6.2.1.0\lib\Net45\FluentValidation.dll - - - ..\packages\MarkdownSharp.1.13.0.0\lib\35\MarkdownSharp.dll - - - ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll - - - ..\packages\Microsoft.Owin.Host.HttpListener.3.0.1\lib\net45\Microsoft.Owin.Host.HttpListener.dll - - - ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll - - - ..\packages\Microsoft.Owin.Hosting.3.0.1\lib\net45\Microsoft.Owin.Hosting.dll - - - ..\Assemblies\Mono.Data.Sqlite.dll - - - ..\packages\Mono.Posix.4.0.0.0\lib\net40\Mono.Posix.dll - - - ..\packages\Nancy.Authentication.Basic.1.4.1\lib\net40\Nancy.Authentication.Basic.dll - - - ..\packages\Nancy.Authentication.Forms.1.4.1\lib\net40\Nancy.Authentication.Forms.dll - - - ..\packages\Nancy.Hosting.Self.1.4.1\lib\net40\Nancy.Hosting.Self.dll - - - ..\packages\Nancy.Owin.1.4.1\lib\net40\Nancy.Owin.dll - - - ..\packages\Nancy.Validation.FluentValidation.1.4.1\lib\net40\Nancy.Validation.FluentValidation.dll - - - ..\packages\Nancy.Viewengines.Razor.1.4.1\lib\net40\Nancy.ViewEngines.Razor.dll - - - ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll - - - ..\packages\Owin.1.0\lib\net40\Owin.dll - - - ..\packages\Quartz.2.3.3\lib\net40\Quartz.dll - - - ..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll - - - ..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - base.scss - PreserveNewest - - - base.css - PreserveNewest - - - PreserveNewest - - - - - - - - - - - - - - - - - - - - - - datepicker.scss - - - datepicker.css - Always - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - original.scss - PreserveNewest - - - original.css - PreserveNewest - - - plex.scss - PreserveNewest - - - plex.css - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - - - PreserveNewest - - - PreserveNewest - - - Always - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - Always - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - Always - - - moment.min.js - - - moment.min.es5.js - - - Always - - - pace.scss - - - pace.css - Always - - - PreserveNewest - - - PreserveNewest - - - - - Always - - - - compilerconfig.json - - - PreserveNewest - - - - - - - Always - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - PreserveNewest - - - Designer - - - Designer - - - Always - - - Designer - - - Always - - - Always - - - Always - - - Always - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - web.config - - - web.config - - - - - - {95834072-A675-415D-AA8F-877C91623810} - PlexRequests.Api.Interfaces - - - {CB37A5F8-6DFC-4554-99D3-A42B502E4591} - PlexRequests.Api.Models - - - {8CB8D235-2674-442D-9C6A-35FCAEEB160D} - PlexRequests.Api - - - {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} - PlexRequests.Core - - - {1252336D-42A3-482A-804C-836E60173DFA} - PlexRequests.Helpers - - - {566EFA49-68F8-4716-9693-A6B3F2624DEA} - PlexRequests.Services - - - {92433867-2B7B-477B-A566-96C382427525} - PlexRequests.Store - - - {ebe6fc1c-7b4b-47e9-af54-0ee0604a2be5} - PlexRequests.Updater - - - - - PreserveNewest - - - - - False - Microsoft .NET Framework 4.5.2 %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 - false - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - - - - - - - - - - + + + + + Debug + AnyCPU + {68F5F5F3-B8BB-4911-875F-6F00AAE04EA6} + Exe + Properties + PlexRequests.UI + PlexRequests + v4.5 + 512 + ..\..\ + true + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Nancy.1.4.3\lib\net40\Nancy.dll + True + + + ..\packages\Nancy.Metadata.Modules.1.4.1\lib\net40\Nancy.Metadata.Modules.dll + True + + + ..\packages\Nancy.Swagger.0.1.0-alpha3\lib\net40\Nancy.Swagger.dll + True + + + ..\packages\NLog.4.3.4\lib\net45\NLog.dll + True + + + ..\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll + True + + + ..\packages\Swagger.ObjectModel.0.1.0-alpha3\lib\net40\Swagger.ObjectModel.dll + True + + + + + + + + + + + + ..\packages\CommandLineParser.2.0.275-beta\lib\net45\CommandLine.dll + + + ..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll + + + ..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll + + + ..\packages\Dapper.1.42\lib\net45\Dapper.dll + + + ..\packages\FluentValidation.6.2.1.0\lib\Net45\FluentValidation.dll + + + ..\packages\MarkdownSharp.1.13.0.0\lib\35\MarkdownSharp.dll + + + ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll + + + ..\packages\Microsoft.Owin.Host.HttpListener.3.0.1\lib\net45\Microsoft.Owin.Host.HttpListener.dll + + + ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll + + + ..\packages\Microsoft.Owin.Hosting.3.0.1\lib\net45\Microsoft.Owin.Hosting.dll + + + ..\Assemblies\Mono.Data.Sqlite.dll + + + ..\packages\Mono.Posix.4.0.0.0\lib\net40\Mono.Posix.dll + + + ..\packages\Nancy.Authentication.Basic.1.4.1\lib\net40\Nancy.Authentication.Basic.dll + + + ..\packages\Nancy.Authentication.Forms.1.4.1\lib\net40\Nancy.Authentication.Forms.dll + + + ..\packages\Nancy.Hosting.Self.1.4.1\lib\net40\Nancy.Hosting.Self.dll + + + ..\packages\Nancy.Owin.1.4.1\lib\net40\Nancy.Owin.dll + + + ..\packages\Nancy.Validation.FluentValidation.1.4.1\lib\net40\Nancy.Validation.FluentValidation.dll + + + ..\packages\Nancy.Viewengines.Razor.1.4.1\lib\net40\Nancy.ViewEngines.Razor.dll + + + ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll + + + ..\packages\Owin.1.0\lib\net40\Owin.dll + + + ..\packages\Quartz.2.3.3\lib\net40\Quartz.dll + + + ..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll + + + ..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + UI.resx + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + base.scss + PreserveNewest + + + base.css + PreserveNewest + + + PreserveNewest + + + + + + + + + + + + + + + + + + + + + + datepicker.scss + + + datepicker.css + Always + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + original.scss + PreserveNewest + + + original.css + PreserveNewest + + + plex.scss + PreserveNewest + + + plex.css + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + PreserveNewest + + + PreserveNewest + + + Always + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + Always + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + Always + + + moment.min.js + + + moment.min.es5.js + + + Always + + + pace.scss + + + pace.css + Always + + + PreserveNewest + + + PreserveNewest + + + + + Always + + + + compilerconfig.json + + + PreserveNewest + + + + + + + Always + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + PreserveNewest + + + Designer + + + Designer + + + Always + + + Designer + + + Always + + + Always + + + Always + + + Always + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + web.config + + + web.config + + + + + + + + + + + + PublicResXFileCodeGenerator + UI.Designer.cs + + + + + + {95834072-A675-415D-AA8F-877C91623810} + PlexRequests.Api.Interfaces + + + {CB37A5F8-6DFC-4554-99D3-A42B502E4591} + PlexRequests.Api.Models + + + {8CB8D235-2674-442D-9C6A-35FCAEEB160D} + PlexRequests.Api + + + {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} + PlexRequests.Core + + + {1252336D-42A3-482A-804C-836E60173DFA} + PlexRequests.Helpers + + + {566EFA49-68F8-4716-9693-A6B3F2624DEA} + PlexRequests.Services + + + {92433867-2B7B-477B-A566-96C382427525} + PlexRequests.Store + + + {ebe6fc1c-7b4b-47e9-af54-0ee0604a2be5} + PlexRequests.Updater + + + + + PreserveNewest + + + + + False + Microsoft .NET Framework 4.5.2 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.Designer.cs b/PlexRequests.UI/Resources/UI.Designer.cs new file mode 100644 index 000000000..14e5aa6ae --- /dev/null +++ b/PlexRequests.UI/Resources/UI.Designer.cs @@ -0,0 +1,1054 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace PlexRequests.UI.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class UI { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal UI() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PlexRequests.UI.Resources.UI", typeof(UI).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Approve. + /// + public static string Common_Approve { + get { + return ResourceManager.GetString("Common_Approve", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Approved. + /// + public static string Common_Approved { + get { + return ResourceManager.GetString("Common_Approved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Close. + /// + public static string Common_Close { + get { + return ResourceManager.GetString("Common_Close", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not save, please try again. + /// + public static string Common_CouldNotSave { + get { + return ResourceManager.GetString("Common_CouldNotSave", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remove. + /// + public static string Common_Remove { + get { + return ResourceManager.GetString("Common_Remove", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string Common_Save { + get { + return ResourceManager.GetString("Common_Save", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Issue. + /// + public static string Issues_Issue { + get { + return ResourceManager.GetString("Issues_Issue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save Changes. + /// + public static string Issues_Modal_Save { + get { + return ResourceManager.GetString("Issues_Modal_Save", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add an issue. + /// + public static string Issues_Modal_Title { + get { + return ResourceManager.GetString("Issues_Modal_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No Subtitles. + /// + public static string Issues_NoSubs { + get { + return ResourceManager.GetString("Issues_NoSubs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Other. + /// + public static string Issues_Other { + get { + return ResourceManager.GetString("Issues_Other", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Playback Issues. + /// + public static string Issues_Playback { + get { + return ResourceManager.GetString("Issues_Playback", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Wrong Audio. + /// + public static string Issues_WrongAudio { + get { + return ResourceManager.GetString("Issues_WrongAudio", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Wrong Content. + /// + public static string Issues_WrongContent { + get { + return ResourceManager.GetString("Issues_WrongContent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Something went wrong!. + /// + public static string Javascript_SomethingWentWrong { + get { + return ResourceManager.GetString("Javascript_SomethingWentWrong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Success!. + /// + public static string Javascript_Success { + get { + return ResourceManager.GetString("Javascript_Success", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Admin. + /// + public static string Layout_Admin { + get { + return ResourceManager.GetString("Layout_Admin", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change Password. + /// + public static string Layout_ChangePassword { + get { + return ResourceManager.GetString("Layout_ChangePassword", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Danish. + /// + public static string Layout_Danish { + get { + return ResourceManager.GetString("Layout_Danish", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Donate. + /// + public static string Layout_Donate { + get { + return ResourceManager.GetString("Layout_Donate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dutch. + /// + public static string Layout_Dutch { + get { + return ResourceManager.GetString("Layout_Dutch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to English. + /// + public static string Layout_English { + get { + return ResourceManager.GetString("Layout_English", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to German. + /// + public static string Layout_German { + get { + return ResourceManager.GetString("Layout_German", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Issues. + /// + public static string Layout_Issues { + get { + return ResourceManager.GetString("Layout_Issues", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Italian. + /// + public static string Layout_Italian { + get { + return ResourceManager.GetString("Layout_Italian", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logout. + /// + public static string Layout_Logout { + get { + return ResourceManager.GetString("Layout_Logout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Portuguese. + /// + public static string Layout_Portuguese { + get { + return ResourceManager.GetString("Layout_Portuguese", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Requests. + /// + public static string Layout_Requests { + get { + return ResourceManager.GetString("Layout_Requests", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Search. + /// + public static string Layout_Search { + get { + return ResourceManager.GetString("Layout_Search", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Settings. + /// + public static string Layout_Settings { + get { + return ResourceManager.GetString("Layout_Settings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Spanish. + /// + public static string Layout_Spanish { + get { + return ResourceManager.GetString("Layout_Spanish", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Swedish. + /// + public static string Layout_Swedish { + get { + return ResourceManager.GetString("Layout_Swedish", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Plex Requests. + /// + public static string Layout_Title { + get { + return ResourceManager.GetString("Layout_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There is a new update available! Click. + /// + public static string Layout_UpdateAvailablePart1 { + get { + return ResourceManager.GetString("Layout_UpdateAvailablePart1", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Here!. + /// + public static string Layout_UpdateAvailablePart2 { + get { + return ResourceManager.GetString("Layout_UpdateAvailablePart2", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Welcome. + /// + public static string Layout_Welcome { + get { + return ResourceManager.GetString("Layout_Welcome", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Albums. + /// + public static string Requests_AlbumsTabTitle { + get { + return ResourceManager.GetString("Requests_AlbumsTabTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Approve Movies. + /// + public static string Requests_ApproveMovies { + get { + return ResourceManager.GetString("Requests_ApproveMovies", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Approve Music. + /// + public static string Requests_ApproveMusic { + get { + return ResourceManager.GetString("Requests_ApproveMusic", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Approve TV Shows. + /// + public static string Requests_ApproveTvShows { + get { + return ResourceManager.GetString("Requests_ApproveTvShows", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Available. + /// + public static string Requests_Available { + get { + return ResourceManager.GetString("Requests_Available", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete Movies. + /// + public static string Requests_DeleteMovies { + get { + return ResourceManager.GetString("Requests_DeleteMovies", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete Music. + /// + public static string Requests_DeleteMusic { + get { + return ResourceManager.GetString("Requests_DeleteMusic", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete TV Shows. + /// + public static string Requests_DeleteTVShows { + get { + return ResourceManager.GetString("Requests_DeleteTVShows", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Filter. + /// + public static string Requests_Filter { + get { + return ResourceManager.GetString("Requests_Filter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All. + /// + public static string Requests_Filter_All { + get { + return ResourceManager.GetString("Requests_Filter_All", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Approved. + /// + public static string Requests_Filter_Approved { + get { + return ResourceManager.GetString("Requests_Filter_Approved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Available. + /// + public static string Requests_Filter_Available { + get { + return ResourceManager.GetString("Requests_Filter_Available", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Not Approved. + /// + public static string Requests_Filter_NotApproved { + get { + return ResourceManager.GetString("Requests_Filter_NotApproved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Not Available. + /// + public static string Requests_Filter_NotAvailable { + get { + return ResourceManager.GetString("Requests_Filter_NotAvailable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Not Released. + /// + public static string Requests_Filter_NotReleased { + get { + return ResourceManager.GetString("Requests_Filter_NotReleased", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Released. + /// + public static string Requests_Filter_Released { + get { + return ResourceManager.GetString("Requests_Filter_Released", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mark Available. + /// + public static string Requests_MarkAvailable { + get { + return ResourceManager.GetString("Requests_MarkAvailable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mark Unavailable. + /// + public static string Requests_MarkUnavailable { + get { + return ResourceManager.GetString("Requests_MarkUnavailable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Movies. + /// + public static string Requests_MoviesTabTitle { + get { + return ResourceManager.GetString("Requests_MoviesTabTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Order. + /// + public static string Requests_Order { + get { + return ResourceManager.GetString("Requests_Order", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Latest Releases. + /// + public static string Requests_Order_LatestReleases { + get { + return ResourceManager.GetString("Requests_Order_LatestReleases", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Latest Requests. + /// + public static string Requests_Order_LatestRequests { + get { + return ResourceManager.GetString("Requests_Order_LatestRequests", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Oldest Releases. + /// + public static string Requests_Order_OldestReleases { + get { + return ResourceManager.GetString("Requests_Order_OldestReleases", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Oldest Requests. + /// + public static string Requests_Order_OldestRequests { + get { + return ResourceManager.GetString("Requests_Order_OldestRequests", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Below you can see yours and all other requests, as well as their download and approval status.. + /// + public static string Requests_Paragraph { + get { + return ResourceManager.GetString("Requests_Paragraph", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Release Date. + /// + public static string Requests_ReleaseDate { + get { + return ResourceManager.GetString("Requests_ReleaseDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Requested By. + /// + public static string Requests_RequestedBy { + get { + return ResourceManager.GetString("Requests_RequestedBy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Requested Date. + /// + public static string Requests_RequestedDate { + get { + return ResourceManager.GetString("Requests_RequestedDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Seasons Requested. + /// + public static string Requests_SeasonsRequested { + get { + return ResourceManager.GetString("Requests_SeasonsRequested", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Requests. + /// + public static string Requests_Title { + get { + return ResourceManager.GetString("Requests_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Toggle Dropdown. + /// + public static string Requests_ToggleDropdown { + get { + return ResourceManager.GetString("Requests_ToggleDropdown", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to TV Shows. + /// + public static string Requests_TvShowTabTitle { + get { + return ResourceManager.GetString("Requests_TvShowTabTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Albums. + /// + public static string Search_Albums { + get { + return ResourceManager.GetString("Search_Albums", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All Seasons. + /// + public static string Search_AllSeasons { + get { + return ResourceManager.GetString("Search_AllSeasons", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to is already in Plex!. + /// + public static string Search_AlreadyInPlex { + get { + return ResourceManager.GetString("Search_AlreadyInPlex", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to has already been requested!. + /// + public static string Search_AlreadyRequested { + get { + return ResourceManager.GetString("Search_AlreadyRequested", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Available. + /// + public static string Search_Available { + get { + return ResourceManager.GetString("Search_Available", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Coming Soon. + /// + public static string Search_ComingSoon { + get { + return ResourceManager.GetString("Search_ComingSoon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Something went wrong adding the movie to CouchPotato! Please check your settings.. + /// + public static string Search_CouchPotatoError { + get { + return ResourceManager.GetString("Search_CouchPotatoError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to We could not check if {0} is in Plex, are you sure it's correctly setup?. + /// + public static string Search_CouldNotCheckPlex { + get { + return ResourceManager.GetString("Search_CouldNotCheckPlex", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Country. + /// + public static string Search_Country { + get { + return ResourceManager.GetString("Search_Country", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sorry, but your administrator has not yet enabled this functionality.. + /// + public static string Search_ErrorNotEnabled { + get { + return ResourceManager.GetString("Search_ErrorNotEnabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sorry, but this functionality is currently only for users with Plex accounts. + /// + public static string Search_ErrorPlexAccountOnly { + get { + return ResourceManager.GetString("Search_ErrorPlexAccountOnly", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to First Season. + /// + public static string Search_FirstSeason { + get { + return ResourceManager.GetString("Search_FirstSeason", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to In Theaters. + /// + public static string Search_InTheaters { + get { + return ResourceManager.GetString("Search_InTheaters", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Latest Season. + /// + public static string Search_LatestSeason { + get { + return ResourceManager.GetString("Search_LatestSeason", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Seasons. + /// + public static string Search_Modal_SeasonsTitle { + get { + return ResourceManager.GetString("Search_Modal_SeasonsTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Movies. + /// + public static string Search_Movies { + get { + return ResourceManager.GetString("Search_Movies", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to We could not find the artist on MusicBrainz. Please try again later or contact your admin. + /// + public static string Search_MusicBrainzError { + get { + return ResourceManager.GetString("Search_MusicBrainzError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to We could not remove this notification because you never had it!. + /// + public static string Search_NotificationError { + get { + return ResourceManager.GetString("Search_NotificationError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Want to watch something that is not currently on Plex?! No problem! Just search for it below and request it!. + /// + public static string Search_Paragraph { + get { + return ResourceManager.GetString("Search_Paragraph", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Report Issue. + /// + public static string Search_ReportIssue { + get { + return ResourceManager.GetString("Search_ReportIssue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Request. + /// + public static string Search_Request { + get { + return ResourceManager.GetString("Search_Request", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Requested. + /// + public static string Search_Requested { + get { + return ResourceManager.GetString("Search_Requested", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Season. + /// + public static string Search_Season { + get { + return ResourceManager.GetString("Search_Season", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select . + /// + public static string Search_SelectSeason { + get { + return ResourceManager.GetString("Search_SelectSeason", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Send me a notification when items I have requested have been added. + /// + public static string Search_SendNotificationText { + get { + return ResourceManager.GetString("Search_SendNotificationText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Something went wrong adding the movie to SickRage! Please check your settings.. + /// + public static string Search_SickrageError { + get { + return ResourceManager.GetString("Search_SickrageError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to was successfully added!. + /// + public static string Search_SuccessfullyAdded { + get { + return ResourceManager.GetString("Search_SuccessfullyAdded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Suggestions. + /// + public static string Search_Suggestions { + get { + return ResourceManager.GetString("Search_Suggestions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Search. + /// + public static string Search_Title { + get { + return ResourceManager.GetString("Search_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Track Count. + /// + public static string Search_TrackCount { + get { + return ResourceManager.GetString("Search_TrackCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The request of TV Shows is not correctly set up. Please contact your admin.. + /// + public static string Search_TvNotSetUp { + get { + return ResourceManager.GetString("Search_TvNotSetUp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to TV Shows. + /// + public static string Search_TvShows { + get { + return ResourceManager.GetString("Search_TvShows", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You have reached your weekly request limit for Albums! Please contact your admin.. + /// + public static string Search_WeeklyRequestLimitAlbums { + get { + return ResourceManager.GetString("Search_WeeklyRequestLimitAlbums", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You have reached your weekly request limit for Movies! Please contact your admin.. + /// + public static string Search_WeeklyRequestLimitMovie { + get { + return ResourceManager.GetString("Search_WeeklyRequestLimitMovie", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You have reached your weekly request limit for TV Shows! Please contact your admin.. + /// + public static string Search_WeeklyRequestLimitTVShow { + get { + return ResourceManager.GetString("Search_WeeklyRequestLimitTVShow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Want to watch a movie or tv show but it's not currently on Plex? + /// Login below with your Plex.tv username and password!. + /// + public static string UserLogin_Paragraph { + get { + return ResourceManager.GetString("UserLogin_Paragraph", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your login details are only used to authenticate your Plex account.. + /// + public static string UserLogin_Paragraph_SpanHover { + get { + return ResourceManager.GetString("UserLogin_Paragraph_SpanHover", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Password. + /// + public static string UserLogin_Password { + get { + return ResourceManager.GetString("UserLogin_Password", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sign In. + /// + public static string UserLogin_SignIn { + get { + return ResourceManager.GetString("UserLogin_SignIn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Login. + /// + public static string UserLogin_Title { + get { + return ResourceManager.GetString("UserLogin_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Plex.tv Username . + /// + public static string UserLogin_Username { + get { + return ResourceManager.GetString("UserLogin_Username", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Username. + /// + public static string UserLogin_Username_Placeholder { + get { + return ResourceManager.GetString("UserLogin_Username_Placeholder", resourceCulture); + } + } + } +} diff --git a/PlexRequests.UI/Resources/UI.da.resx b/PlexRequests.UI/Resources/UI.da.resx new file mode 100644 index 000000000..c8acb402e --- /dev/null +++ b/PlexRequests.UI/Resources/UI.da.resx @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Log ind + + + Ønsker du at se en film eller tv-show, men det er i øjeblikket ikke på Plex? Log nedenfor med dit Plex.tv brugernavn og password !! + + + Dine login-oplysninger bruges kun til at godkende din Plex konto. + + + Plex.tv Brugernavn + + + Brugernavn + + + Adgangskode + + + log på + + + Noget gik galt + + + Fuldført + + + Plex Requests + + + Søg + + + Anmodninger + + + Issues + + + STØT + + + Administrator + + + Indstillinger + + + Skift adgangskode + + + Log ud + + + Der er en ny opdatering tilgængelig! Klik + + + Dansk + + + Spansk + + + Tysk + + + Dansk + + + Portugisisk + + + Swedish + + + Italiensk + + + her + + + Hollandsk + + + Film + + + TV-shows + + + Album + + + Ønsker at se noget, der ikke i øjeblikket på Plex ?! Intet problem! Bare søge efter det nedenfor og anmode den ! + + + Søg + + + Forslag + + + Kommer snart + + + Teatre + + + Send mig en meddelelse, når emner, jeg har anmodet er blevet tilføjet + + + Gem + + + Tilgængelig + + + Forespørgsel sendt + + + Forespørgsel + + + Alle sæsoner + + + Første sæson + + + Sidste sæson + + + Vælg + + + Rapport Issue + + + Forkert lyd + + + Undertekster + + + Forkert indhold + + + Playback Issues + + + Andet + + + Track Count + + + Land + + + Årstid + + + Luk + + + Tilføj et problem! + + + Gem ændringer + + + Sæson + + + Velkommen + + + Anmodninger + + + Nedenfor kan du se dine og alle andre anmodninger, samt deres download og godkendelse status. + + + Film + + + TV-shows + + + Album + + + Slet Film + + + Godkend film + + + Slet tv-udsendelser + + + Godkend tv-udsendelser + + + Slet Music + + + Godkend Music + + + Alle + + + Godkendt + + + Godkendt + + + Tilgængelig + + + Ikke tilgængelig + + + Udgivet + + + Udgivet: + + + Ordrer + + + Filter + + + Seneste anmodninger + + + Ældste anmodninger + + + Nyeste udgivelser + + + Ældste udgivelser + + + Frigivelsesdato + + + Seasons Anmodet + + + Anmodet Af + + + Ønskede dato + + + Toggle Dropdown + + + Godkend + + + Fjern + + + ikke tilgængelig + + + Mark tilgængelig + + + Godkendt + + + Tilgængelig + + + Aktieemission + + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.de.resx b/PlexRequests.UI/Resources/UI.de.resx new file mode 100644 index 000000000..6d5bd0920 --- /dev/null +++ b/PlexRequests.UI/Resources/UI.de.resx @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Anmelden + + + Möchten Sie einen Film oder eine TV-Show zu sehen, aber es ist derzeit nicht auf Plex? Loggen Sie sich unten mit Ihrem Plex.tv Benutzernamen und Passwort ! + + + Ihre Login-Daten werden verwendet, nur Ihr Plex Konto zu authentifizieren. + + + Plex.tv Benutzername + + + Benutzername + + + Passwort + + + Anmelden + + + Irgendetwas ist falsch gelaufen + + + Erfolg + + + Plex Requests + + + Suche + + + Anfragen + + + Probleme + + + Spenden + + + Verwaltung + + + Einstellungen + + + Passwort ändern + + + Ausloggen + + + Es gibt ein neues Update verfügbar! Klicken + + + Englisch + + + Spanisch + + + Deutsch + + + Dänisch + + + Portugiesisch + + + Schwedisch + + + Italienisch + + + hier + + + Niederländisch + + + Filme + + + SERIEN + + + Alben + + + Möchten Sie etwas zu sehen, die derzeit nicht auf Plex ist ?! Kein Problem! Suchen Sie einfach nach unten und es es wünschen ! + + + Suche + + + Vorschläge + + + Demnächst + + + Theatern + + + Senden Sie mir eine Benachrichtigung, wenn Gegenstände, die ich angefordert wurden hinzugefügt + + + Speichern + + + V ERFÜGBAR + + + angefragt + + + Angefordert + + + alle Saisonen + + + Erste Saison + + + Neueste Saison + + + Auswählen + + + Report Ausgabe + + + Falsche Audio + + + Keine Untertitel + + + Falscher Inhaltstyp. + + + Wiedergabe-Probleme + + + Sonstige + + + Track-Count + + + Land + + + Jahreszeiten + + + Schließen + + + Fügen Sie ein Problem + + + Änderungen speichern + + + Staffel + + + Herzlich willkommen + + + Anfragen + + + Im Folgenden finden Sie Ihre und alle anderen Anfragen, sowie deren Download und Genehmigungsstatus zu sehen. + + + Filme + + + SERIEN + + + Alben + + + Löschen von Filmen + + + Genehmigen-Filme + + + Löschen TV Shows + + + Genehmigen TV Shows + + + Löschen Music + + + Genehmigen Music + + + Alle + + + Genehmigt + + + Nicht bestätigt + + + V ERFÜGBAR + + + Nicht verfügbar + + + Frei + + + Frei + + + Bestellung + + + Filter! + + + Aktuelle Anfragen + + + Älteste Anfragen + + + Neueste Veröffentlichungen + + + Die ältesten Releases + + + Veröffentlichung + + + Jahreszeiten heraus + + + Beantragt von + + + angefragt + + + Toggle Dropdown + + + Genehmigen + + + Entfernen + + + Nicht verfügbar + + + V ERFÜGBAR + + + Genehmigt + + + V ERFÜGBAR + + + Problemstellung + + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.es.resx b/PlexRequests.UI/Resources/UI.es.resx new file mode 100644 index 000000000..b1f7dd630 --- /dev/null +++ b/PlexRequests.UI/Resources/UI.es.resx @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + INICIAR SESIÓN + + + ¿Quieres ver una película o programa de televisión, pero no es actualmente en Plex? Ingresa abajo con su nombre de usuario y contraseña Plex.tv ! + + + Sus datos de acceso sólo se utilizan para autenticar su cuenta Plex. + + + Plex.tv nombre de usuario + + + Username + + + Contraseña + + + Iniciar sesión + + + Algo salió mal + + + ¡Éxito! + + + Plex Requests + + + Buscar + + + Peticiones + + + Problemas + + + Dona + + + Administración + + + Ajustes + + + Cambiar contraseña + + + Desconectarse + + + Hay una nueva actualización disponible! Hacer clic + + + Inglés + + + Spanish + + + German + + + Danés + + + Portugués + + + Sueco + + + Italiano + + + aquí + + + Holandés + + + Películas + + + Serie de TV + + + Álbumes + + + ¿Quieres ver algo que no se encuentra actualmente en Plex ?! ¡No hay problema! Sólo la búsqueda de abajo y que solicitarlo ! + + + Buscar + + + Sugerencias + + + Muy Pronto + + + En los cines + + + Envíame una notificación cuando se han añadido elementos que he solicitado + + + Ahorra + + + disponible + + + Pedido + + + Solicitud + + + Todas las temporadas + + + Primera Temporada + + + Última estación + + + Seleccionar + + + Informe del problema + + + Audio mal + + + Subtitles + + + Contenido incorrecto + + + Problemas de reproducción + + + Otro + + + El número de pistas + + + País + + + Temporadas + + + Cerrar + + + Añadir un problema + + + Guardar cambios + + + Temporada + + + Bienvenido + + + Peticiones + + + A continuación se puede ver la suya y todas las demás solicitudes, así como su descarga y aprobación de estado. + + + Películas + + + Serie de TV + + + Álbumes + + + Eliminar películas + + + Aprobar películas + + + Eliminar programas de televisión + + + Aprobar programas de televisión + + + Eliminar la música + + + Aprobar la música + + + Todo + + + Aprobado + + + No Aprovado + + + disponible + + + No disponible + + + Publicado + + + Publicado + + + Pedido + + + Filtra + + + Las últimas peticiones + + + Las solicitudes más antiguas + + + Últimos Lanzamientos + + + Estrenos más antiguas + + + Fecha de lanzamiento + + + Estaciones solicitado + + + SOLICITADO POR + + + ¡Fecha solicitada + + + Toggle Desplegable + + + Trabajo aprobado + + + Eliminar + + + Marcar como no disponible + + + disponible + + + Aprobado + + + disponible + + + emitir + + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.fr.resx b/PlexRequests.UI/Resources/UI.fr.resx new file mode 100644 index 000000000..52168e37b --- /dev/null +++ b/PlexRequests.UI/Resources/UI.fr.resx @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Se connecter + + + Vous voulez regarder un film ou la télévision, mais il est pas actuellement sur Plex? Connectez-vous ci-dessous avec votre nom d'utilisateur et mot de passe Plex.tv! + + + Vos informations de connexion sont uniquement utilisées pour authentifier votre compte Plex. + + + Plex.tv Nom d'utilisateur + + + Nom d’utilisateur + + + Mot de passe + + + Se connecter + + + Quelque-chose s'est mal passé + + + Succès + + + Plex Requests + + + Chercher + + + Requêtes + + + Sortie + + + Faire Un Don + + + d'Administration + + + Paramètres + + + Modifier le mot de passe + + + Déconnexion + + + Il y a une nouvelle mise à jour disponible! Cliquez + + + Anglais + + + Espagnol + + + Allemand + + + Danois + + + Portugais + + + Suédois (homonymie) + + + Italien + + + ici + + + Néerlandais + + + Films + + + Émissions de télévision + + + Albums + + + Vous voulez regarder quelque chose qui est pas actuellement sur Plex ?! Pas de problème! Il suffit de chercher ci-dessous et demander ! + + + Chercher + + + Suggestions + + + Bientôt disponible + + + Théâtre + + + Envoyez-moi une notification lorsque des éléments que j'ai demandés ont été ajoutés + + + Enregistrer + + + Disponible + + + Demandé + + + Requête + + + Saisons + + + Première saison + + + Dernière saison + + + Sélectionner + + + Formulaire de rapport d'incident + + + Mauvais Audio + + + Pas de sous-titres + + + Contenu erroné + + + Problèmes de lecture + + + Autre + + + Nombre de pistes + + + Pays + + + Saisons + + + Fermer + + + Ajouter une question + + + Sauvegarder les modifications + + + SAISON + + + Bienvenue + + + Requêtes + + + Ci-dessous vous pouvez voir la vôtre et toutes les autres demandes, ainsi que le téléchargement et l'état d'approbation + + + Films + + + Émissions de télévision + + + Albums + + + Supprimer Films + + + Approuver Films + + + Supprimer Séries TV + + + Approuver Séries TV + + + Supprimer la musique + + + Approuver la musique + + + Tous + + + Approuvé + + + Par voie orale pas approuvée + + + Disponible + + + Non disponible + + + Publié + + + Publié + + + Commandez + + + Filtrer + + + Dernières demandes + + + Le plus ancien demandes + + + Dernières informations + + + Le plus ancien de presse + + + Date de commercialisation + + + Seasons Demandés + + + Demandé par + + + Date demandée + + + Basculer Dropdown + + + Approuver + + + Supprimer + + + Marquer comme indisponible + + + Marquer comme disponible + + + Approuvé + + + Disponible + + + Question en litige + + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.it.resx b/PlexRequests.UI/Resources/UI.it.resx new file mode 100644 index 000000000..5cb7fa1eb --- /dev/null +++ b/PlexRequests.UI/Resources/UI.it.resx @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Accesso + + + Vuoi guardare un film o tv ma non è attualmente in Plex? Effettua il login con il tuo username e la password Plex.tv ! + + + I dati di accesso vengono utilizzati solo per autenticare l&#39;account Plex.! + + + Plex.tv Nome utente + + + Nome utente + + + Parola d'ordine + + + Accedi + + + Errore + + + Successo + + + Plex Requests + + + C'è un nuovo aggiornamento disponibile! Clic + + + Cerca! + + + Requests + + + problemi quantificati + + + Donazione + + + admin + + + Impostazioni + + + Modifica password + + + Disconnettersi + + + Inglese + + + Spagnolo + + + Tedesco + + + Danese + + + Portoghese + + + Svedese + + + Italiano + + + Qui! + + + Olandese + + + Cerca + + + Film + + + Spettacoli TV + + + Album + + + Voglia di guardare qualcosa che non è attualmente il Plex?! Non c'è problema! Basta cercare per esso qui sotto e richiederla! + + + Suggerimenti + + + Novita + + + Nei Cinema + + + Inviami una notifica quando sono stati aggiunti elementi che ho chiesto! + + + Salva + + + disponibili + + + Richiesto + + + Richiedi + + + Tutte le Stagioni + + + Prima stagione + + + Ultima stagione + + + seleziona + + + Segnala il problema + + + Audio sbagliato + + + Nessun sottotitolo + + + Contenuto sbagliato + + + Problemi di riproduzione + + + Altre + + + Conta di pista + + + Nazione + + + Stagioni + + + Chiudi + + + Aggiungere un problema + + + Salva Cambia + + + Stagione + + + Benvenuta + + + Requests + + + Qui sotto potete vedere il vostro e tutte le altre richieste, così come il loro download e l'approvazione dello stato + + + Film + + + Spettacoli TV + + + Album + + + Cancellare i filmati + + + Approva Film + + + Cancellare programmi TV + + + Approvare programmi TV + + + Eliminare la musica + + + Approva Musica + + + Tutto ciò + + + Approvato + + + Approvato + + + disponibili + + + Non disponibile + + + Rilasciato + + + Non rilasciato + + + Ordina + + + Filtro + + + Ultimi Richieste + + + I più vecchi richieste + + + Ultime uscite + + + I più vecchi uscite + + + Data di disponibilità + + + Stagioni obbligatorio + + + Richiesto + + + Richiesto + + + Imposta/rimuovi a discesa + + + Approva + + + Togliere + + + Segna non disponibile + + + Segna disponibile + + + Approvato + + + disponibili + + + Problema + + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.nl.resx b/PlexRequests.UI/Resources/UI.nl.resx new file mode 100644 index 000000000..91932a6b1 --- /dev/null +++ b/PlexRequests.UI/Resources/UI.nl.resx @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Inloggen + + + Wilt u een film of tv-show te kijken, maar het is op het moment niet op Plex? Log hieronder in met uw gebruikersnaam en wachtwoord Plex.tv !! + + + Uw login gegevens worden alleen gebruikt om uw account te verifiëren Plex. + + + Plex.tv Gebruikersnaam + + + Gebruikersnaam + + + Wachtwoord + + + Aanmelden! + + + Er is iets fout gegaan + + + Succes + + + Plex Requests + + + Zoeken + + + Verzoeken + + + UItgaves + + + Doneren + + + Admin + + + Instellingen + + + Wachtwoord wijzigen + + + Uitloggen + + + Er is een nieuwe update beschikbaar is! Klik + + + English + + + Spanish + + + German + + + Koffiebroodje + + + Portuguese + + + Zweeds + + + Italian + + + Hier! + + + Dutch + + + Zoeken + + + Aangevraagd + + + films + + + TV programma's + + + albums + + + Wilt u kijken naar iets dat is momenteel niet op de Plex?! Geen probleem! Onderzoek enkel naar het hieronder en vraag het! + + + Suggesties + + + Binnenkort verwacht + + + In de Theaters + + + Stuur me een bericht wanneer objecten die ik heb gevraagd zijn toegevoegd + + + Opslaan + + + Beschikbaar + + + Verzoek + + + Alle seizoenen + + + Eerste seizoen + + + Laatste seizoen + + + Selecteer + + + Probleem melden + + + Verkeerde Audio + + + Geen ondertiteling + + + Verkeerde inhoud + + + Problemen met het afspelen + + + Overig + + + Spoor de graaf + + + Land + + + Seasons + + + Dichtbij + + + Een actie-item toevoegen + + + Wijzigingen opslaan + + + Seizoen + + + Welkom + + + Verzoeken + + + Hieronder vindt u de jouwe en alle andere verzoeken kan zien, evenals hun download en goedkeuring-status. + + + films + + + TV programma's + + + albums + + + Verwijder Movies + + + Goedkeuren Movies + + + Verwijder TV Shows + + + Goedkeuren TV Shows + + + Verwijderen Muziek + + + Goedkeuren Music + + + Alle + + + Goedgekeurd + + + Nog niet gestart + + + Beschikbaar + + + Niet beschikbaar + + + Released + + + Niet vrijgegeven + + + Bestel + + + Filter! + + + Laatste aanvragen + + + Oudste aanvragen + + + Nieuwste releases + + + Oudste Releases + + + Uitgiftedatum + + + Seasons gevraagd + + + Verzocht door + + + Aanvraag datum + + + Toggle Dropdown + + + Goedkeuren + + + verwijder + + + Niet beschikbaar + + + Mark beschikbaar + + + Goedgekeurd + + + Beschikbaar + + + Uitgave + + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.pt.resx b/PlexRequests.UI/Resources/UI.pt.resx new file mode 100644 index 000000000..345322658 --- /dev/null +++ b/PlexRequests.UI/Resources/UI.pt.resx @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Entrar + + + Quer assistir a um filme ou programa de TV, mas não está atualmente em Plex? Entre abaixo com seu nome de usuário e senha Plex.tv !! + + + Seus dados de login são apenas usados ​​para autenticar sua conta Plex.! + + + Plex.tv usuário + + + Nome de usuário + + + Senha + + + Assinar em! + + + Alguma coisa saiu errada. + + + Sucesso + + + Plex Requests + + + Buscar + + + Pedidos + + + Issues + + + Doar + + + Administrativo + + + Configurações + + + Alterar Senha + + + Sair + + + Há uma nova atualização disponível! Clique + + + Inglês + + + Espanhol + + + Alemão + + + Dinamarquês + + + Português + + + Sueco + + + Italiano + + + aqui + + + Holandês + + + Filmes + + + Todas as Series de TV + + + Álbuns + + + Quer assistir algo que não está atualmente em Plex ?! Sem problemas! Basta procurá-lo abaixo e solicitá-lo !! + + + Buscar + + + Sugestões + + + Em breve! + + + somente nos cinemas + + + Envie-me uma notificação quando os itens I solicitados foram adicionados! + + + Salvar + + + Disponível + + + Requeridos + + + Pedido + + + Todas as temporadas + + + Primeira Temporada + + + Últimas estação + + + Selecione + + + relatório do problema + + + Áudio errado + + + Sem legendas + + + Conteúdo errado + + + Problemas de reprodução + + + Outra + + + Contagem pista + + + País + + + Temporadas + + + Fechar + + + Adicionar um problema + + + Salvar alterações + + + Temporada + + + Bem vinda + + + Pedidos + + + Abaixo você pode ver o seu e todos os outros pedidos, bem como o seu estado de download e aprovação. + + + Filmes + + + Todas as Series de TV + + + Álbuns + + + Apagar filmes + + + Aprovar filmes + + + Excluir programas televisivo + + + Aprovar programas televisivo + + + Excluir música + + + Aprovar a música + + + tudo + + + Aprovado + + + Aprovado + + + Disponível + + + Não disponível + + + Liberada + + + Liberada + + + de Fabricação + + + Filtro! + + + Últimos pedidos + + + Mais antigos pedidos + + + Últimos Lançamentos + + + Mais antigos Lançamentos + + + Data de liberação + + + Estações pedida + + + solicitado por + + + data solicitada + + + Alternar Menu Suspenso + + + Aprovar + + + REMOVER + + + Marcar como indisponível + + + Marcar como disponível + + + Aprovado + + + Disponível + + + Questão + + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.resx b/PlexRequests.UI/Resources/UI.resx new file mode 100644 index 000000000..11d158d2c --- /dev/null +++ b/PlexRequests.UI/Resources/UI.resx @@ -0,0 +1,431 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Login + + + Want to watch a movie or tv show but it's not currently on Plex? + Login below with your Plex.tv username and password! + + + Your login details are only used to authenticate your Plex account. + + + Plex.tv Username + + + Username + + + Password + + + Sign In + + + Something went wrong! + + + Success! + + + Plex Requests + + + Search + + + Requests + + + Issues + + + Donate + + + Admin + + + Settings + + + Change Password + + + Logout + + + There is a new update available! Click + + + English + + + Spanish + + + German + + + Danish + + + Portuguese + + + Swedish + + + Italian + + + Here! + + + Dutch + + + Movies + + + TV Shows + + + Albums + + + Want to watch something that is not currently on Plex?! No problem! Just search for it below and request it! + + + Search + + + Suggestions + + + Coming Soon + + + In Theaters + + + Send me a notification when items I have requested have been added + + + Save + + + Available + + + Requested + + + Request + + + All Seasons + + + First Season + + + Latest Season + + + Select + + + Report Issue + + + Wrong Audio + + + No Subtitles + + + Wrong Content + + + Playback Issues + + + Other + + + Track Count + + + Country + + + Seasons + + + Close + + + Add an issue + + + Save Changes + + + Season + + + Welcome + + + Requests + + + Below you can see yours and all other requests, as well as their download and approval status. + + + Movies + + + TV Shows + + + Albums + + + Delete Movies + + + Approve Movies + + + Delete TV Shows + + + Approve TV Shows + + + Delete Music + + + Approve Music + + + All + + + Approved + + + Not Approved + + + Available + + + Not Available + + + Released + + + Not Released + + + Order + + + Filter + + + Latest Requests + + + Oldest Requests + + + Latest Releases + + + Oldest Releases + + + Release Date + + + Seasons Requested + + + Requested By + + + Requested Date + + + Toggle Dropdown + + + Approve + + + Remove + + + Mark Unavailable + + + Mark Available + + + Approved + + + Available + + + Issue + + + was successfully added! + + + has already been requested! + + + We could not check if {0} is in Plex, are you sure it's correctly setup? + + + Something went wrong adding the movie to CouchPotato! Please check your settings. + + + You have reached your weekly request limit for Movies! Please contact your admin. + + + is already in Plex! + + + Something went wrong adding the movie to SickRage! Please check your settings. + + + The request of TV Shows is not correctly set up. Please contact your admin. + + + You have reached your weekly request limit for Albums! Please contact your admin. + + + We could not find the artist on MusicBrainz. Please try again later or contact your admin + + + You have reached your weekly request limit for TV Shows! Please contact your admin. + + + Sorry, but this functionality is currently only for users with Plex accounts + + + Sorry, but your administrator has not yet enabled this functionality. + + + We could not remove this notification because you never had it! + + + Could not save, please try again + + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.sv.resx b/PlexRequests.UI/Resources/UI.sv.resx new file mode 100644 index 000000000..6f5040485 --- /dev/null +++ b/PlexRequests.UI/Resources/UI.sv.resx @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Logga in + + + Vill du titta på en film eller TV-show, men det är inte närvarande på Plex? Logga in nedan med Plex.tv användarnamn och lösenord !! + + + Dina inloggningsuppgifter används endast för att autentisera ditt Plex-konto. + + + Plex.tv användarnamn + + + Användarnamn + + + Lösenord + + + Logga in + + + Något gick fel + + + Lyckades + + + Plex Requests + + + Sök + + + Begäran + + + Frågor + + + Donera + + + admin + + + Inställningar + + + Byt lösenord + + + Logga ut + + + Det finns en ny uppdatering tillgänglig! Klick + + + Svenska + + + Spanska + + + Tyska + + + Danska + + + Portugisiska + + + Svenska + + + Italienska + + + Här + + + dutch + + + Fråga + + + Filmer + + + Tv program + + + Album + + + Vill titta på något som inte är närvarande på Plex ?! Inga problem! Bara söka efter den nedan och begär det ! + + + Sök + + + Förslag + + + Kommer snart + + + Teater + + + Skicka mig ett meddelande när objekt jag har begärt har lagts till! + + + Spara + + + Tillgänglig + + + Begärd + + + Alla säsonger + + + Första säsongen + + + Senaste säsongen + + + Välj + + + Rapporten fråga + + + Fel ljud + + + Inga undertexter + + + Fel innehåll + + + Uppspelningsproblem + + + Annat + + + Spår räknas + + + Land + + + Säsonger + + + Stäng + + + Lägg till en fråga + + + Spara Ändringar + + + Årstid + + + Välkommen + + + Begäran + + + Nedan kan du se din och alla andra förfrågningar, liksom deras nedladdning och godkännandestatus. + + + Filmer + + + Tv program + + + Album + + + Radera filmer + + + Godkänn filmer + + + Radera TV-program + + + Godkänna TV-program + + + Radera musik + + + Godkänn musik + + + Alla + + + Godkänd + + + Ej Godkänd + + + Tillgänglig + + + Inte tillgängligt + + + Släppte ut + + + Inte släppt + + + Beställning + + + Filter + + + Senaste förfrågningar + + + Äldsta önskemål + + + Senaste versionerna + + + Äldsta meddelanden + + + Släpptes + + + Säsonger Requested + + + Begärd av + + + Önskat datum + + + Växla rullgardinslista + + + Godkänn + + + ta bort + + + Ej tillgänglig + + + Tillgänglig + + + Godkänd + + + Tillgänglig + + + Problem + + \ No newline at end of file diff --git a/PlexRequests.UI/Startup.cs b/PlexRequests.UI/Startup.cs index 14aa38657..6f7f5fd8c 100644 --- a/PlexRequests.UI/Startup.cs +++ b/PlexRequests.UI/Startup.cs @@ -1,59 +1,59 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: Startup.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 Nancy.TinyIoc; - -using NLog; - -using Owin; - -using PlexRequests.UI.Helpers; -using PlexRequests.UI.Jobs; - -namespace PlexRequests.UI -{ - public class Startup - { - private static readonly Logger Log = LogManager.GetCurrentClassLogger(); - - public void Configuration(IAppBuilder app) - { - try - { - app.UseNancy(); - var scheduler = new Scheduler(); - scheduler.StartScheduler(); - } - catch (Exception exception) - { - Log.Fatal(exception); - throw; - } - } - } +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: Startup.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 Nancy.TinyIoc; + +using NLog; + +using Owin; + +using PlexRequests.UI.Helpers; +using PlexRequests.UI.Jobs; + +namespace PlexRequests.UI +{ + public class Startup + { + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + + public void Configuration(IAppBuilder app) + { + try + { + app.UseNancy(); + var scheduler = new Scheduler(); + scheduler.StartScheduler(); + } + catch (Exception exception) + { + Log.Fatal(exception); + throw; + } + } + } } \ No newline at end of file diff --git a/PlexRequests.UI/Views/Admin/Logs.cshtml b/PlexRequests.UI/Views/Admin/Logs.cshtml index 9fc58d202..233b703c2 100644 --- a/PlexRequests.UI/Views/Admin/Logs.cshtml +++ b/PlexRequests.UI/Views/Admin/Logs.cshtml @@ -1,123 +1,151 @@ -@using PlexRequests.UI.Helpers -@Html.Partial("_Sidebar") -@Html.LoadTableAssets() - -@{ - var baseUrl = Html.GetBaseUrl(); - var formAction = "/admin/loglevel"; - if (!string.IsNullOrEmpty(baseUrl.ToHtmlString())) - { - formAction = "/" + baseUrl.ToHtmlString() + formAction; - } -} - -
-
- Logs -
-
- -
- -
-
-
-
- -
-
-
- - - - - - - - - - - -
MessageAreaLog LevelDate
-
-
- - - - \ No newline at end of file diff --git a/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml b/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml index 37e85e772..db4ed7c36 100644 --- a/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml +++ b/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml @@ -61,6 +61,15 @@ + + Please note, this will not reset the users request limit, it will just check every X hours to see if it needs to be reset. +
+ +
+ +
+
+
diff --git a/PlexRequests.UI/Views/Admin/Settings.cshtml b/PlexRequests.UI/Views/Admin/Settings.cshtml index 91532b433..79aa9eb47 100644 --- a/PlexRequests.UI/Views/Admin/Settings.cshtml +++ b/PlexRequests.UI/Views/Admin/Settings.cshtml @@ -77,87 +77,87 @@
- - @if (Model.SearchForMovies) - { - - } - else - { - - } - + + @if (Model.SearchForMovies) + { + + } + else + { + + } +
- @if (Model.SearchForTvShows) - { - - } - else - { - - } - + @if (Model.SearchForTvShows) + { + + } + else + { + + } +
- - @if (Model.SearchForMusic) - { - - } - else - { - - } - + + @if (Model.SearchForMusic) + { + + } + else + { + + } +
- - @if (Model.RequireMovieApproval) - { - - } - else - { - - } -
+ + @if (Model.RequireMovieApproval) + { + + } + else + { + + } +
- - @if (Model.RequireTvShowApproval) - { - - } - else - { - - } - + + @if (Model.RequireTvShowApproval) + { + + } + else + { + + } +
- - @if (Model.RequireMusicApproval) - { - - } - else - { - - } - + + @if (Model.RequireMusicApproval) + { + + } + else + { + + } +
@@ -226,7 +226,7 @@ -

A comma separated list of users whose requests do not require approval.

+

A comma separated list of users whose requests do not require approval (These users also do not have a request limit).

@@ -234,16 +234,36 @@
- @*
- + +

If the request limits are set to 0 then no request limit is applied.

+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
-
//TODO: Need to implement this*@ +
-
+
@@ -255,9 +275,9 @@
\ No newline at end of file diff --git a/PlexRequests.UI/Views/Admin/Status.cshtml b/PlexRequests.UI/Views/Admin/Status.cshtml index efea96a0f..c6e3f6588 100644 --- a/PlexRequests.UI/Views/Admin/Status.cshtml +++ b/PlexRequests.UI/Views/Admin/Status.cshtml @@ -1,71 +1,71 @@ -@using PlexRequests.UI.Helpers -@Html.Partial("_Sidebar") - -
-
- Status - - -
- - -
- -
- - @if (Model.UpdateAvailable) - { - -
- - } - else - { - - } - -
- - @if (Model.UpdateAvailable) - { -

- @Model.ReleaseTitle -

-
- - @Html.Raw(Model.ReleaseNotes) - } - - -
-
- - \ No newline at end of file diff --git a/PlexRequests.UI/Views/Issues/Index.cshtml b/PlexRequests.UI/Views/Issues/Index.cshtml index 77d7b983f..9f8b15bd1 100644 --- a/PlexRequests.UI/Views/Issues/Index.cshtml +++ b/PlexRequests.UI/Views/Issues/Index.cshtml @@ -26,7 +26,7 @@

Type

-

Issue's

+

Issues

@@ -83,10 +83,12 @@
Details
+ {{#if admin}}
+ {{/if}}
diff --git a/PlexRequests.UI/Views/Landing/Index.cshtml b/PlexRequests.UI/Views/Landing/Index.cshtml index 2a3ff8945..af535fedc 100644 --- a/PlexRequests.UI/Views/Landing/Index.cshtml +++ b/PlexRequests.UI/Views/Landing/Index.cshtml @@ -19,7 +19,7 @@

Notice

- @Model.NoticeMessage
+ @Html.Raw(Model.NoticeMessage)
@if (Model.EnabledNoticeTime) { diff --git a/PlexRequests.UI/Views/Requests/Index.cshtml b/PlexRequests.UI/Views/Requests/Index.cshtml index e0e36f5ed..d603ea105 100644 --- a/PlexRequests.UI/Views/Requests/Index.cshtml +++ b/PlexRequests.UI/Views/Requests/Index.cshtml @@ -1,5 +1,6 @@ @using Nancy.Security @using PlexRequests.UI.Helpers +@using PlexRequests.UI.Resources @{ var baseUrl = Html.GetBaseUrl(); var formAction = string.Empty; @@ -9,23 +10,23 @@ } }
-

Requests

-

Below you can see yours and all other requests, as well as their download and approval status.

+

@UI.Requests_Title

+

@UI.Requests_Paragraph



@@ -40,46 +41,46 @@ { @if (Model.SearchForMovies) { - - + + } @if (Model.SearchForTvShows) { - - + + } @if (Model.SearchForMusic) { - - + + } }
@@ -152,9 +153,9 @@ {{status}}
-
Release Date: {{releaseDate}}
+
@UI.Requests_ReleaseDate: {{releaseDate}}
- Approved: + @UI.Common_Approved: {{#if_eq approved false}} {{/if_eq}} @@ -163,7 +164,7 @@ {{/if_eq}}
- Available + @UI.Requests_Available {{#if_eq available false}} {{/if_eq}} @@ -172,14 +173,14 @@ {{/if_eq}}
{{#if_eq type "tv"}} -
Seasons Requested: {{seriesRequested}}
+
@UI.Requests_SeasonsRequested: {{seriesRequested}}
{{/if_eq}} {{#if requestedUsers}} -
Requested By: {{requestedUsers}}
+
@UI.Requests_RequestedBy: {{requestedUsers}}
{{/if}} -
Requested Date: {{requestedDate}}
+
@UI.Requests_RequestedDate: {{requestedDate}}
- Issue: + @UI.Issues_Issue: {{#if_eq issueId 0}} {{else}} @@ -194,10 +195,10 @@ {{#if_eq hasQualities true}}
- +
{{else}} - + {{/if_eq}} {{/if_eq}}
- +
{{#if_eq available true}} - + {{else}} - + {{/if_eq}}
@@ -230,15 +231,15 @@ @@ -270,9 +271,9 @@ {{status}}

-
Release Date: {{releaseDate}}
+
@UI.Requests_ReleaseDate {{releaseDate}}
- Approved: + @UI.Common_Approved: {{#if_eq approved false}} {{/if_eq}} @@ -281,7 +282,7 @@ {{/if_eq}}
- Available + @UI.Requests_Available {{#if_eq available false}} {{/if_eq}} @@ -290,41 +291,29 @@ {{/if_eq}}
{{#if requestedUsers}} -
Requested By: {{requestedUsers}}
+
@UI.Requests_RequestedBy: {{requestedUsers}}
{{/if}} -
Requested Date: {{requestedDate}}
-
- {{#if otherMessage}} -
Message: {{otherMessage}}
- {{else}} -
Issue: {{issues}}
- {{/if}} -
-
- {{#if adminNote}} -
Note from Admin: {{adminNote}}
- {{/if}} -
+
@UI.Requests_RequestedDate: {{requestedDate}}
{{#if_eq admin true}} {{#if_eq approved false}}
- +
{{/if_eq}}
- +
{{#if_eq available true}} - + {{else}} - + {{/if_eq}}
@@ -334,15 +323,15 @@ @@ -358,7 +347,7 @@ diff --git a/PlexRequests.UI/Views/Search/Index.cshtml b/PlexRequests.UI/Views/Search/Index.cshtml index 6a5be0006..a37b099ae 100644 --- a/PlexRequests.UI/Views/Search/Index.cshtml +++ b/PlexRequests.UI/Views/Search/Index.cshtml @@ -1,326 +1,327 @@ -@using PlexRequests.UI.Helpers -@{ - var baseUrl = Html.GetBaseUrl(); - var url = string.Empty; - if (!string.IsNullOrEmpty(baseUrl.ToHtmlString())) - { - url = "/" + baseUrl.ToHtmlString(); - } -} -
-

Search

-

Want to watch something that is not currently on Plex?! No problem! Just search for it below and request it!

-
- - - - - - -
- @if (Model.SearchForMovies) - { - -
- -
-
- -
-
-
- } - - - @if (Model.SearchForTvShows) - { - -
-
- -
- -
-
-
-
- -
-
-
- } - - @if (Model.SearchForMusic) - { - -
-
- -
- -
-
-
-
- -
-
-
- } - - -
-
-
-
-
- - -
-
-
-
- - -
-
-
-
- -
-
-
-
-
-
-
- - - - - - - - - - - - - - - -@Html.LoadSearchAssets() +@using PlexRequests.UI.Helpers +@using PlexRequests.UI.Resources +@{ + var baseUrl = Html.GetBaseUrl(); + var url = string.Empty; + if (!string.IsNullOrEmpty(baseUrl.ToHtmlString())) + { + url = "/" + baseUrl.ToHtmlString(); + } +} +
+

@UI.Search_Title

+

@UI.Search_Paragraph

+
+ + + + + + +
+ @if (Model.SearchForMovies) + { + + + } + + + @if (Model.SearchForTvShows) + { + +
+
+ +
+ +
+
+
+
+ +
+
+
+ } + + @if (Model.SearchForMusic) + { + +
+
+ +
+ +
+
+
+
+ +
+
+
+ } + + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + +@Html.LoadSearchAssets() diff --git a/PlexRequests.UI/Views/Shared/_Layout.cshtml b/PlexRequests.UI/Views/Shared/_Layout.cshtml index fee4537f8..3cc50a93b 100644 --- a/PlexRequests.UI/Views/Shared/_Layout.cshtml +++ b/PlexRequests.UI/Views/Shared/_Layout.cshtml @@ -1,157 +1,191 @@ -@using Nancy.Security -@using Nancy.Session -@using PlexRequests.UI.Helpers -@using PlexRequests.UI.Models -@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase -@{ - var baseUrl = Html.GetBaseUrl(); - var url = string.Empty; - if (!string.IsNullOrEmpty(baseUrl.ToHtmlString())) - { - url = "/" + baseUrl.ToHtmlString(); - } -} - - - - Plex Requests - - - @Html.LoadAnalytics() - @Html.LoadAssets() - - - - - - -
- @RenderBody() -
-
- - - -
- - - \ No newline at end of file diff --git a/PlexRequests.UI/Views/UserLogin/Index.cshtml b/PlexRequests.UI/Views/UserLogin/Index.cshtml index ff2d574ff..0c04b8f7b 100644 --- a/PlexRequests.UI/Views/UserLogin/Index.cshtml +++ b/PlexRequests.UI/Views/UserLogin/Index.cshtml @@ -1,70 +1,70 @@ -@using PlexRequests.UI.Helpers -
-

Login

-
-

- Want to watch a movie or tv show but it's not currently on Plex? - Login below with your Plex.tv username and password! -

-
-
-
-
- -
-
- -
-
-
- @if (Model.UsePassword) - { -
-
- -
-
- -
-
-
- } - - -
-
- - \ No newline at end of file diff --git a/PlexRequests.UI/app.config b/PlexRequests.UI/app.config index 747d7cb7a..1de7c9330 100644 --- a/PlexRequests.UI/app.config +++ b/PlexRequests.UI/app.config @@ -1,50 +1,50 @@ - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PlexRequests.sln b/PlexRequests.sln index 93ac3b1f4..3aa64d8b8 100644 --- a/PlexRequests.sln +++ b/PlexRequests.sln @@ -1,102 +1,105 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.UI", "PlexRequests.UI\PlexRequests.UI.csproj", "{68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Api", "PlexRequests.Api\PlexRequests.Api.csproj", "{8CB8D235-2674-442D-9C6A-35FCAEEB160D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Api.Interfaces", "PlexRequests.Api.Interfaces\PlexRequests.Api.Interfaces.csproj", "{95834072-A675-415D-AA8F-877C91623810}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Core", "PlexRequests.Core\PlexRequests.Core.csproj", "{DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Store", "PlexRequests.Store\PlexRequests.Store.csproj", "{92433867-2B7B-477B-A566-96C382427525}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F4BC839C-B8FF-48BE-B22E-536A0A0A81A5}" - ProjectSection(SolutionItems) = preProject - .travis.yml = .travis.yml - appveyor.yml = appveyor.yml - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Helpers", "PlexRequests.Helpers\PlexRequests.Helpers.csproj", "{1252336D-42A3-482A-804C-836E60173DFA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.UI.Tests", "PlexRequests.UI.Tests\PlexRequests.UI.Tests.csproj", "{A930E2CF-79E2-45F9-B06A-9A719A254CE4}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Core.Tests", "PlexRequests.Core.Tests\PlexRequests.Core.Tests.csproj", "{FCFECD5D-47F6-454D-8692-E27A921BE655}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Services", "PlexRequests.Services\PlexRequests.Services.csproj", "{566EFA49-68F8-4716-9693-A6B3F2624DEA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Api.Models", "PlexRequests.Api.Models\PlexRequests.Api.Models.csproj", "{CB37A5F8-6DFC-4554-99D3-A42B502E4591}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Services.Tests", "PlexRequests.Services.Tests\PlexRequests.Services.Tests.csproj", "{EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Updater", "PlexRequests.Updater\PlexRequests.Updater.csproj", "{EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Helpers.Tests", "PlexRequests.Helpers.Tests\PlexRequests.Helpers.Tests.csproj", "{0E6395D3-B074-49E8-898D-0EB99E507E0E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}.Release|Any CPU.Build.0 = Release|Any CPU - {8CB8D235-2674-442D-9C6A-35FCAEEB160D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8CB8D235-2674-442D-9C6A-35FCAEEB160D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8CB8D235-2674-442D-9C6A-35FCAEEB160D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8CB8D235-2674-442D-9C6A-35FCAEEB160D}.Release|Any CPU.Build.0 = Release|Any CPU - {95834072-A675-415D-AA8F-877C91623810}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {95834072-A675-415D-AA8F-877C91623810}.Debug|Any CPU.Build.0 = Debug|Any CPU - {95834072-A675-415D-AA8F-877C91623810}.Release|Any CPU.ActiveCfg = Release|Any CPU - {95834072-A675-415D-AA8F-877C91623810}.Release|Any CPU.Build.0 = Release|Any CPU - {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Release|Any CPU.Build.0 = Release|Any CPU - {92433867-2B7B-477B-A566-96C382427525}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {92433867-2B7B-477B-A566-96C382427525}.Debug|Any CPU.Build.0 = Debug|Any CPU - {92433867-2B7B-477B-A566-96C382427525}.Release|Any CPU.ActiveCfg = Release|Any CPU - {92433867-2B7B-477B-A566-96C382427525}.Release|Any CPU.Build.0 = Release|Any CPU - {1252336D-42A3-482A-804C-836E60173DFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1252336D-42A3-482A-804C-836E60173DFA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1252336D-42A3-482A-804C-836E60173DFA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1252336D-42A3-482A-804C-836E60173DFA}.Release|Any CPU.Build.0 = Release|Any CPU - {A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Release|Any CPU.Build.0 = Release|Any CPU - {FCFECD5D-47F6-454D-8692-E27A921BE655}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FCFECD5D-47F6-454D-8692-E27A921BE655}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FCFECD5D-47F6-454D-8692-E27A921BE655}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FCFECD5D-47F6-454D-8692-E27A921BE655}.Release|Any CPU.Build.0 = Release|Any CPU - {566EFA49-68F8-4716-9693-A6B3F2624DEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {566EFA49-68F8-4716-9693-A6B3F2624DEA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {566EFA49-68F8-4716-9693-A6B3F2624DEA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {566EFA49-68F8-4716-9693-A6B3F2624DEA}.Release|Any CPU.Build.0 = Release|Any CPU - {CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Release|Any CPU.Build.0 = Release|Any CPU - {EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}.Release|Any CPU.Build.0 = Release|Any CPU - {EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}.Release|Any CPU.Build.0 = Release|Any CPU - {0E6395D3-B074-49E8-898D-0EB99E507E0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0E6395D3-B074-49E8-898D-0EB99E507E0E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0E6395D3-B074-49E8-898D-0EB99E507E0E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0E6395D3-B074-49E8-898D-0EB99E507E0E}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25123.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.UI", "PlexRequests.UI\PlexRequests.UI.csproj", "{68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Api", "PlexRequests.Api\PlexRequests.Api.csproj", "{8CB8D235-2674-442D-9C6A-35FCAEEB160D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Api.Interfaces", "PlexRequests.Api.Interfaces\PlexRequests.Api.Interfaces.csproj", "{95834072-A675-415D-AA8F-877C91623810}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Core", "PlexRequests.Core\PlexRequests.Core.csproj", "{DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Store", "PlexRequests.Store\PlexRequests.Store.csproj", "{92433867-2B7B-477B-A566-96C382427525}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F4BC839C-B8FF-48BE-B22E-536A0A0A81A5}" + ProjectSection(SolutionItems) = preProject + .travis.yml = .travis.yml + appveyor.yml = appveyor.yml + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Helpers", "PlexRequests.Helpers\PlexRequests.Helpers.csproj", "{1252336D-42A3-482A-804C-836E60173DFA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.UI.Tests", "PlexRequests.UI.Tests\PlexRequests.UI.Tests.csproj", "{A930E2CF-79E2-45F9-B06A-9A719A254CE4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Core.Tests", "PlexRequests.Core.Tests\PlexRequests.Core.Tests.csproj", "{FCFECD5D-47F6-454D-8692-E27A921BE655}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Services", "PlexRequests.Services\PlexRequests.Services.csproj", "{566EFA49-68F8-4716-9693-A6B3F2624DEA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Api.Models", "PlexRequests.Api.Models\PlexRequests.Api.Models.csproj", "{CB37A5F8-6DFC-4554-99D3-A42B502E4591}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Services.Tests", "PlexRequests.Services.Tests\PlexRequests.Services.Tests.csproj", "{EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Updater", "PlexRequests.Updater\PlexRequests.Updater.csproj", "{EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Helpers.Tests", "PlexRequests.Helpers.Tests\PlexRequests.Helpers.Tests.csproj", "{0E6395D3-B074-49E8-898D-0EB99E507E0E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}.Release|Any CPU.Build.0 = Release|Any CPU + {8CB8D235-2674-442D-9C6A-35FCAEEB160D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8CB8D235-2674-442D-9C6A-35FCAEEB160D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8CB8D235-2674-442D-9C6A-35FCAEEB160D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8CB8D235-2674-442D-9C6A-35FCAEEB160D}.Release|Any CPU.Build.0 = Release|Any CPU + {95834072-A675-415D-AA8F-877C91623810}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {95834072-A675-415D-AA8F-877C91623810}.Debug|Any CPU.Build.0 = Debug|Any CPU + {95834072-A675-415D-AA8F-877C91623810}.Release|Any CPU.ActiveCfg = Release|Any CPU + {95834072-A675-415D-AA8F-877C91623810}.Release|Any CPU.Build.0 = Release|Any CPU + {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Release|Any CPU.Build.0 = Release|Any CPU + {92433867-2B7B-477B-A566-96C382427525}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {92433867-2B7B-477B-A566-96C382427525}.Debug|Any CPU.Build.0 = Debug|Any CPU + {92433867-2B7B-477B-A566-96C382427525}.Release|Any CPU.ActiveCfg = Release|Any CPU + {92433867-2B7B-477B-A566-96C382427525}.Release|Any CPU.Build.0 = Release|Any CPU + {1252336D-42A3-482A-804C-836E60173DFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1252336D-42A3-482A-804C-836E60173DFA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1252336D-42A3-482A-804C-836E60173DFA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1252336D-42A3-482A-804C-836E60173DFA}.Release|Any CPU.Build.0 = Release|Any CPU + {A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Release|Any CPU.Build.0 = Release|Any CPU + {FCFECD5D-47F6-454D-8692-E27A921BE655}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FCFECD5D-47F6-454D-8692-E27A921BE655}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FCFECD5D-47F6-454D-8692-E27A921BE655}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FCFECD5D-47F6-454D-8692-E27A921BE655}.Release|Any CPU.Build.0 = Release|Any CPU + {566EFA49-68F8-4716-9693-A6B3F2624DEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {566EFA49-68F8-4716-9693-A6B3F2624DEA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {566EFA49-68F8-4716-9693-A6B3F2624DEA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {566EFA49-68F8-4716-9693-A6B3F2624DEA}.Release|Any CPU.Build.0 = Release|Any CPU + {CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Release|Any CPU.Build.0 = Release|Any CPU + {EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}.Release|Any CPU.Build.0 = Release|Any CPU + {EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}.Release|Any CPU.Build.0 = Release|Any CPU + {0E6395D3-B074-49E8-898D-0EB99E507E0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0E6395D3-B074-49E8-898D-0EB99E507E0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E6395D3-B074-49E8-898D-0EB99E507E0E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0E6395D3-B074-49E8-898D-0EB99E507E0E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + RESX_PrefixTranslations = False + EndGlobalSection +EndGlobal diff --git a/appveyor.yml b/appveyor.yml index f89de2ad2..a23086f4c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,9 +3,9 @@ configuration: Release assembly_info: patch: true file: '**\AssemblyInfo.*' - assembly_version: '1.8.2' + assembly_version: '1.8.3' assembly_file_version: '{version}' - assembly_informational_version: '1.8.2' + assembly_informational_version: '1.8.3' before_build: - cmd: appveyor-retry nuget restore build: