From a5dc3253d7666f8b3130813237b4c2b44c818bed Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Sun, 13 Mar 2016 21:48:31 +0000 Subject: [PATCH 01/21] fixed an issue where the issues text appears larger --- PlexRequests.UI/Content/requests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlexRequests.UI/Content/requests.js b/PlexRequests.UI/Content/requests.js index 98252440f..9fbff85d0 100644 --- a/PlexRequests.UI/Content/requests.js +++ b/PlexRequests.UI/Content/requests.js @@ -171,7 +171,7 @@ $(document).on("click", ".clear", function (e) { if (checkJsonResponse(response)) { generateNotify("Success! Issues Cleared.", "info"); - $('#issueArea').html("

Issue: None

"); + $('#issueArea').html("
Issue: None
"); } }, error: function (e) { From 02d3118438d54d00d144a215eb98e9f9cf0e91e2 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Sun, 13 Mar 2016 22:07:16 +0000 Subject: [PATCH 02/21] Started added tests --- PlexRequests.UI.Tests/AdminModuleTests.cs | 104 ++++++++++++++++++ .../PlexRequests.UI.Tests.csproj | 1 + PlexRequests.UI/Content/requests.js | 2 - PlexRequests.UI/Modules/AdminModule.cs | 4 +- 4 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 PlexRequests.UI.Tests/AdminModuleTests.cs diff --git a/PlexRequests.UI.Tests/AdminModuleTests.cs b/PlexRequests.UI.Tests/AdminModuleTests.cs new file mode 100644 index 000000000..e95bde6ce --- /dev/null +++ b/PlexRequests.UI.Tests/AdminModuleTests.cs @@ -0,0 +1,104 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UserLoginModuleTests.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 Moq; + +using Nancy; +using Nancy.Testing; + +using Newtonsoft.Json; + +using NUnit.Framework; + +using PlexRequests.Api.Interfaces; +using PlexRequests.Api.Models; +using PlexRequests.Api.Models.Plex; +using PlexRequests.Core; +using PlexRequests.Core.SettingModels; +using PlexRequests.UI.Models; +using PlexRequests.UI.Modules; + +namespace PlexRequests.UI.Tests +{ + [TestFixture] + public class AdminModuleTests + { + private Mock> AuthMock { get; set; } + private Mock PlexMock { get; set; } + + [SetUp] + public void Setup() + { + AuthMock = new Mock>(); + PlexMock = new Mock(); + } + + [Test] + [Ignore("Need to finish")] + public void RequestAuthTokenTest() + { + var expectedSettings = new AuthenticationSettings {UserAuthentication = false, PlexAuthToken = "abc"}; + AuthMock.Setup(x => x.GetSettings()).Returns(expectedSettings); + + var bootstrapper = new ConfigurableBootstrapper(with => + { + with.Module(); + with.Dependency(AuthMock.Object); + with.Dependency(PlexMock.Object); + with.RootPathProvider(); + with.RequestStartup((container, pipelines, context) => + { + context.CurrentUser = new UserIdentity { UserName = "user"}; + }); + }); + + bootstrapper.WithSession(new Dictionary()); + + var browser = new Browser(bootstrapper); + + var result = browser.Post("/admin/requestauth", with => + { + with.HttpRequest(); + with.Header("Accept", "application/json"); + with.FormValue("username", "abc"); + with.FormValue("password","pass"); + + }); + + Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode)); + Assert.That(result.Context.Request.Session[SessionKeys.UsernameKey], Is.EqualTo("abc")); + + var body = JsonConvert.DeserializeObject(result.Body.AsString()); + Assert.That(body.Result, Is.EqualTo(true)); + AuthMock.Verify(x => x.GetSettings(), Times.Once); + PlexMock.Verify(x => x.SignIn(It.IsAny(), It.IsAny()), Times.Never); + PlexMock.Verify(x => x.GetUsers(It.IsAny()), Times.Never); + } + + } +} \ No newline at end of file diff --git a/PlexRequests.UI.Tests/PlexRequests.UI.Tests.csproj b/PlexRequests.UI.Tests/PlexRequests.UI.Tests.csproj index 594207c57..08c9374d2 100644 --- a/PlexRequests.UI.Tests/PlexRequests.UI.Tests.csproj +++ b/PlexRequests.UI.Tests/PlexRequests.UI.Tests.csproj @@ -94,6 +94,7 @@ + diff --git a/PlexRequests.UI/Content/requests.js b/PlexRequests.UI/Content/requests.js index 9fbff85d0..371fe8c51 100644 --- a/PlexRequests.UI/Content/requests.js +++ b/PlexRequests.UI/Content/requests.js @@ -182,8 +182,6 @@ $(document).on("click", ".clear", function (e) { }); -//change - // Change Availability $(document).on("click", ".change", function (e) { e.preventDefault(); diff --git a/PlexRequests.UI/Modules/AdminModule.cs b/PlexRequests.UI/Modules/AdminModule.cs index e075d7763..13bf1afef 100644 --- a/PlexRequests.UI/Modules/AdminModule.cs +++ b/PlexRequests.UI/Modules/AdminModule.cs @@ -68,9 +68,9 @@ ISettingsService auth SonarrService = sonarr; SonarrApi = sonarrApi; -#if !DEBUG +//#if !DEBUG this.RequiresAuthentication(); -#endif +//#endif Get["/"] = _ => Admin(); Get["/authentication"] = _ => Authentication(); From 5153afd3f196a7b18c43b2af2e8d394de529b272 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Mon, 14 Mar 2016 13:01:56 +0000 Subject: [PATCH 03/21] Added pace.js --- PlexRequests.UI/Content/pace.css | 19 ++++++++++++++++ PlexRequests.UI/Content/pace.min.css | 1 + PlexRequests.UI/Content/pace.min.js | 2 ++ PlexRequests.UI/Content/pace.scss | 24 +++++++++++++++++++++ PlexRequests.UI/PlexRequests.UI.csproj | 11 ++++++++++ PlexRequests.UI/Views/Shared/_Layout.cshtml | 3 +++ PlexRequests.UI/compilerconfig.json | 4 ++++ 7 files changed, 64 insertions(+) create mode 100644 PlexRequests.UI/Content/pace.css create mode 100644 PlexRequests.UI/Content/pace.min.css create mode 100644 PlexRequests.UI/Content/pace.min.js create mode 100644 PlexRequests.UI/Content/pace.scss diff --git a/PlexRequests.UI/Content/pace.css b/PlexRequests.UI/Content/pace.css new file mode 100644 index 000000000..6992c049a --- /dev/null +++ b/PlexRequests.UI/Content/pace.css @@ -0,0 +1,19 @@ +.pace { + -webkit-pointer-events: none; + pointer-events: none; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; } + +.pace-inactive { + display: none; } + +.pace .pace-progress { + background: #ffa400; + position: fixed; + z-index: 2000; + top: 0; + right: 100%; + width: 100%; + height: 2px; } + diff --git a/PlexRequests.UI/Content/pace.min.css b/PlexRequests.UI/Content/pace.min.css new file mode 100644 index 000000000..caea56aa7 --- /dev/null +++ b/PlexRequests.UI/Content/pace.min.css @@ -0,0 +1 @@ +.pace{-webkit-pointer-events:none;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;}.pace-inactive{display:none;}.pace .pace-progress{background:#ffa400;position:fixed;z-index:2000;top:0;right:100%;width:100%;height:2px;} \ No newline at end of file diff --git a/PlexRequests.UI/Content/pace.min.js b/PlexRequests.UI/Content/pace.min.js new file mode 100644 index 000000000..5a3ef3d4d --- /dev/null +++ b/PlexRequests.UI/Content/pace.min.js @@ -0,0 +1,2 @@ +/*! pace 1.0.0 */ +(function () { var a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X = [].slice, Y = {}.hasOwnProperty, Z = function (a, b) { function c() { this.constructor = a } for (var d in b) Y.call(b, d) && (a[d] = b[d]); return c.prototype = b.prototype, a.prototype = new c, a.__super__ = b.prototype, a }, $ = [].indexOf || function (a) { for (var b = 0, c = this.length; c > b; b++) if (b in this && this[b] === a) return b; return -1 }; for (u = { catchupTime: 100, initialRate: .03, minTime: 250, ghostTime: 100, maxProgressPerFrame: 20, easeFactor: 1.25, startOnPageLoad: !0, restartOnPushState: !0, restartOnRequestAfter: 500, target: "body", elements: { checkInterval: 100, selectors: ["body"] }, eventLag: { minSamples: 10, sampleCount: 3, lagThreshold: 3 }, ajax: { trackMethods: ["GET"], trackWebSockets: !0, ignoreURLs: [] } }, C = function () { var a; return null != (a = "undefined" != typeof performance && null !== performance && "function" == typeof performance.now ? performance.now() : void 0) ? a : +new Date }, E = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame, t = window.cancelAnimationFrame || window.mozCancelAnimationFrame, null == E && (E = function (a) { return setTimeout(a, 50) }, t = function (a) { return clearTimeout(a) }), G = function (a) { var b, c; return b = C(), (c = function () { var d; return d = C() - b, d >= 33 ? (b = C(), a(d, function () { return E(c) })) : setTimeout(c, 33 - d) })() }, F = function () { var a, b, c; return c = arguments[0], b = arguments[1], a = 3 <= arguments.length ? X.call(arguments, 2) : [], "function" == typeof c[b] ? c[b].apply(c, a) : c[b] }, v = function () { var a, b, c, d, e, f, g; for (b = arguments[0], d = 2 <= arguments.length ? X.call(arguments, 1) : [], f = 0, g = d.length; g > f; f++) if (c = d[f]) for (a in c) Y.call(c, a) && (e = c[a], null != b[a] && "object" == typeof b[a] && null != e && "object" == typeof e ? v(b[a], e) : b[a] = e); return b }, q = function (a) { var b, c, d, e, f; for (c = b = 0, e = 0, f = a.length; f > e; e++) d = a[e], c += Math.abs(d), b++; return c / b }, x = function (a, b) { var c, d, e; if (null == a && (a = "options"), null == b && (b = !0), e = document.querySelector("[data-pace-" + a + "]")) { if (c = e.getAttribute("data-pace-" + a), !b) return c; try { return JSON.parse(c) } catch (f) { return d = f, "undefined" != typeof console && null !== console ? console.error("Error parsing inline pace options", d) : void 0 } } }, g = function () { function a() { } return a.prototype.on = function (a, b, c, d) { var e; return null == d && (d = !1), null == this.bindings && (this.bindings = {}), null == (e = this.bindings)[a] && (e[a] = []), this.bindings[a].push({ handler: b, ctx: c, once: d }) }, a.prototype.once = function (a, b, c) { return this.on(a, b, c, !0) }, a.prototype.off = function (a, b) { var c, d, e; if (null != (null != (d = this.bindings) ? d[a] : void 0)) { if (null == b) return delete this.bindings[a]; for (c = 0, e = []; c < this.bindings[a].length;) e.push(this.bindings[a][c].handler === b ? this.bindings[a].splice(c, 1) : c++); return e } }, a.prototype.trigger = function () { var a, b, c, d, e, f, g, h, i; if (c = arguments[0], a = 2 <= arguments.length ? X.call(arguments, 1) : [], null != (g = this.bindings) ? g[c] : void 0) { for (e = 0, i = []; e < this.bindings[c].length;) h = this.bindings[c][e], d = h.handler, b = h.ctx, f = h.once, d.apply(null != b ? b : this, a), i.push(f ? this.bindings[c].splice(e, 1) : e++); return i } }, a }(), j = window.Pace || {}, window.Pace = j, v(j, g.prototype), D = j.options = v({}, u, window.paceOptions, x()), U = ["ajax", "document", "eventLag", "elements"], Q = 0, S = U.length; S > Q; Q++) K = U[Q], D[K] === !0 && (D[K] = u[K]); i = function (a) { function b() { return V = b.__super__.constructor.apply(this, arguments) } return Z(b, a), b }(Error), b = function () { function a() { this.progress = 0 } return a.prototype.getElement = function () { var a; if (null == this.el) { if (a = document.querySelector(D.target), !a) throw new i; this.el = document.createElement("div"), this.el.className = "pace pace-active", document.body.className = document.body.className.replace(/pace-done/g, ""), document.body.className += " pace-running", this.el.innerHTML = '
\n
\n
\n
', null != a.firstChild ? a.insertBefore(this.el, a.firstChild) : a.appendChild(this.el) } return this.el }, a.prototype.finish = function () { var a; return a = this.getElement(), a.className = a.className.replace("pace-active", ""), a.className += " pace-inactive", document.body.className = document.body.className.replace("pace-running", ""), document.body.className += " pace-done" }, a.prototype.update = function (a) { return this.progress = a, this.render() }, a.prototype.destroy = function () { try { this.getElement().parentNode.removeChild(this.getElement()) } catch (a) { i = a } return this.el = void 0 }, a.prototype.render = function () { var a, b, c, d, e, f, g; if (null == document.querySelector(D.target)) return !1; for (a = this.getElement(), d = "translate3d(" + this.progress + "%, 0, 0)", g = ["webkitTransform", "msTransform", "transform"], e = 0, f = g.length; f > e; e++) b = g[e], a.children[0].style[b] = d; return (!this.lastRenderedProgress || this.lastRenderedProgress | 0 !== this.progress | 0) && (a.children[0].setAttribute("data-progress-text", "" + (0 | this.progress) + "%"), this.progress >= 100 ? c = "99" : (c = this.progress < 10 ? "0" : "", c += 0 | this.progress), a.children[0].setAttribute("data-progress", "" + c)), this.lastRenderedProgress = this.progress }, a.prototype.done = function () { return this.progress >= 100 }, a }(), h = function () { function a() { this.bindings = {} } return a.prototype.trigger = function (a, b) { var c, d, e, f, g; if (null != this.bindings[a]) { for (f = this.bindings[a], g = [], d = 0, e = f.length; e > d; d++) c = f[d], g.push(c.call(this, b)); return g } }, a.prototype.on = function (a, b) { var c; return null == (c = this.bindings)[a] && (c[a] = []), this.bindings[a].push(b) }, a }(), P = window.XMLHttpRequest, O = window.XDomainRequest, N = window.WebSocket, w = function (a, b) { var c, d, e, f; f = []; for (d in b.prototype) try { e = b.prototype[d], f.push(null == a[d] && "function" != typeof e ? a[d] = e : void 0) } catch (g) { c = g } return f }, A = [], j.ignore = function () { var a, b, c; return b = arguments[0], a = 2 <= arguments.length ? X.call(arguments, 1) : [], A.unshift("ignore"), c = b.apply(null, a), A.shift(), c }, j.track = function () { var a, b, c; return b = arguments[0], a = 2 <= arguments.length ? X.call(arguments, 1) : [], A.unshift("track"), c = b.apply(null, a), A.shift(), c }, J = function (a) { var b; if (null == a && (a = "GET"), "track" === A[0]) return "force"; if (!A.length && D.ajax) { if ("socket" === a && D.ajax.trackWebSockets) return !0; if (b = a.toUpperCase(), $.call(D.ajax.trackMethods, b) >= 0) return !0 } return !1 }, k = function (a) { function b() { var a, c = this; b.__super__.constructor.apply(this, arguments), a = function (a) { var b; return b = a.open, a.open = function (d, e) { return J(d) && c.trigger("request", { type: d, url: e, request: a }), b.apply(a, arguments) } }, window.XMLHttpRequest = function (b) { var c; return c = new P(b), a(c), c }; try { w(window.XMLHttpRequest, P) } catch (d) { } if (null != O) { window.XDomainRequest = function () { var b; return b = new O, a(b), b }; try { w(window.XDomainRequest, O) } catch (d) { } } if (null != N && D.ajax.trackWebSockets) { window.WebSocket = function (a, b) { var d; return d = null != b ? new N(a, b) : new N(a), J("socket") && c.trigger("request", { type: "socket", url: a, protocols: b, request: d }), d }; try { w(window.WebSocket, N) } catch (d) { } } } return Z(b, a), b }(h), R = null, y = function () { return null == R && (R = new k), R }, I = function (a) { var b, c, d, e; for (e = D.ajax.ignoreURLs, c = 0, d = e.length; d > c; c++) if (b = e[c], "string" == typeof b) { if (-1 !== a.indexOf(b)) return !0 } else if (b.test(a)) return !0; return !1 }, y().on("request", function (b) { var c, d, e, f, g; return f = b.type, e = b.request, g = b.url, I(g) ? void 0 : j.running || D.restartOnRequestAfter === !1 && "force" !== J(f) ? void 0 : (d = arguments, c = D.restartOnRequestAfter || 0, "boolean" == typeof c && (c = 0), setTimeout(function () { var b, c, g, h, i, k; if (b = "socket" === f ? e.readyState < 2 : 0 < (h = e.readyState) && 4 > h) { for (j.restart(), i = j.sources, k = [], c = 0, g = i.length; g > c; c++) { if (K = i[c], K instanceof a) { K.watch.apply(K, d); break } k.push(void 0) } return k } }, c)) }), a = function () { function a() { var a = this; this.elements = [], y().on("request", function () { return a.watch.apply(a, arguments) }) } return a.prototype.watch = function (a) { var b, c, d, e; return d = a.type, b = a.request, e = a.url, I(e) ? void 0 : (c = "socket" === d ? new n(b) : new o(b), this.elements.push(c)) }, a }(), o = function () { function a(a) { var b, c, d, e, f, g, h = this; if (this.progress = 0, null != window.ProgressEvent) for (c = null, a.addEventListener("progress", function (a) { return h.progress = a.lengthComputable ? 100 * a.loaded / a.total : h.progress + (100 - h.progress) / 2 }, !1), g = ["load", "abort", "timeout", "error"], d = 0, e = g.length; e > d; d++) b = g[d], a.addEventListener(b, function () { return h.progress = 100 }, !1); else f = a.onreadystatechange, a.onreadystatechange = function () { var b; return 0 === (b = a.readyState) || 4 === b ? h.progress = 100 : 3 === a.readyState && (h.progress = 50), "function" == typeof f ? f.apply(null, arguments) : void 0 } } return a }(), n = function () { function a(a) { var b, c, d, e, f = this; for (this.progress = 0, e = ["error", "open"], c = 0, d = e.length; d > c; c++) b = e[c], a.addEventListener(b, function () { return f.progress = 100 }, !1) } return a }(), d = function () { function a(a) { var b, c, d, f; for (null == a && (a = {}), this.elements = [], null == a.selectors && (a.selectors = []), f = a.selectors, c = 0, d = f.length; d > c; c++) b = f[c], this.elements.push(new e(b)) } return a }(), e = function () { function a(a) { this.selector = a, this.progress = 0, this.check() } return a.prototype.check = function () { var a = this; return document.querySelector(this.selector) ? this.done() : setTimeout(function () { return a.check() }, D.elements.checkInterval) }, a.prototype.done = function () { return this.progress = 100 }, a }(), c = function () { function a() { var a, b, c = this; this.progress = null != (b = this.states[document.readyState]) ? b : 100, a = document.onreadystatechange, document.onreadystatechange = function () { return null != c.states[document.readyState] && (c.progress = c.states[document.readyState]), "function" == typeof a ? a.apply(null, arguments) : void 0 } } return a.prototype.states = { loading: 0, interactive: 50, complete: 100 }, a }(), f = function () { function a() { var a, b, c, d, e, f = this; this.progress = 0, a = 0, e = [], d = 0, c = C(), b = setInterval(function () { var g; return g = C() - c - 50, c = C(), e.push(g), e.length > D.eventLag.sampleCount && e.shift(), a = q(e), ++d >= D.eventLag.minSamples && a < D.eventLag.lagThreshold ? (f.progress = 100, clearInterval(b)) : f.progress = 100 * (3 / (a + 3)) }, 50) } return a }(), m = function () { function a(a) { this.source = a, this.last = this.sinceLastUpdate = 0, this.rate = D.initialRate, this.catchup = 0, this.progress = this.lastProgress = 0, null != this.source && (this.progress = F(this.source, "progress")) } return a.prototype.tick = function (a, b) { var c; return null == b && (b = F(this.source, "progress")), b >= 100 && (this.done = !0), b === this.last ? this.sinceLastUpdate += a : (this.sinceLastUpdate && (this.rate = (b - this.last) / this.sinceLastUpdate), this.catchup = (b - this.progress) / D.catchupTime, this.sinceLastUpdate = 0, this.last = b), b > this.progress && (this.progress += this.catchup * a), c = 1 - Math.pow(this.progress / 100, D.easeFactor), this.progress += c * this.rate * a, this.progress = Math.min(this.lastProgress + D.maxProgressPerFrame, this.progress), this.progress = Math.max(0, this.progress), this.progress = Math.min(100, this.progress), this.lastProgress = this.progress, this.progress }, a }(), L = null, H = null, r = null, M = null, p = null, s = null, j.running = !1, z = function () { return D.restartOnPushState ? j.restart() : void 0 }, null != window.history.pushState && (T = window.history.pushState, window.history.pushState = function () { return z(), T.apply(window.history, arguments) }), null != window.history.replaceState && (W = window.history.replaceState, window.history.replaceState = function () { return z(), W.apply(window.history, arguments) }), l = { ajax: a, elements: d, document: c, eventLag: f }, (B = function () { var a, c, d, e, f, g, h, i; for (j.sources = L = [], g = ["ajax", "elements", "document", "eventLag"], c = 0, e = g.length; e > c; c++) a = g[c], D[a] !== !1 && L.push(new l[a](D[a])); for (i = null != (h = D.extraSources) ? h : [], d = 0, f = i.length; f > d; d++) K = i[d], L.push(new K(D)); return j.bar = r = new b, H = [], M = new m })(), j.stop = function () { return j.trigger("stop"), j.running = !1, r.destroy(), s = !0, null != p && ("function" == typeof t && t(p), p = null), B() }, j.restart = function () { return j.trigger("restart"), j.stop(), j.start() }, j.go = function () { var a; return j.running = !0, r.render(), a = C(), s = !1, p = G(function (b, c) { var d, e, f, g, h, i, k, l, n, o, p, q, t, u, v, w; for (l = 100 - r.progress, e = p = 0, f = !0, i = q = 0, u = L.length; u > q; i = ++q) for (K = L[i], o = null != H[i] ? H[i] : H[i] = [], h = null != (w = K.elements) ? w : [K], k = t = 0, v = h.length; v > t; k = ++t) g = h[k], n = null != o[k] ? o[k] : o[k] = new m(g), f &= n.done, n.done || (e++, p += n.tick(b)); return d = p / e, r.update(M.tick(b, d)), r.done() || f || s ? (r.update(100), j.trigger("done"), setTimeout(function () { return r.finish(), j.running = !1, j.trigger("hide") }, Math.max(D.ghostTime, Math.max(D.minTime - (C() - a), 0)))) : c() }) }, j.start = function (a) { v(D, a), j.running = !0; try { r.render() } catch (b) { i = b } return document.querySelector(".pace") ? (j.trigger("start"), j.go()) : setTimeout(j.start, 50) }, "function" == typeof define && define.amd ? define(function () { return j }) : "object" == typeof exports ? module.exports = j : D.startOnPageLoad && j.start() }).call(this); \ No newline at end of file diff --git a/PlexRequests.UI/Content/pace.scss b/PlexRequests.UI/Content/pace.scss new file mode 100644 index 000000000..4ae613086 --- /dev/null +++ b/PlexRequests.UI/Content/pace.scss @@ -0,0 +1,24 @@ +$primary-colour: rgb(255, 164, 0); + +.pace { + -webkit-pointer-events: none; + pointer-events: none; + + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.pace-inactive { + display: none; +} + +.pace .pace-progress { + background: $primary-colour; + position: fixed; + z-index: 2000; + top: 0; + right: 100%; + width: 100%; + height: 2px; +} \ No newline at end of file diff --git a/PlexRequests.UI/PlexRequests.UI.csproj b/PlexRequests.UI/PlexRequests.UI.csproj index 707795319..c2700c5aa 100644 --- a/PlexRequests.UI/PlexRequests.UI.csproj +++ b/PlexRequests.UI/PlexRequests.UI.csproj @@ -206,6 +206,13 @@ PreserveNewest + + pace.scss + + + pace.css + Always + PreserveNewest @@ -222,6 +229,10 @@ compilerconfig.json
+ + Always + + Designer diff --git a/PlexRequests.UI/Views/Shared/_Layout.cshtml b/PlexRequests.UI/Views/Shared/_Layout.cshtml index 89b82a3e9..1efb8dff3 100644 --- a/PlexRequests.UI/Views/Shared/_Layout.cshtml +++ b/PlexRequests.UI/Views/Shared/_Layout.cshtml @@ -9,6 +9,8 @@ + + @@ -16,6 +18,7 @@ + diff --git a/PlexRequests.UI/compilerconfig.json b/PlexRequests.UI/compilerconfig.json index 347a6059b..789c03398 100644 --- a/PlexRequests.UI/compilerconfig.json +++ b/PlexRequests.UI/compilerconfig.json @@ -2,5 +2,9 @@ { "outputFile": "Content/custom.css", "inputFile": "Content/custom.scss" + }, + { + "outputFile": "Content/pace.css", + "inputFile": "Content/pace.scss" } ] \ No newline at end of file From 334a6f3d8e77bf54547c6f846f894e0472fbf4c5 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Mon, 14 Mar 2016 13:39:52 +0000 Subject: [PATCH 04/21] added settings page for #8 --- PlexRequests.UI/Bootstrapper.cs | 1 + PlexRequests.UI/Modules/AdminModule.cs | 28 +++++- PlexRequests.UI/PlexRequests.UI.csproj | 3 + PlexRequests.UI/Startup.cs | 7 +- .../Views/Admin/EmailNotifications.cshtml | 92 +++++++++++++++++++ PlexRequests.UI/Views/Admin/_Sidebar.cshtml | 10 +- 6 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 PlexRequests.UI/Views/Admin/EmailNotifications.cshtml diff --git a/PlexRequests.UI/Bootstrapper.cs b/PlexRequests.UI/Bootstrapper.cs index ab0a5bfd8..4eee11c67 100644 --- a/PlexRequests.UI/Bootstrapper.cs +++ b/PlexRequests.UI/Bootstrapper.cs @@ -71,6 +71,7 @@ protected override void ConfigureRequestContainer(TinyIoCContainer container, Na container.Register, SettingsServiceV2>(); container.Register, SettingsServiceV2>(); container.Register, SettingsServiceV2>(); + container.Register, SettingsServiceV2>(); container.Register, GenericRepository>(); container.Register(); diff --git a/PlexRequests.UI/Modules/AdminModule.cs b/PlexRequests.UI/Modules/AdminModule.cs index 13bf1afef..184bdcc85 100644 --- a/PlexRequests.UI/Modules/AdminModule.cs +++ b/PlexRequests.UI/Modules/AdminModule.cs @@ -51,6 +51,7 @@ public class AdminModule : NancyModule private ISettingsService AuthService { get; set; } private ISettingsService PlexService { get; set; } private ISettingsService SonarrService { get; set; } + private ISettingsService EmailService { get; set; } private ISonarrApi SonarrApi { get; set; } private static Logger Log = LogManager.GetCurrentClassLogger(); @@ -59,7 +60,8 @@ public AdminModule(ISettingsService rpService, ISettingsService auth , ISettingsService plex, ISettingsService sonarr, - ISonarrApi sonarrApi) : base("admin") + ISonarrApi sonarrApi, + ISettingsService email) : base("admin") { RpService = rpService; CpService = cpService; @@ -67,10 +69,11 @@ ISettingsService auth PlexService = plex; SonarrService = sonarr; SonarrApi = sonarrApi; + EmailService = email; -//#if !DEBUG +#if !DEBUG this.RequiresAuthentication(); -//#endif +#endif Get["/"] = _ => Admin(); Get["/authentication"] = _ => Authentication(); @@ -92,6 +95,9 @@ ISettingsService auth Post["/sonarr"] = _ => SaveSonarr(); Post["/sonarrprofiles"] = _ => GetSonarrQualityProfiles(); + + Get["/emailnotification"] = _ => EmailNotifications(); + Post["/emailnotification"] = _ => SaveEmailNotifications(); } private Negotiator Authentication() @@ -239,5 +245,21 @@ private Response GetSonarrQualityProfiles() return Response.AsJson(profiles); } + + private Negotiator EmailNotifications() + { + var settings = EmailService.GetSettings(); + return View["EmailNotifications",settings]; + } + + private Response SaveEmailNotifications() + { + var settings = this.Bind(); + Log.Trace(settings.DumpJson()); + + var result = EmailService.SaveSettings(settings); + Log.Info("Saved email settings, result: {0}", result); + return Context.GetRedirect("~/admin/emailnotification"); + } } } \ No newline at end of file diff --git a/PlexRequests.UI/PlexRequests.UI.csproj b/PlexRequests.UI/PlexRequests.UI.csproj index c2700c5aa..036f695a7 100644 --- a/PlexRequests.UI/PlexRequests.UI.csproj +++ b/PlexRequests.UI/PlexRequests.UI.csproj @@ -296,6 +296,9 @@ Always + + Always + web.config diff --git a/PlexRequests.UI/Startup.cs b/PlexRequests.UI/Startup.cs index 28a918fd7..60a00ac37 100644 --- a/PlexRequests.UI/Startup.cs +++ b/PlexRequests.UI/Startup.cs @@ -26,6 +26,9 @@ #endregion using System; using FluentScheduler; + +using NLog; + using Owin; using PlexRequests.UI.Jobs; using TaskFactory = FluentScheduler.TaskFactory; @@ -34,6 +37,8 @@ namespace PlexRequests.UI { public class Startup { + + private static Logger Log = LogManager.GetCurrentClassLogger(); public void Configuration(IAppBuilder app) { try @@ -44,7 +49,7 @@ public void Configuration(IAppBuilder app) } catch (Exception exception) { - Console.WriteLine(exception.Message); + Log.Fatal(exception); throw; } diff --git a/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml b/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml new file mode 100644 index 000000000..d0560a863 --- /dev/null +++ b/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml @@ -0,0 +1,92 @@ +@Html.Partial("_Sidebar") +@{ + int port; + if (Model.EmailPort == 0) + { + port = 25; + } + else + { + port = Model.EmailPort; + } +} +
+
+
+ Email Notifications + +
+
+ +
+
+
+ +
+ +
+
+ +
+ + +
+ +
+
+ + +
+ +
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ + +
+
+ +
+
+
+
+
diff --git a/PlexRequests.UI/Views/Admin/_Sidebar.cshtml b/PlexRequests.UI/Views/Admin/_Sidebar.cshtml index 642eced41..f414c0769 100644 --- a/PlexRequests.UI/Views/Admin/_Sidebar.cshtml +++ b/PlexRequests.UI/Views/Admin/_Sidebar.cshtml @@ -43,8 +43,16 @@ else { Sonarr Settings - } @*Sickbeard Settings*@ + + @if (Context.Request.Path == "/admin/emailnotification") + { + Email Notifications + } + else + { + Email Notifications + } \ No newline at end of file From 4c0884999ee32463666bc39c46b73eaf1e967a14 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Mon, 14 Mar 2016 14:03:01 +0000 Subject: [PATCH 05/21] Added the application tester for CP #9 --- .../ICouchPotatoApi.cs | 4 +- .../Movie/CouchPotatoStatus.cs | 13 +++ .../PlexRequests.Api.Models.csproj | 1 + PlexRequests.Api/CouchPotatoApi.cs | 22 +++++ PlexRequests.UI/Modules/AdminModule.cs | 2 + .../Modules/ApplicationTesterModule.cs | 89 +++++++++++++++++++ PlexRequests.UI/PlexRequests.UI.csproj | 1 + .../Views/Admin/CouchPotato.cshtml | 45 +++++++++- .../Views/Admin/EmailNotifications.cshtml | 1 + 9 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 PlexRequests.Api.Models/Movie/CouchPotatoStatus.cs create mode 100644 PlexRequests.UI/Modules/ApplicationTesterModule.cs diff --git a/PlexRequests.Api.Interfaces/ICouchPotatoApi.cs b/PlexRequests.Api.Interfaces/ICouchPotatoApi.cs index a7b17d61c..bcfdb3dfd 100644 --- a/PlexRequests.Api.Interfaces/ICouchPotatoApi.cs +++ b/PlexRequests.Api.Interfaces/ICouchPotatoApi.cs @@ -27,11 +27,13 @@ using System; +using PlexRequests.Api.Models.Movie; + namespace PlexRequests.Api.Interfaces { public interface ICouchPotatoApi { bool AddMovie(string imdbid, string apiKey, string title, Uri baseUrl); - + CouchPotatoStatus GetStatus(Uri url, string apiKey); } } \ No newline at end of file diff --git a/PlexRequests.Api.Models/Movie/CouchPotatoStatus.cs b/PlexRequests.Api.Models/Movie/CouchPotatoStatus.cs new file mode 100644 index 000000000..7f20a7596 --- /dev/null +++ b/PlexRequests.Api.Models/Movie/CouchPotatoStatus.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlexRequests.Api.Models.Movie +{ + public class CouchPotatoStatus + { + public bool success { get; set; } + } +} diff --git a/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj b/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj index 79683c5fe..c7d8aaeef 100644 --- a/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj +++ b/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj @@ -42,6 +42,7 @@
+ diff --git a/PlexRequests.Api/CouchPotatoApi.cs b/PlexRequests.Api/CouchPotatoApi.cs index 0f8e91fcd..bdb238ee2 100644 --- a/PlexRequests.Api/CouchPotatoApi.cs +++ b/PlexRequests.Api/CouchPotatoApi.cs @@ -30,6 +30,8 @@ using NLog; using PlexRequests.Api.Interfaces; +using PlexRequests.Api.Models.Movie; + using RestSharp; namespace PlexRequests.Api @@ -71,5 +73,25 @@ public bool AddMovie(string imdbid, string apiKey, string title, Uri baseUrl) } return false; } + + /// + /// Gets the status. + /// + /// The URL. + /// The API key. + /// + public CouchPotatoStatus GetStatus(Uri url, string apiKey) + { + Log.Trace("Getting CP Status, ApiKey = {0}", apiKey); + var request = new RestRequest + { + Resource = "api/{apikey}/app.available/", + Method = Method.GET + }; + + request.AddUrlSegment("apikey", apiKey); + + return Api.Execute(request,url); + } } } \ No newline at end of file diff --git a/PlexRequests.UI/Modules/AdminModule.cs b/PlexRequests.UI/Modules/AdminModule.cs index 184bdcc85..7ac0bba95 100644 --- a/PlexRequests.UI/Modules/AdminModule.cs +++ b/PlexRequests.UI/Modules/AdminModule.cs @@ -199,6 +199,8 @@ private Negotiator CouchPotato() return View["CouchPotato", model]; } + + private Response SaveCouchPotato() { var couchPotatoSettings = this.Bind(); diff --git a/PlexRequests.UI/Modules/ApplicationTesterModule.cs b/PlexRequests.UI/Modules/ApplicationTesterModule.cs new file mode 100644 index 000000000..b979952bf --- /dev/null +++ b/PlexRequests.UI/Modules/ApplicationTesterModule.cs @@ -0,0 +1,89 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: ApplicationTesterModule.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 Nancy; +using Nancy.ModelBinding; +using Nancy.Security; + +using NLog; + +using PlexRequests.Api; +using PlexRequests.Api.Interfaces; +using PlexRequests.Core; +using PlexRequests.Core.SettingModels; +using PlexRequests.Store; +using PlexRequests.UI.Models; + +namespace PlexRequests.UI.Modules +{ + public class ApplicationTesterModule : BaseModule + { + + public ApplicationTesterModule(ICouchPotatoApi cpApi, ISonarrApi sonarrApi) : base("test") + { + this.RequiresAuthentication(); + + CpApi = cpApi; + SonarrApi = sonarrApi; + + + Post["/cp"] = _ => CouchPotatoTest(); + + } + + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + private ISonarrApi SonarrApi { get; set; } + private ICouchPotatoApi CpApi { get; } + + private Response CouchPotatoTest() + { + var couchPotatoSettings = this.Bind(); + try + { + var status = CpApi.GetStatus(couchPotatoSettings.FullUri, couchPotatoSettings.ApiKey); + return status.success + ? Response.AsJson(new JsonResponseModel { Result = true, Message = "Connected to CouchPotato successfully!" }) + : Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not connect to CouchPotato, please check your settings." }); + + } + catch (ApplicationException e) // Exceptions are expected if we cannot connect so we will just log and swallow them. + { + Log.Warn("Exception thrown when attempting to get CP's status: "); + Log.Warn(e); + var message = $"Could not connect to CouchPotato, please check your settings. Exception Message: {e.Message}"; + if (e.InnerException != null) + { + message = $"Could not connect to CouchPotato, please check your settings. Exception Message: {e.InnerException.Message}"; + } + return Response.AsJson(new JsonResponseModel { Result = false, Message = message }); + } + } + } +} \ No newline at end of file diff --git a/PlexRequests.UI/PlexRequests.UI.csproj b/PlexRequests.UI/PlexRequests.UI.csproj index 036f695a7..901540af6 100644 --- a/PlexRequests.UI/PlexRequests.UI.csproj +++ b/PlexRequests.UI/PlexRequests.UI.csproj @@ -166,6 +166,7 @@ + diff --git a/PlexRequests.UI/Views/Admin/CouchPotato.cshtml b/PlexRequests.UI/Views/Admin/CouchPotato.cshtml index 9d32f6871..a9ae695c0 100644 --- a/PlexRequests.UI/Views/Admin/CouchPotato.cshtml +++ b/PlexRequests.UI/Views/Admin/CouchPotato.cshtml @@ -29,7 +29,7 @@ - +
@@ -38,7 +38,14 @@
- + +
+
+ +
+
+ +
@@ -51,3 +58,37 @@
+ + \ No newline at end of file diff --git a/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml b/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml index d0560a863..90c17038d 100644 --- a/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml +++ b/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml @@ -90,3 +90,4 @@ + From 3188a720fae41481018bad3eeadddcb1fa97cf78 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Mon, 14 Mar 2016 14:14:08 +0000 Subject: [PATCH 06/21] Added a bunch of logging --- PlexRequests.UI/Modules/SearchModule.cs | 26 ++++++++++++++-------- PlexRequests.UI/Modules/UserLoginModule.cs | 24 ++++++++++++++++---- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/PlexRequests.UI/Modules/SearchModule.cs b/PlexRequests.UI/Modules/SearchModule.cs index c3c6e9fbd..3a681c338 100644 --- a/PlexRequests.UI/Modules/SearchModule.cs +++ b/PlexRequests.UI/Modules/SearchModule.cs @@ -105,6 +105,7 @@ private Response SearchTvShow(string searchTerm) if (tvShow?.data == null) { + Log.Trace("TV Show data is null"); return Response.AsJson(""); } var model = new List(); @@ -137,32 +138,39 @@ private Response SearchTvShow(string searchTerm) Zap2ItId = t.zap2itId }); } + + Log.Trace("Returning TV Show results: "); + Log.Trace(model.DumpJson()); return Response.AsJson(model); } - private Response UpcomingMovies() + private Response UpcomingMovies() // TODO : Not used { var movies = MovieApi.GetUpcomingMovies(); var result = movies.Result; + Log.Trace("Movie Upcoming Results: "); + Log.Trace(result.DumpJson()); return Response.AsJson(result); } - private Response CurrentlyPlayingMovies() + private Response CurrentlyPlayingMovies() // TODO : Not used { var movies = MovieApi.GetCurrentPlayingMovies(); var result = movies.Result; + Log.Trace("Movie Currently Playing Results: "); + Log.Trace(result.DumpJson()); return Response.AsJson(result); } private Response RequestMovie(int movieId) { - Log.Trace("Requesting movie with id {0}", movieId); + Log.Info("Requesting movie with id {0}", movieId); if (RequestService.CheckRequest(movieId)) { Log.Trace("movie with id {0} exists", movieId); return Response.AsJson(new { Result = false, Message = "Movie has already been requested!" }); } - Log.Trace("movie with id {0} doesnt exists", movieId); + Log.Debug("movie with id {0} doesnt exists", movieId); var cpSettings = CpService.GetSettings(); if (cpSettings.ApiKey == null) { @@ -195,16 +203,17 @@ private Response RequestMovie(int movieId) var settings = PrService.GetSettings(); + Log.Trace(settings.DumpJson()); if (!settings.RequireApproval) { var cp = new CouchPotatoApi(); - Log.Trace("Adding movie to CP (No approval required)"); + Log.Info("Adding movie to CP (No approval required)"); var result = cp.AddMovie(model.ImdbId, cpSettings.ApiKey, model.Title, cpSettings.FullUri); - Log.Trace("Adding movie to CP result {0}", result); + Log.Debug("Adding movie to CP result {0}", result); if (result) { model.Approved = true; - Log.Trace("Adding movie to database requests (No approval required)"); + Log.Debug("Adding movie to database requests (No approval required)"); RequestService.AddRequest(movieId, model); return Response.AsJson(new { Result = true }); @@ -214,7 +223,7 @@ private Response RequestMovie(int movieId) try { - Log.Trace("Adding movie to database requests"); + Log.Debug("Adding movie to database requests"); var id = RequestService.AddRequest(movieId, model); //BackgroundJob.Enqueue(() => Checker.CheckAndUpdate(model.Title, (int)id)); @@ -236,7 +245,6 @@ private Response RequestMovie(int movieId) /// private Response RequestTvShow(int showId, bool latest) { - // Latest send to Sonarr and no need to store in DB if (RequestService.CheckRequest(showId)) { return Response.AsJson(new { Result = false, Message = "TV Show has already been requested!" }); diff --git a/PlexRequests.UI/Modules/UserLoginModule.cs b/PlexRequests.UI/Modules/UserLoginModule.cs index e8f6d214e..f4901061e 100644 --- a/PlexRequests.UI/Modules/UserLoginModule.cs +++ b/PlexRequests.UI/Modules/UserLoginModule.cs @@ -30,16 +30,17 @@ using Nancy.Extensions; using Nancy.Responses.Negotiation; +using NLog; + using PlexRequests.Api.Interfaces; -using PlexRequests.Api.Models; using PlexRequests.Api.Models.Plex; using PlexRequests.Core; using PlexRequests.Core.SettingModels; +using PlexRequests.Helpers; using PlexRequests.UI.Models; namespace PlexRequests.UI.Modules { - // TODO: Add ability to logout public class UserLoginModule : NancyModule { public UserLoginModule(ISettingsService auth, IPlexApi api) : base("userlogin") @@ -54,6 +55,8 @@ public UserLoginModule(ISettingsService auth, IPlexApi a private ISettingsService AuthService { get; } private IPlexApi Api { get; } + private static Logger Log = LogManager.GetCurrentClassLogger(); + public Negotiator Index() { var settings = AuthService.GetSettings(); @@ -63,7 +66,7 @@ public Negotiator Index() private Response LoginUser() { var username = Request.Form.username.Value; - + Log.Debug("Username \"{0}\" attempting to login",username); if (string.IsNullOrWhiteSpace(username)) { return Response.AsJson(new JsonResponseModel { Result = false, Message = "Incorrect User or Password" }); @@ -72,39 +75,49 @@ private Response LoginUser() var authenticated = false; var settings = AuthService.GetSettings(); - + Log.Debug("Settings: "); + Log.Debug(settings.DumpJson()); if (IsUserInDeniedList(username, settings)) { + Log.Debug("User is in denied list, not allowing them to authenticate"); return Response.AsJson(new JsonResponseModel { Result = false, Message = "Incorrect User or Password" }); } var password = string.Empty; if (settings.UsePassword) { + Log.Debug("Using password"); password = Request.Form.password.Value; } if (settings.UserAuthentication && settings.UsePassword) // Authenticate with Plex { + Log.Debug("Need to auth and also provide pass"); var signedIn = (PlexAuthentication)Api.SignIn(username, password); if (signedIn.user?.authentication_token != null) { + Log.Debug("Correct credentials, checking if the user is in the friends list"); authenticated = CheckIfUserIsInPlexFriends(username, settings.PlexAuthToken); + Log.Debug("Friends list result = {0}", authenticated); } } else if(settings.UserAuthentication) // Check against the users in Plex { + Log.Debug("Need to auth"); authenticated = CheckIfUserIsInPlexFriends(username, settings.PlexAuthToken); + Log.Debug("Friends list result = {0}", authenticated); } else if(!settings.UserAuthentication) // No auth, let them pass! { + Log.Debug("No need to auth"); authenticated = true; } if (authenticated) { + Log.Debug("We are authenticated! Setting session."); // Add to the session (Used in the BaseModules) Session[SessionKeys.UsernameKey] = (string)username; } @@ -116,6 +129,7 @@ private Response LoginUser() private Response Logout() { + Log.Debug("Logging Out"); if (Session[SessionKeys.UsernameKey] != null) { Session.Delete(SessionKeys.UsernameKey); @@ -126,6 +140,8 @@ private Response Logout() private bool CheckIfUserIsInPlexFriends(string username, string authToken) { var users = Api.GetUsers(authToken); + Log.Debug("Plex Users: "); + Log.Debug(users.DumpJson()); return users.User.Any(x => x.Username == username); } From ba5ccfec3312cd40ec6da90606151d3ea4a79d82 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 15 Mar 2016 12:21:41 +0000 Subject: [PATCH 07/21] Fixed #5 and also added some tests to the availability checker --- .../PlexAvailabilityCheckerTests.cs | 102 ++++++++++++++++ .../PlexRequests.Services.Tests.csproj | 113 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 36 ++++++ PlexRequests.Services.Tests/packages.config | 5 + .../AvailabilityUpdateService.cs | 3 +- .../Interfaces/IAvailabilityChecker.cs | 1 + .../PlexAvailabilityChecker.cs | 68 +++++++++-- .../PlexRequests.UI.Tests.csproj | 4 +- PlexRequests.UI.Tests/packages.config | 2 +- PlexRequests.UI/Modules/SearchModule.cs | 47 +++++--- PlexRequests.sln | 6 + 11 files changed, 356 insertions(+), 31 deletions(-) create mode 100644 PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs create mode 100644 PlexRequests.Services.Tests/PlexRequests.Services.Tests.csproj create mode 100644 PlexRequests.Services.Tests/Properties/AssemblyInfo.cs create mode 100644 PlexRequests.Services.Tests/packages.config diff --git a/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs b/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs new file mode 100644 index 000000000..44f960c87 --- /dev/null +++ b/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs @@ -0,0 +1,102 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: PlexAvailabilityCheckerTests.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.Api.Interfaces; +using PlexRequests.Api.Models; +using PlexRequests.Core; +using PlexRequests.Core.SettingModels; +using PlexRequests.Helpers.Exceptions; +using PlexRequests.Services.Interfaces; + +namespace PlexRequests.Services.Tests +{ + [TestFixture] + public class PlexAvailabilityCheckerTests + { + public IAvailabilityChecker Checker { get; set; } + + [Test] + public void IsAvailableWithEmptySettingsTest() + { + var settingsMock = new Mock>(); + var authMock = new Mock>(); + var requestMock = new Mock(); + var plexMock = new Mock(); + Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object); + + Assert.Throws(() => Checker.IsAvailable("title"), "We should be throwing an exception since we cannot talk to the services."); + } + + [Test] + public void IsAvailableTest() + { + var settingsMock = new Mock>(); + var authMock = new Mock>(); + var requestMock = new Mock(); + var plexMock = new Mock(); + + var searchResult = new PlexSearch {Video = new List
diff --git a/PlexRequests.Core.Tests/StatusCheckerTests.cs b/PlexRequests.Core.Tests/StatusCheckerTests.cs new file mode 100644 index 000000000..aaacaf78a --- /dev/null +++ b/PlexRequests.Core.Tests/StatusCheckerTests.cs @@ -0,0 +1,43 @@ +#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; + +namespace PlexRequests.Core.Tests +{ + [TestFixture] + public class StatusCheckerTests + { + [Test] + public void CheckStatusTest() + { + var checker = new StatusChecker(); + var status = checker.GetStatus(); + + Assert.That(status, Is.Not.Null); + } + } +} \ No newline at end of file diff --git a/PlexRequests.Core.Tests/app.config b/PlexRequests.Core.Tests/app.config index 7503b02ee..ee433ca9b 100644 --- a/PlexRequests.Core.Tests/app.config +++ b/PlexRequests.Core.Tests/app.config @@ -1,11 +1,11 @@ - + - - + + - + diff --git a/PlexRequests.Core/Models/StatusModel.cs b/PlexRequests.Core/Models/StatusModel.cs new file mode 100644 index 000000000..33d8f0fe1 --- /dev/null +++ b/PlexRequests.Core/Models/StatusModel.cs @@ -0,0 +1,37 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: StatusModel.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 class StatusModel + { + public string Version { get; set; } + public bool UpdateAvailable { get; set; } + public int ReleasesBehind { get; set; } + public string UpdateUri { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.Core/PlexRequests.Core.csproj b/PlexRequests.Core/PlexRequests.Core.csproj index 79aca698c..eb20f81d1 100644 --- a/PlexRequests.Core/PlexRequests.Core.csproj +++ b/PlexRequests.Core/PlexRequests.Core.csproj @@ -46,6 +46,10 @@ ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll True + + ..\packages\Octokit.0.19.0\lib\net45\Octokit.dll + True + ..\packages\valueinjecter.3.1.1.2\lib\net40\Omu.ValueInjecter.dll True @@ -66,6 +70,7 @@ + @@ -77,6 +82,7 @@ + diff --git a/PlexRequests.Core/StatusChecker.cs b/PlexRequests.Core/StatusChecker.cs new file mode 100644 index 000000000..e4c939206 --- /dev/null +++ b/PlexRequests.Core/StatusChecker.cs @@ -0,0 +1,78 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: StatusChecker.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 System.Reflection; +using System.Runtime.Versioning; +using System.Threading.Tasks; + +using Octokit; + +using PlexRequests.Core.Models; +using PlexRequests.Helpers; + +namespace PlexRequests.Core +{ + public class StatusChecker + { + public StatusChecker() + { + Git = new GitHubClient(new ProductHeaderValue("PlexRequests-StatusChecker")); + } + private IGitHubClient Git { get; } + private const string Owner = "tidusjar"; + private const string RepoName = "PlexRequests.Net"; + + public async Task GetLatestRelease() + { + var releases = await Git.Repository.Release.GetAll(Owner, RepoName); + return releases.FirstOrDefault(); + } + + public StatusModel GetStatus() + { + var assemblyVersion = AssemblyHelper.GetAssemblyVersion(); + var model = new StatusModel + { + Version = assemblyVersion, + }; + + var latestRelease = GetLatestRelease(); + + var latestVersionArray = latestRelease.Result.Name.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + var latestVersion = latestVersionArray.Length > 1 ? latestVersionArray[1] : string.Empty; + + if (!latestVersion.Equals(AssemblyHelper.GetReleaseVersion(), StringComparison.InvariantCultureIgnoreCase)) + { + model.UpdateAvailable = true; + model.UpdateUri = latestRelease.Result.HtmlUrl; + } + + return model; + } + } +} \ No newline at end of file diff --git a/PlexRequests.Core/app.config b/PlexRequests.Core/app.config index 7503b02ee..ee433ca9b 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 2d5fcc6ef..ddcb2361b 100644 --- a/PlexRequests.Core/packages.config +++ b/PlexRequests.Core/packages.config @@ -3,5 +3,6 @@ + \ No newline at end of file diff --git a/PlexRequests.Helpers.Tests/AssemblyHelperTests.cs b/PlexRequests.Helpers.Tests/AssemblyHelperTests.cs new file mode 100644 index 000000000..689bfd039 --- /dev/null +++ b/PlexRequests.Helpers.Tests/AssemblyHelperTests.cs @@ -0,0 +1,42 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: AssemblyHelperTests.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; + +namespace PlexRequests.Helpers.Tests +{ + [TestFixture] + public class AssemblyHelperTests + { + [Test] + public void GetReleaseVersionTest() + { + var result = AssemblyHelper.GetReleaseVersion(); + Assert.That(result, Contains.Substring("v")); + Assert.That(result, Is.Not.EqualTo("1.0.0.0")); + } + } +} \ No newline at end of file diff --git a/PlexRequests.Helpers.Tests/PlexRequests.Helpers.Tests.csproj b/PlexRequests.Helpers.Tests/PlexRequests.Helpers.Tests.csproj index 0fa7e92d8..54af6aebe 100644 --- a/PlexRequests.Helpers.Tests/PlexRequests.Helpers.Tests.csproj +++ b/PlexRequests.Helpers.Tests/PlexRequests.Helpers.Tests.csproj @@ -35,14 +35,6 @@ 4 - - ..\packages\Hangfire.Core.1.5.3\lib\net45\Hangfire.Core.dll - True - - - ..\packages\Hangfire.SqlServer.1.5.3\lib\net45\Hangfire.SqlServer.dll - True - ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll True @@ -78,6 +70,7 @@ + diff --git a/PlexRequests.Helpers/AssemblyHelper.cs b/PlexRequests.Helpers/AssemblyHelper.cs index a2a1b6163..c1a03d2b7 100644 --- a/PlexRequests.Helpers/AssemblyHelper.cs +++ b/PlexRequests.Helpers/AssemblyHelper.cs @@ -37,5 +37,13 @@ public static string GetAssemblyVersion() var fvi = FileVersionInfo.GetVersionInfo(assembly.Location); return fvi.FileVersion; } + + public static string GetReleaseVersion() + { + var assembly = Assembly.GetExecutingAssembly(); + var fvi = FileVersionInfo.GetVersionInfo(assembly.Location); + var retVersion = "v" + fvi.FileVersion.Substring(0, fvi.FileVersion.Length - 2); + return retVersion; + } } } \ 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 0f9c7f1c1..175b4f0f8 100644 --- a/PlexRequests.Services.Tests/PlexRequests.Services.Tests.csproj +++ b/PlexRequests.Services.Tests/PlexRequests.Services.Tests.csproj @@ -59,6 +59,7 @@ + diff --git a/PlexRequests.Services.Tests/app.config b/PlexRequests.Services.Tests/app.config new file mode 100644 index 000000000..ac2586ac9 --- /dev/null +++ b/PlexRequests.Services.Tests/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/PlexRequests.Services/PlexRequests.Services.csproj b/PlexRequests.Services/PlexRequests.Services.csproj index e817d50d3..3a18c5a04 100644 --- a/PlexRequests.Services/PlexRequests.Services.csproj +++ b/PlexRequests.Services/PlexRequests.Services.csproj @@ -82,6 +82,7 @@ + diff --git a/PlexRequests.Services/app.config b/PlexRequests.Services/app.config new file mode 100644 index 000000000..ac2586ac9 --- /dev/null +++ b/PlexRequests.Services/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/PlexRequests.UI/Modules/AdminModule.cs b/PlexRequests.UI/Modules/AdminModule.cs index 52c67eb08..70c7e5fb0 100644 --- a/PlexRequests.UI/Modules/AdminModule.cs +++ b/PlexRequests.UI/Modules/AdminModule.cs @@ -100,6 +100,7 @@ public AdminModule(ISettingsService rpService, Get["/emailnotification"] = _ => EmailNotifications(); Post["/emailnotification"] = _ => SaveEmailNotifications(); + Get["/status"] = _ => Status(); } private Negotiator Authentication() @@ -272,5 +273,12 @@ private Response SaveEmailNotifications() Log.Info("Saved email settings, result: {0}", result); return Context.GetRedirect("~/admin/emailnotification"); } + + private Negotiator Status() + { + var checker = new StatusChecker(); + var status = checker.GetStatus(); + return View["Status", status]; + } } } \ No newline at end of file diff --git a/PlexRequests.UI/PlexRequests.UI.csproj b/PlexRequests.UI/PlexRequests.UI.csproj index 901540af6..430232605 100644 --- a/PlexRequests.UI/PlexRequests.UI.csproj +++ b/PlexRequests.UI/PlexRequests.UI.csproj @@ -300,6 +300,9 @@ Always + + Always + web.config diff --git a/PlexRequests.UI/Views/Admin/Status.cshtml b/PlexRequests.UI/Views/Admin/Status.cshtml new file mode 100644 index 000000000..1cb621f3c --- /dev/null +++ b/PlexRequests.UI/Views/Admin/Status.cshtml @@ -0,0 +1,18 @@ +@Html.Partial("_Sidebar") + +
+
+ Status + + +
+ + +
+ +
+
+ + +
+
\ No newline at end of file diff --git a/PlexRequests.UI/Views/Admin/_Sidebar.cshtml b/PlexRequests.UI/Views/Admin/_Sidebar.cshtml index f414c0769..7733857e8 100644 --- a/PlexRequests.UI/Views/Admin/_Sidebar.cshtml +++ b/PlexRequests.UI/Views/Admin/_Sidebar.cshtml @@ -54,5 +54,14 @@ { Email Notifications } + + @if (Context.Request.Path == "/admin/status") + { + Status/a> + } + else + { + Status + } \ No newline at end of file diff --git a/PlexRequests.sln b/PlexRequests.sln index 69ed4c4e6..4cdca72cf 100644 --- a/PlexRequests.sln +++ b/PlexRequests.sln @@ -33,6 +33,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Api.Models", " 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.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 @@ -83,6 +85,10 @@ Global {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 + {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 From a1933d8d7a2bac37f557a79fbf2319e0049a05ae Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 15 Mar 2016 17:20:23 +0000 Subject: [PATCH 15/21] Update appveyor.yml --- appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 4f4404c4a..04911a47f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,9 +1,9 @@ -version: 1.1.{build} +version: 1.2.{build} configuration: Release assembly_info: patch: true file: '**\AssemblyInfo.*' - assembly_version: '{version}' + assembly_version: '1.2.0' assembly_file_version: '{version}' assembly_informational_version: '{version}' before_build: @@ -14,4 +14,4 @@ after_build: - cmd: >- 7z a PlexRequests.zip %APPVEYOR_BUILD_FOLDER%\PlexRequests.UI\bin\Release\ - appveyor PushArtifact PlexRequests.zip \ No newline at end of file + appveyor PushArtifact PlexRequests.zip From b47213a2a73df871a64ecb63b9c28be9eda147b0 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 15 Mar 2016 18:25:22 +0000 Subject: [PATCH 16/21] updated appveyor --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 04911a47f..d786aadd9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,7 +5,7 @@ assembly_info: file: '**\AssemblyInfo.*' assembly_version: '1.2.0' assembly_file_version: '{version}' - assembly_informational_version: '{version}' + assembly_informational_version: '1.2.0' before_build: - cmd: appveyor-retry nuget restore build: From 7c8d0b269a5a793a2e606fddd539de43f5e11e0e Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 15 Mar 2016 18:39:43 +0000 Subject: [PATCH 17/21] Finished the status page #11 and some more work to #12 --- PlexRequests.Core.Tests/StatusCheckerTests.cs | 2 ++ PlexRequests.Core/StatusChecker.cs | 4 ++-- .../AssemblyHelperTests.cs | 5 ++--- PlexRequests.Helpers/AssemblyHelper.cs | 4 ++-- PlexRequests.UI/Views/Admin/Status.cshtml | 18 ++++++++++++++---- PlexRequests.UI/Views/Admin/_Sidebar.cshtml | 2 +- appveyor.yml | 4 ++-- 7 files changed, 25 insertions(+), 14 deletions(-) diff --git a/PlexRequests.Core.Tests/StatusCheckerTests.cs b/PlexRequests.Core.Tests/StatusCheckerTests.cs index aaacaf78a..e45b4dcab 100644 --- a/PlexRequests.Core.Tests/StatusCheckerTests.cs +++ b/PlexRequests.Core.Tests/StatusCheckerTests.cs @@ -24,6 +24,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // ************************************************************************/ #endregion + +using System; using NUnit.Framework; namespace PlexRequests.Core.Tests diff --git a/PlexRequests.Core/StatusChecker.cs b/PlexRequests.Core/StatusChecker.cs index e4c939206..1512082f0 100644 --- a/PlexRequests.Core/StatusChecker.cs +++ b/PlexRequests.Core/StatusChecker.cs @@ -63,10 +63,10 @@ public StatusModel GetStatus() var latestRelease = GetLatestRelease(); - var latestVersionArray = latestRelease.Result.Name.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + var latestVersionArray = latestRelease.Result.Name.Split(new[] { 'v' }, StringSplitOptions.RemoveEmptyEntries); var latestVersion = latestVersionArray.Length > 1 ? latestVersionArray[1] : string.Empty; - if (!latestVersion.Equals(AssemblyHelper.GetReleaseVersion(), StringComparison.InvariantCultureIgnoreCase)) + if (!latestVersion.Equals(AssemblyHelper.GetProductVersion(), StringComparison.InvariantCultureIgnoreCase)) { model.UpdateAvailable = true; model.UpdateUri = latestRelease.Result.HtmlUrl; diff --git a/PlexRequests.Helpers.Tests/AssemblyHelperTests.cs b/PlexRequests.Helpers.Tests/AssemblyHelperTests.cs index 689bfd039..b55443e61 100644 --- a/PlexRequests.Helpers.Tests/AssemblyHelperTests.cs +++ b/PlexRequests.Helpers.Tests/AssemblyHelperTests.cs @@ -34,9 +34,8 @@ public class AssemblyHelperTests [Test] public void GetReleaseVersionTest() { - var result = AssemblyHelper.GetReleaseVersion(); - Assert.That(result, Contains.Substring("v")); - Assert.That(result, Is.Not.EqualTo("1.0.0.0")); + var result = AssemblyHelper.GetProductVersion(); + Assert.That(result, Is.Not.Null); } } } \ No newline at end of file diff --git a/PlexRequests.Helpers/AssemblyHelper.cs b/PlexRequests.Helpers/AssemblyHelper.cs index c1a03d2b7..6c450bda8 100644 --- a/PlexRequests.Helpers/AssemblyHelper.cs +++ b/PlexRequests.Helpers/AssemblyHelper.cs @@ -38,11 +38,11 @@ public static string GetAssemblyVersion() return fvi.FileVersion; } - public static string GetReleaseVersion() + public static string GetProductVersion() { var assembly = Assembly.GetExecutingAssembly(); var fvi = FileVersionInfo.GetVersionInfo(assembly.Location); - var retVersion = "v" + fvi.FileVersion.Substring(0, fvi.FileVersion.Length - 2); + var retVersion = fvi.ProductVersion; return retVersion; } } diff --git a/PlexRequests.UI/Views/Admin/Status.cshtml b/PlexRequests.UI/Views/Admin/Status.cshtml index 1cb621f3c..057c16e44 100644 --- a/PlexRequests.UI/Views/Admin/Status.cshtml +++ b/PlexRequests.UI/Views/Admin/Status.cshtml @@ -6,11 +6,21 @@
- + + +
+ +
+ + @if (Model.UpdateAvailable) + { + + } + else + { + + } -
- -
diff --git a/PlexRequests.UI/Views/Admin/_Sidebar.cshtml b/PlexRequests.UI/Views/Admin/_Sidebar.cshtml index 7733857e8..b2e3da3c2 100644 --- a/PlexRequests.UI/Views/Admin/_Sidebar.cshtml +++ b/PlexRequests.UI/Views/Admin/_Sidebar.cshtml @@ -57,7 +57,7 @@ @if (Context.Request.Path == "/admin/status") { - Status/a> + Status } else { diff --git a/appveyor.yml b/appveyor.yml index d786aadd9..90f12b286 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,10 +1,10 @@ -version: 1.2.{build} +version: 1.2.0 configuration: Release assembly_info: patch: true file: '**\AssemblyInfo.*' assembly_version: '1.2.0' - assembly_file_version: '{version}' + assembly_file_version: '1.2.{build}' assembly_informational_version: '1.2.0' before_build: - cmd: appveyor-retry nuget restore From 1936512dbee2613788d25cc85e3a804e2fc0eff2 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 15 Mar 2016 18:48:08 +0000 Subject: [PATCH 18/21] #12 --- PlexRequests.Api.Interfaces/Properties/AssemblyInfo.cs | 2 ++ PlexRequests.Api.Models/Properties/AssemblyInfo.cs | 2 ++ PlexRequests.Api/Properties/AssemblyInfo.cs | 1 + PlexRequests.Core.Tests/Properties/AssemblyInfo.cs | 1 + PlexRequests.Core/Properties/AssemblyInfo.cs | 1 + PlexRequests.Helpers.Tests/Properties/AssemblyInfo.cs | 1 + PlexRequests.Helpers/Properties/AssemblyInfo.cs | 1 + PlexRequests.Services.Tests/Properties/AssemblyInfo.cs | 1 + PlexRequests.Services/Properties/AssemblyInfo.cs | 1 + PlexRequests.Store/Properties/AssemblyInfo.cs | 1 + PlexRequests.UI/Properties/AssemblyInfo.cs | 1 + 11 files changed, 13 insertions(+) diff --git a/PlexRequests.Api.Interfaces/Properties/AssemblyInfo.cs b/PlexRequests.Api.Interfaces/Properties/AssemblyInfo.cs index cc5288abe..e8fb7c60d 100644 --- a/PlexRequests.Api.Interfaces/Properties/AssemblyInfo.cs +++ b/PlexRequests.Api.Interfaces/Properties/AssemblyInfo.cs @@ -34,3 +34,5 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] + +[assembly: AssemblyInformationalVersionAttribute("1.0.0.0")] diff --git a/PlexRequests.Api.Models/Properties/AssemblyInfo.cs b/PlexRequests.Api.Models/Properties/AssemblyInfo.cs index 2774b1281..8115a2543 100644 --- a/PlexRequests.Api.Models/Properties/AssemblyInfo.cs +++ b/PlexRequests.Api.Models/Properties/AssemblyInfo.cs @@ -34,3 +34,5 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] + +[assembly: AssemblyInformationalVersionAttribute("1.0.0.0")] diff --git a/PlexRequests.Api/Properties/AssemblyInfo.cs b/PlexRequests.Api/Properties/AssemblyInfo.cs index bf8845186..e09af95eb 100644 --- a/PlexRequests.Api/Properties/AssemblyInfo.cs +++ b/PlexRequests.Api/Properties/AssemblyInfo.cs @@ -34,3 +34,4 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyInformationalVersionAttribute("1.0.0.0")] diff --git a/PlexRequests.Core.Tests/Properties/AssemblyInfo.cs b/PlexRequests.Core.Tests/Properties/AssemblyInfo.cs index 874fc2052..1cc04fc46 100644 --- a/PlexRequests.Core.Tests/Properties/AssemblyInfo.cs +++ b/PlexRequests.Core.Tests/Properties/AssemblyInfo.cs @@ -34,3 +34,4 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyInformationalVersionAttribute("1.0.0.0")] diff --git a/PlexRequests.Core/Properties/AssemblyInfo.cs b/PlexRequests.Core/Properties/AssemblyInfo.cs index 9ecb23fff..c72e6cfae 100644 --- a/PlexRequests.Core/Properties/AssemblyInfo.cs +++ b/PlexRequests.Core/Properties/AssemblyInfo.cs @@ -34,3 +34,4 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyInformationalVersionAttribute("1.0.0.0")] diff --git a/PlexRequests.Helpers.Tests/Properties/AssemblyInfo.cs b/PlexRequests.Helpers.Tests/Properties/AssemblyInfo.cs index 66ce11c94..1bebc4548 100644 --- a/PlexRequests.Helpers.Tests/Properties/AssemblyInfo.cs +++ b/PlexRequests.Helpers.Tests/Properties/AssemblyInfo.cs @@ -34,3 +34,4 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyInformationalVersionAttribute("1.0.0.0")] diff --git a/PlexRequests.Helpers/Properties/AssemblyInfo.cs b/PlexRequests.Helpers/Properties/AssemblyInfo.cs index 2001ed779..e1d4fa2db 100644 --- a/PlexRequests.Helpers/Properties/AssemblyInfo.cs +++ b/PlexRequests.Helpers/Properties/AssemblyInfo.cs @@ -34,3 +34,4 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyInformationalVersionAttribute("1.0.0.0")] diff --git a/PlexRequests.Services.Tests/Properties/AssemblyInfo.cs b/PlexRequests.Services.Tests/Properties/AssemblyInfo.cs index 2d8f480fd..40fbae3f4 100644 --- a/PlexRequests.Services.Tests/Properties/AssemblyInfo.cs +++ b/PlexRequests.Services.Tests/Properties/AssemblyInfo.cs @@ -34,3 +34,4 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyInformationalVersionAttribute("1.0.0.0")] diff --git a/PlexRequests.Services/Properties/AssemblyInfo.cs b/PlexRequests.Services/Properties/AssemblyInfo.cs index 1307c1c3c..1e66f1698 100644 --- a/PlexRequests.Services/Properties/AssemblyInfo.cs +++ b/PlexRequests.Services/Properties/AssemblyInfo.cs @@ -34,3 +34,4 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyInformationalVersionAttribute("1.0.0.0")] diff --git a/PlexRequests.Store/Properties/AssemblyInfo.cs b/PlexRequests.Store/Properties/AssemblyInfo.cs index d4649c354..0995b2f1f 100644 --- a/PlexRequests.Store/Properties/AssemblyInfo.cs +++ b/PlexRequests.Store/Properties/AssemblyInfo.cs @@ -34,3 +34,4 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyInformationalVersionAttribute("1.0.0.0")] diff --git a/PlexRequests.UI/Properties/AssemblyInfo.cs b/PlexRequests.UI/Properties/AssemblyInfo.cs index c6b127d59..8e32c54ad 100644 --- a/PlexRequests.UI/Properties/AssemblyInfo.cs +++ b/PlexRequests.UI/Properties/AssemblyInfo.cs @@ -33,3 +33,4 @@ // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyInformationalVersionAttribute("1.0.0.0")] From 98c0a4f879272523bbe366f88484a4eff61e29bf Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 15 Mar 2016 18:49:29 +0000 Subject: [PATCH 19/21] #12 --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 90f12b286..d786aadd9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,10 +1,10 @@ -version: 1.2.0 +version: 1.2.{build} configuration: Release assembly_info: patch: true file: '**\AssemblyInfo.*' assembly_version: '1.2.0' - assembly_file_version: '1.2.{build}' + assembly_file_version: '{version}' assembly_informational_version: '1.2.0' before_build: - cmd: appveyor-retry nuget restore From a2f44cf8d232237de60671c8f98ef96b03cd782f Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 15 Mar 2016 19:14:27 +0000 Subject: [PATCH 20/21] Finished the notes! Resolved #7 --- PlexRequests.Api.Models/Sonarr/SystemStatus.cs | 7 +++++-- PlexRequests.Api/ApiRequest.cs | 13 ++++++++++--- PlexRequests.Core/StatusChecker.cs | 4 ++-- PlexRequests.UI/Content/requests.js | 2 +- .../Modules/ApplicationTesterModule.cs | 2 +- PlexRequests.UI/Modules/LoginModule.cs | 6 ++++-- PlexRequests.UI/Modules/RequestsModule.cs | 4 ++-- PlexRequests.UI/Program.cs | 4 ++-- PlexRequests.UI/Views/Admin/_Sidebar.cshtml | 16 ++++++++-------- PlexRequests.UI/Views/Requests/Index.cshtml | 10 +++++----- 10 files changed, 40 insertions(+), 28 deletions(-) diff --git a/PlexRequests.Api.Models/Sonarr/SystemStatus.cs b/PlexRequests.Api.Models/Sonarr/SystemStatus.cs index 5f04c607a..19d822805 100644 --- a/PlexRequests.Api.Models/Sonarr/SystemStatus.cs +++ b/PlexRequests.Api.Models/Sonarr/SystemStatus.cs @@ -37,12 +37,15 @@ public class SystemStatus public string startupPath { get; set; } public string appData { get; set; } public string osVersion { get; set; } + public bool isMonoRuntime { get; set; } public bool isMono { get; set; } public bool isLinux { get; set; } + public bool isOsx { get; set; } public bool isWindows { get; set; } public string branch { get; set; } - public bool authentication { get; set; } - public int startOfWeek { get; set; } + public string authentication { get; set; } + public string sqliteVersion { get; set; } public string urlBase { get; set; } + public string runtimeVersion { get; set; } } } \ No newline at end of file diff --git a/PlexRequests.Api/ApiRequest.cs b/PlexRequests.Api/ApiRequest.cs index f90aec7cf..f1a2c11d0 100644 --- a/PlexRequests.Api/ApiRequest.cs +++ b/PlexRequests.Api/ApiRequest.cs @@ -95,10 +95,17 @@ public T ExecuteXml(IRestRequest request, Uri baseUri) where T : class throw new ApplicationException(message, response.ErrorException); } - var json = JsonConvert.DeserializeObject(response.Content); - - return json; + try + { + var json = JsonConvert.DeserializeObject(response.Content); + return json; + } + catch (Exception e) + { + Log.Fatal(e); + throw; + } } public T DeserializeXml(string input) diff --git a/PlexRequests.Core/StatusChecker.cs b/PlexRequests.Core/StatusChecker.cs index 1512082f0..52b135312 100644 --- a/PlexRequests.Core/StatusChecker.cs +++ b/PlexRequests.Core/StatusChecker.cs @@ -55,7 +55,7 @@ public async Task GetLatestRelease() public StatusModel GetStatus() { - var assemblyVersion = AssemblyHelper.GetAssemblyVersion(); + var assemblyVersion = AssemblyHelper.GetProductVersion(); var model = new StatusModel { Version = assemblyVersion, @@ -66,7 +66,7 @@ public StatusModel GetStatus() var latestVersionArray = latestRelease.Result.Name.Split(new[] { 'v' }, StringSplitOptions.RemoveEmptyEntries); var latestVersion = latestVersionArray.Length > 1 ? latestVersionArray[1] : string.Empty; - if (!latestVersion.Equals(AssemblyHelper.GetProductVersion(), StringComparison.InvariantCultureIgnoreCase)) + if (!latestVersion.Equals(assemblyVersion, StringComparison.InvariantCultureIgnoreCase)) { model.UpdateAvailable = true; model.UpdateUri = latestRelease.Result.HtmlUrl; diff --git a/PlexRequests.UI/Content/requests.js b/PlexRequests.UI/Content/requests.js index 756283b3b..8c83f3328 100644 --- a/PlexRequests.UI/Content/requests.js +++ b/PlexRequests.UI/Content/requests.js @@ -136,7 +136,7 @@ $('#noteModal').on('show.bs.modal', function (event) { var modal = $(this); modal.find('.theNoteSaveButton').val(id); // Add ID to the button - var requestField = modal.find('input'); + var requestField = modal.find('.noteId'); requestField.val(id); // Add ID to the hidden field }); diff --git a/PlexRequests.UI/Modules/ApplicationTesterModule.cs b/PlexRequests.UI/Modules/ApplicationTesterModule.cs index 40d118f9e..2e6c719ad 100644 --- a/PlexRequests.UI/Modules/ApplicationTesterModule.cs +++ b/PlexRequests.UI/Modules/ApplicationTesterModule.cs @@ -118,7 +118,7 @@ private Response PlexTest() var settings = AuthSettings.GetSettings(); if (settings?.PlexAuthToken == null) { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "Plex is not setup yet, we need " }); + return Response.AsJson(new JsonResponseModel { Result = false, Message = "Plex is not setup yet, you need to update your Authentication settings" }); } try { diff --git a/PlexRequests.UI/Modules/LoginModule.cs b/PlexRequests.UI/Modules/LoginModule.cs index a983377c6..d24bf19bf 100644 --- a/PlexRequests.UI/Modules/LoginModule.cs +++ b/PlexRequests.UI/Modules/LoginModule.cs @@ -86,12 +86,14 @@ public LoginModule() Post["/register"] = x => { + var username = (string) Request.Form.Username; var exists = UserMapper.DoUsersExist(); if (exists) { - return Context.GetRedirect("~/register?error=true&username=" + (string)Request.Form.Username); + return Context.GetRedirect("~/register?error=true&username=" + username); } - var userId = UserMapper.CreateUser(Request.Form.Username, Request.Form.Password); + var userId = UserMapper.CreateUser(username, Request.Form.Password); + Session[SessionKeys.UsernameKey] = username; return this.LoginAndRedirect((Guid)userId); }; } diff --git a/PlexRequests.UI/Modules/RequestsModule.cs b/PlexRequests.UI/Modules/RequestsModule.cs index 239b98e4a..68c997560 100644 --- a/PlexRequests.UI/Modules/RequestsModule.cs +++ b/PlexRequests.UI/Modules/RequestsModule.cs @@ -61,7 +61,7 @@ public RequestsModule(IRepository service, ISettingsService ClearIssue((int)Request.Form.Id); Post["/changeavailability"] = _ => ChangeRequestAvailability((int)Request.Form.Id, (bool)Request.Form.Available); - Post["/addnote"] = _ => AddNote((int)Request.Form.Id, (string)Request.Form.noteArea); + Post["/addnote"] = _ => AddNote((int)Request.Form.requestId, (string)Request.Form.noteArea); } private IRepository Service { get; } @@ -213,7 +213,7 @@ private Response AddNote(int requestId, string noteArea) var originalRequest = Service.Get(requestId); if (originalRequest == null) { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to change the availability!" }); + return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to add a note!" }); } originalRequest.AdminNote = noteArea; diff --git a/PlexRequests.UI/Program.cs b/PlexRequests.UI/Program.cs index b85c99c73..b3c66e329 100644 --- a/PlexRequests.UI/Program.cs +++ b/PlexRequests.UI/Program.cs @@ -62,7 +62,7 @@ static void Main(string[] args) uri = $"http://localhost:{portResult}"; } - Log.Trace("Getting assembly version"); + Log.Trace("Getting product version"); WriteOutVersion(); var s = new Setup(); @@ -81,7 +81,7 @@ static void Main(string[] args) private static void WriteOutVersion() { - var assemblyVer = AssemblyHelper.GetAssemblyVersion(); + var assemblyVer = AssemblyHelper.GetProductVersion(); Log.Info($"Version: {assemblyVer}"); Console.WriteLine($"Version: {assemblyVer}"); } diff --git a/PlexRequests.UI/Views/Admin/_Sidebar.cshtml b/PlexRequests.UI/Views/Admin/_Sidebar.cshtml index b2e3da3c2..88726ee09 100644 --- a/PlexRequests.UI/Views/Admin/_Sidebar.cshtml +++ b/PlexRequests.UI/Views/Admin/_Sidebar.cshtml @@ -2,11 +2,11 @@
@if (Context.Request.Path == "/admin") { - Plex Request Settings + Plex Request } else { - Plex Request Settings + Plex Request } @if (Context.Request.Path == "/admin/authentication") { @@ -21,28 +21,28 @@ @if (Context.Request.Path == "/admin/plex") { - Plex Settings + Plex } else { - Plex Settings + Plex } @if (Context.Request.Path == "/admin/couchpotato") { - CouchPotato Settings + CouchPotato } else { - CouchPotato Settings + CouchPotato } @if (Context.Request.Path == "/admin/sonarr") { - Sonarr Settings + Sonarr } else { - Sonarr Settings + Sonarr } @*Sickbeard Settings*@ diff --git a/PlexRequests.UI/Views/Requests/Index.cshtml b/PlexRequests.UI/Views/Requests/Index.cshtml index 6aeadfb7f..7de26fd08 100644 --- a/PlexRequests.UI/Views/Requests/Index.cshtml +++ b/PlexRequests.UI/Views/Requests/Index.cshtml @@ -150,14 +150,14 @@
  • Wrong Content
  • Playback Issues
  • Other
  • + {{#if_eq admin true}} +
  • Add Note
  • + + {{/if_eq}}
    - {{#if_eq admin true}} - - - {{/if_eq}} @* // TODO add Issues to the view *@ @@ -195,7 +195,7 @@