- Added a visual indication on the UI to tell the admin there is a update available.

- We are now also recording the last scheduled run in the database
This commit is contained in:
tidusjar 2016-04-29 14:08:30 +01:00
commit df3dc4ac04
28 changed files with 392 additions and 45 deletions

View file

@ -87,8 +87,10 @@ namespace PlexRequests.UI
// Repo's
container.Register<IRepository<LogEntity>, GenericRepository<LogEntity>>();
container.Register<IRepository<ScheduledJobs>, GenericRepository<ScheduledJobs>>();
container.Register<IRequestService, JsonRequestService>();
container.Register<ISettingsRepository, SettingsJsonRepository>();
container.Register<IJobRecord, JobRecord>();
// Services
container.Register<IAvailabilityChecker, PlexAvailabilityChecker>();

View file

@ -220,3 +220,7 @@ label {
border-radius: 0 0.25rem 0.25rem 0 !important;
padding: 12px 8px; }
#updateAvailable {
background-color: #ffa400;
text-align: center; }

View file

@ -1 +1 @@
@media(min-width:768px){.row{position:relative;}.bottom-align-text{position:absolute;bottom:0;right:0;}}@media(max-width:48em){.home{padding-top:1rem;}}@media(min-width:48em){.home{padding-top:4rem;}}.btn{border-radius:.25rem !important;}.multiSelect{background-color:#4e5d6c;}.form-control-custom{background-color:#4e5d6c !important;color:#fff !important;border-radius:0;box-shadow:0 0 0 !important;}h1{font-size:3.5rem !important;font-weight:600 !important;}.request-title{margin-top:0 !important;font-size:1.9rem !important;}p{font-size:1.1rem !important;}label{display:inline-block !important;margin-bottom:.5rem !important;font-size:16px !important;}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{background:#4e5d6c;}.navbar .nav a .fa,.dropdown-menu a .fa{font-size:130%;top:1px;position:relative;display:inline-block;margin-right:5px;}.dropdown-menu a .fa{top:2px;}.btn-danger-outline{color:#d9534f !important;background-color:transparent;background-image:none;border-color:#d9534f !important;}.btn-danger-outline:focus,.btn-danger-outline.focus,.btn-danger-outline:active,.btn-danger-outline.active,.btn-danger-outline:hover,.open>.btn-danger-outline.dropdown-toggle{color:#fff !important;background-color:#d9534f !important;border-color:#d9534f !important;}.btn-primary-outline{color:#ff761b !important;background-color:transparent;background-image:none;border-color:#ff761b !important;}.btn-primary-outline:focus,.btn-primary-outline.focus,.btn-primary-outline:active,.btn-primary-outline.active,.btn-primary-outline:hover,.open>.btn-primary-outline.dropdown-toggle{color:#fff !important;background-color:#df691a !important;border-color:#df691a !important;}.btn-info-outline{color:#5bc0de !important;background-color:transparent;background-image:none;border-color:#5bc0de !important;}.btn-info-outline:focus,.btn-info-outline.focus,.btn-info-outline:active,.btn-info-outline.active,.btn-info-outline:hover,.open>.btn-info-outline.dropdown-toggle{color:#fff !important;background-color:#5bc0de !important;border-color:#5bc0de !important;}.btn-warning-outline{color:#f0ad4e !important;background-color:transparent;background-image:none;border-color:#f0ad4e !important;}.btn-warning-outline:focus,.btn-warning-outline.focus,.btn-warning-outline:active,.btn-warning-outline.active,.btn-warning-outline:hover,.open>.btn-warning-outline.dropdown-toggle{color:#fff !important;background-color:#f0ad4e !important;border-color:#f0ad4e !important;}.btn-success-outline{color:#5cb85c !important;background-color:transparent;background-image:none;border-color:#5cb85c !important;}.btn-success-outline:focus,.btn-success-outline.focus,.btn-success-outline:active,.btn-success-outline.active,.btn-success-outline:hover,.open>.btn-success-outline.dropdown-toggle{color:#fff !important;background-color:#5cb85c !important;border-color:#5cb85c !important;}#movieList .mix{display:none;}#tvList .mix{display:none;}.scroll-top-wrapper{position:fixed;opacity:0;visibility:hidden;overflow:hidden;text-align:center;z-index:99999999;background-color:#4e5d6c;color:#eee;width:50px;height:48px;line-height:48px;right:30px;bottom:30px;padding-top:2px;border-top-left-radius:10px;border-top-right-radius:10px;border-bottom-right-radius:10px;border-bottom-left-radius:10px;-webkit-transition:all .5s ease-in-out;-moz-transition:all .5s ease-in-out;-ms-transition:all .5s ease-in-out;-o-transition:all .5s ease-in-out;transition:all .5s ease-in-out;}.scroll-top-wrapper:hover{background-color:#637689;}.scroll-top-wrapper.show{visibility:visible;cursor:pointer;opacity:1;}.scroll-top-wrapper i.fa{line-height:inherit;}.no-search-results{text-align:center;}.no-search-results .no-search-results-icon{font-size:10em;color:#4e5d6c;}.no-search-results .no-search-results-text{margin:20px 0;color:#ccc;}.form-control-search{padding:13px 105px 13px 16px;height:100%;}.form-control-withbuttons{padding-right:105px;}.input-group-addon .btn-group{position:absolute;right:45px;z-index:3;top:13px;box-shadow:0 0 0;}.input-group-addon .btn-group .btn{border:1px solid rgba(255,255,255,.7) !important;padding:3px 12px;color:rgba(255,255,255,.7) !important;}.btn-split .btn{border-radius:0 !important;}.btn-split .btn:not(.dropdown-toggle){border-radius:.25rem 0 0 .25rem !important;}.btn-split .btn.dropdown-toggle{border-radius:0 .25rem .25rem 0 !important;padding:12px 8px;}
@media(min-width:768px){.row{position:relative;}.bottom-align-text{position:absolute;bottom:0;right:0;}}@media(max-width:48em){.home{padding-top:1rem;}}@media(min-width:48em){.home{padding-top:4rem;}}.btn{border-radius:.25rem !important;}.multiSelect{background-color:#4e5d6c;}.form-control-custom{background-color:#4e5d6c !important;color:#fff !important;border-radius:0;box-shadow:0 0 0 !important;}h1{font-size:3.5rem !important;font-weight:600 !important;}.request-title{margin-top:0 !important;font-size:1.9rem !important;}p{font-size:1.1rem !important;}label{display:inline-block !important;margin-bottom:.5rem !important;font-size:16px !important;}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{background:#4e5d6c;}.navbar .nav a .fa,.dropdown-menu a .fa{font-size:130%;top:1px;position:relative;display:inline-block;margin-right:5px;}.dropdown-menu a .fa{top:2px;}.btn-danger-outline{color:#d9534f !important;background-color:transparent;background-image:none;border-color:#d9534f !important;}.btn-danger-outline:focus,.btn-danger-outline.focus,.btn-danger-outline:active,.btn-danger-outline.active,.btn-danger-outline:hover,.open>.btn-danger-outline.dropdown-toggle{color:#fff !important;background-color:#d9534f !important;border-color:#d9534f !important;}.btn-primary-outline{color:#ff761b !important;background-color:transparent;background-image:none;border-color:#ff761b !important;}.btn-primary-outline:focus,.btn-primary-outline.focus,.btn-primary-outline:active,.btn-primary-outline.active,.btn-primary-outline:hover,.open>.btn-primary-outline.dropdown-toggle{color:#fff !important;background-color:#df691a !important;border-color:#df691a !important;}.btn-info-outline{color:#5bc0de !important;background-color:transparent;background-image:none;border-color:#5bc0de !important;}.btn-info-outline:focus,.btn-info-outline.focus,.btn-info-outline:active,.btn-info-outline.active,.btn-info-outline:hover,.open>.btn-info-outline.dropdown-toggle{color:#fff !important;background-color:#5bc0de !important;border-color:#5bc0de !important;}.btn-warning-outline{color:#f0ad4e !important;background-color:transparent;background-image:none;border-color:#f0ad4e !important;}.btn-warning-outline:focus,.btn-warning-outline.focus,.btn-warning-outline:active,.btn-warning-outline.active,.btn-warning-outline:hover,.open>.btn-warning-outline.dropdown-toggle{color:#fff !important;background-color:#f0ad4e !important;border-color:#f0ad4e !important;}.btn-success-outline{color:#5cb85c !important;background-color:transparent;background-image:none;border-color:#5cb85c !important;}.btn-success-outline:focus,.btn-success-outline.focus,.btn-success-outline:active,.btn-success-outline.active,.btn-success-outline:hover,.open>.btn-success-outline.dropdown-toggle{color:#fff !important;background-color:#5cb85c !important;border-color:#5cb85c !important;}#movieList .mix{display:none;}#tvList .mix{display:none;}.scroll-top-wrapper{position:fixed;opacity:0;visibility:hidden;overflow:hidden;text-align:center;z-index:99999999;background-color:#4e5d6c;color:#eee;width:50px;height:48px;line-height:48px;right:30px;bottom:30px;padding-top:2px;border-top-left-radius:10px;border-top-right-radius:10px;border-bottom-right-radius:10px;border-bottom-left-radius:10px;-webkit-transition:all .5s ease-in-out;-moz-transition:all .5s ease-in-out;-ms-transition:all .5s ease-in-out;-o-transition:all .5s ease-in-out;transition:all .5s ease-in-out;}.scroll-top-wrapper:hover{background-color:#637689;}.scroll-top-wrapper.show{visibility:visible;cursor:pointer;opacity:1;}.scroll-top-wrapper i.fa{line-height:inherit;}.no-search-results{text-align:center;}.no-search-results .no-search-results-icon{font-size:10em;color:#4e5d6c;}.no-search-results .no-search-results-text{margin:20px 0;color:#ccc;}.form-control-search{padding:13px 105px 13px 16px;height:100%;}.form-control-withbuttons{padding-right:105px;}.input-group-addon .btn-group{position:absolute;right:45px;z-index:3;top:13px;box-shadow:0 0 0;}.input-group-addon .btn-group .btn{border:1px solid rgba(255,255,255,.7) !important;padding:3px 12px;color:rgba(255,255,255,.7) !important;}.btn-split .btn{border-radius:0 !important;}.btn-split .btn:not(.dropdown-toggle){border-radius:.25rem 0 0 .25rem !important;}.btn-split .btn.dropdown-toggle{border-radius:0 .25rem .25rem 0 !important;padding:12px 8px;}#updateAvailable{background-color:#ffa400;text-align:center;}

View file

@ -7,8 +7,7 @@ $warning-colour: #f0ad4e;
$danger-colour: #d9534f;
$success-colour: #5cb85c;
$i:
!important
;
!important;
@media (min-width: 768px ) {
.row {
@ -279,4 +278,9 @@ $border-radius: 10px;
.btn-split .btn.dropdown-toggle {
border-radius: 0 .25rem .25rem 0 $i;
padding: 12px 8px;
}
#updateAvailable {
background-color: rgb(255, 164, 0);
text-align: center;
}

View file

@ -36,7 +36,7 @@ using Quartz.Spi;
namespace PlexRequests.UI.Jobs
{
/// <summary>
/// The custom job factory we are using so we are able to use our IoC container with DI in our Jobs.
/// The custom job factory we are using so we are able to use our IoC container with DI in our JobNames.
/// </summary>
/// <seealso cref="Quartz.Spi.IJobFactory" />
public class CustomJobFactory : IJobFactory

View file

@ -0,0 +1,33 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: JsonUpdateAvailableModel.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.UI.Models
{
public class JsonUpdateAvailableModel
{
public bool UpdateAvailable { get; set; }
}
}

View file

@ -295,8 +295,8 @@ namespace PlexRequests.UI.Modules
}
var result = CpService.SaveSettings(couchPotatoSettings);
return Response.AsJson(result
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for CouchPotato!" }
return Response.AsJson(result
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for CouchPotato!" }
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
}
@ -429,7 +429,7 @@ namespace PlexRequests.UI.Modules
finally
{
NotificationService.UnSubscribe(new EmailMessageNotification(EmailService));
}
}
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Email Notification!" });
}
@ -464,7 +464,7 @@ namespace PlexRequests.UI.Modules
{
var checker = new StatusChecker();
var status = checker.GetStatus();
var md = new Markdown();
var md = new Markdown(new MarkdownOptions { AutoNewLines = true });
status.ReleaseNotes = md.Transform(status.ReleaseNotes);
return View["Status", status];
}
@ -623,7 +623,7 @@ namespace PlexRequests.UI.Modules
{
JsonSettings.MaxJsonLength = int.MaxValue;
var allLogs = LogsRepo.GetAll().OrderByDescending(x => x.Id).Take(200);
var model = new DatatablesModel<LogEntity> {Data = new List<LogEntity>()};
var model = new DatatablesModel<LogEntity> { Data = new List<LogEntity>() };
foreach (var l in allLogs)
{
l.DateString = l.Date.ToString("G");
@ -650,7 +650,7 @@ namespace PlexRequests.UI.Modules
settings.Level = level;
LogService.SaveSettings(settings);
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"The new log level is now {newLevel}"});
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"The new log level is now {newLevel}" });
}
private Negotiator Headphones()
@ -673,7 +673,7 @@ namespace PlexRequests.UI.Modules
Log.Trace(settings.DumpJson());
var result = HeadphonesService.SaveSettings(settings);
Log.Info("Saved headphones settings, result: {0}", result);
return Response.AsJson(result
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Headphones!" }

View file

@ -30,8 +30,11 @@ using Nancy.Extensions;
using PlexRequests.UI.Models;
using System;
using Nancy.Security;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers;
namespace PlexRequests.UI.Modules
{
@ -52,6 +55,8 @@ namespace PlexRequests.UI.Modules
}
}
protected bool IsAdmin => Context.CurrentUser.IsAuthenticated();
protected int DateTimeOffset
{
get
@ -87,5 +92,7 @@ namespace PlexRequests.UI.Modules
: null;
}
}
}

View file

@ -124,14 +124,6 @@ namespace PlexRequests.UI.Modules
private IHeadphonesApi HeadphonesApi { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
private bool IsAdmin
{
get
{
return Context.CurrentUser.IsAuthenticated();
}
}
private Negotiator RequestLoad()
{
var settings = PrService.GetSettings();
@ -626,7 +618,7 @@ namespace PlexRequests.UI.Modules
Status = showInfo.status,
RequestedDate = DateTime.UtcNow,
Approved = false,
RequestedUsers = new List<string>() { Username },
RequestedUsers = new List<string> { Username },
Issues = IssueState.None,
ImdbId = showInfo.externals?.imdb ?? string.Empty,
SeasonCount = showInfo.seasonCount
@ -802,7 +794,7 @@ namespace PlexRequests.UI.Modules
}
var sender = new HeadphonesSender(HeadphonesApi, hpSettings, RequestService);
sender.AddAlbum(model);
sender.AddAlbum(model).Wait();
model.Approved = true;
RequestService.AddRequest(model);

View file

@ -0,0 +1,79 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UpdateCheckerModule.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;
using NLog;
using PlexRequests.Core;
using PlexRequests.Helpers;
using PlexRequests.UI.Models;
namespace PlexRequests.UI.Modules
{
public class UpdateCheckerModule : BaseAuthModule
{
public UpdateCheckerModule(ICacheProvider provider) : base("updatechecker")
{
Cache = provider;
Get["/"] = _ => CheckLatestVersion();
}
private ICacheProvider Cache { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
private Response CheckLatestVersion()
{
try
{
if (!IsAdmin)
{
return Response.AsJson(new JsonUpdateAvailableModel { UpdateAvailable = false });
}
var checker = new StatusChecker();
var release = Cache.GetOrSet(CacheKeys.LastestProductVersion, () => checker.GetStatus(), 30);
if (release.UpdateAvailable)
{
return Response.AsJson(new JsonUpdateAvailableModel { UpdateAvailable = true});
}
return Response.AsJson(new JsonUpdateAvailableModel { UpdateAvailable = false });
}
catch (Exception e)
{
Log.Warn("Exception Thrown when attempting to check the status");
Log.Warn(e);
return Response.AsJson(new JsonUpdateAvailableModel { UpdateAvailable = false });
}
}
}
}

View file

@ -185,12 +185,14 @@
<Compile Include="Jobs\CustomJobFactory.cs" />
<Compile Include="Jobs\Scheduler.cs" />
<Compile Include="Models\DatatablesModel.cs" />
<Compile Include="Models\JsonUpdateAvailableModel.cs" />
<Compile Include="Models\MovieSearchType.cs" />
<Compile Include="Models\QualityModel.cs" />
<Compile Include="Models\SearchViewModel.cs" />
<Compile Include="Models\SearchMusicViewModel.cs" />
<Compile Include="Models\SearchMovieViewModel.cs" />
<Compile Include="Modules\BaseModule.cs" />
<Compile Include="Modules\UpdateCheckerModule.cs" />
<Compile Include="Start\StartupOptions.cs" />
<Compile Include="Validators\HeadphonesValidator.cs" />
<Compile Include="Validators\PushoverSettingsValidator.cs" />

View file

@ -34,7 +34,7 @@
</button>
<a class="navbar-brand" href="@url/search">Plex Requests</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
@Html.GetNavbarUrl(Context, "/search", "Search", "search")
@ -42,7 +42,7 @@
</ul>
<ul class="nav navbar-nav navbar-right">
@if (!Context.CurrentUser.IsAuthenticated())
{
<li><a href="@url/login"><i class="fa fa-user"></i> Admin</a></li>
@ -66,6 +66,7 @@
</ul>
</div>
</div>
<div id="updateAvailable" hidden="hidden"></div>
</nav>
<div class="container">
@ -82,6 +83,24 @@
$(function () {
var urlBase = '@Html.GetBaseUrl()';
var url = createBaseUrl(urlBase, '/updatechecker');
$.ajax({
type: "GET",
url: url,
dataType: "json",
success: function (response) {
if (response.updateAvailable) {
var status = createBaseUrl(urlBase, '/admin/status');
$('#updateAvailable').html("There is a new update available! Click <a style='color: white' href='"+status+"'>Here!</a>");
$('#updateAvailable').removeAttr("hidden");
}
},
error: function (e) {
console.log(e);
}
});
$(document).on('scroll', function () {
if ($(window).scrollTop() > 100) {