finished #646 and fixed #664

This commit is contained in:
Jamie.Rees 2016-11-14 13:16:02 +00:00
commit 55f1309140
41 changed files with 494 additions and 261 deletions

View file

@ -23,37 +23,30 @@ namespace PlexRequests.Core.Migration
{ {
var con = Db.DbConnection(); var con = Db.DbConnection();
var versions = GetMigrations(); var versions = GetMigrations();
var dbVersion = con.GetVersionInfo().OrderByDescending(x => x.Version).FirstOrDefault(); var dbVersion = con.GetVersionInfo().OrderByDescending(x => x.Version).FirstOrDefault() ??
if (dbVersion == null) new TableCreation.VersionInfo { Version = 0 };
{
dbVersion = new TableCreation.VersionInfo { Version = 0 };
}
foreach (var v in versions) foreach (var v in versions)
{ {
#if !DEBUG
if (v.Value.Version > dbVersion.Version) if (v.Value.Version > dbVersion.Version)
{ {
// Assuming only one constructor #endif
var ctor = v.Key.GetConstructors().FirstOrDefault(); // Assuming only one constructor
var dependencies = new List<object>(); var ctor = v.Key.GetConstructors().FirstOrDefault();
var dependencies = ctor.GetParameters().Select(param => Kernel.Get(param.ParameterType)).ToList();
foreach (var param in ctor.GetParameters()) var method = v.Key.GetMethod("Start");
{ if (method != null)
var dep = Kernel.Get(param.ParameterType); {
dependencies.Add(dep); var classInstance = Activator.CreateInstance(v.Key, dependencies.Any() ? dependencies.ToArray() : null);
} var parametersArray = new object[] { Db.DbConnection() };
var method = v.Key.GetMethod("Start"); method.Invoke(classInstance, parametersArray);
if (method != null)
{
object result = null;
var classInstance = Activator.CreateInstance(v.Key, dependencies.Any() ? dependencies.ToArray() : null);
var parametersArray = new object[] { Db.DbConnection() };
method.Invoke(classInstance, parametersArray);
}
} }
#if !DEBUG
}
#endif
} }
} }

View file

@ -27,7 +27,10 @@
using System; using System;
using System.Data; using System.Data;
using NLog;
using System.Linq; using System.Linq;
using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers;
using PlexRequests.Helpers.Permissions; using PlexRequests.Helpers.Permissions;
using PlexRequests.Store; using PlexRequests.Store;
using PlexRequests.Store.Repository; using PlexRequests.Store.Repository;
@ -37,14 +40,16 @@ namespace PlexRequests.Core.Migration.Migrations
[Migration(11000, "v1.10.0.0")] [Migration(11000, "v1.10.0.0")]
public class Version1100 : BaseMigration, IMigration public class Version1100 : BaseMigration, IMigration
{ {
public Version1100(IUserRepository userRepo, IRequestService requestService) public Version1100(IUserRepository userRepo, IRequestService requestService, ISettingsService<LogSettings> log)
{ {
UserRepo = userRepo; UserRepo = userRepo;
RequestService = requestService; RequestService = requestService;
Log = log;
} }
public int Version => 11000; public int Version => 11000;
private IUserRepository UserRepo { get; } private IUserRepository UserRepo { get; }
private IRequestService RequestService { get; } private IRequestService RequestService { get; }
private ISettingsService<LogSettings> Log { get; }
public void Start(IDbConnection con) public void Start(IDbConnection con)
{ {
@ -52,22 +57,25 @@ namespace PlexRequests.Core.Migration.Migrations
// Update the current admin permissions set // Update the current admin permissions set
UpdateAdmin(); UpdateAdmin();
ResetLogLevel();
UpdateSchema(con, Version); UpdateSchema(con, Version);
} }
private void ResetLogLevel()
{
var logSettings = Log.GetSettings();
logSettings.Level = LogLevel.Error.Ordinal;
Log.SaveSettings(logSettings);
LoggingHelper.ReconfigureLogLevel(LogLevel.FromOrdinal(logSettings.Level));
}
private void UpdateDb(IDbConnection con) private void UpdateDb(IDbConnection con)
{ {
// Create the two new columns // Create the two new columns
con.AlterTable("Users", "ADD", "Permissions", true, "INTEGER"); con.AlterTable("Users", "ADD", "Permissions", true, "INTEGER");
con.AlterTable("Users", "ADD", "Features", true, "INTEGER"); con.AlterTable("Users", "ADD", "Features", true, "INTEGER");
// Add the new 'running' item into the scheduled jobs so we can check if the cachers are running
con.AlterTable("ScheduledJobs", "ADD", "Running", true, "INTEGER");
//https://image.tmdb.org/t/p/w150/https://image.tmdb.org/t/p/w150//aqhAqttDq7zgsTaBHtCD8wmTk6k.jpg //https://image.tmdb.org/t/p/w150/https://image.tmdb.org/t/p/w150//aqhAqttDq7zgsTaBHtCD8wmTk6k.jpg
// UI = https://image.tmdb.org/t/p/w150/{{posterPath}} // UI = https://image.tmdb.org/t/p/w150/{{posterPath}}

View file

@ -45,6 +45,10 @@
<Reference Include="Ninject"> <Reference Include="Ninject">
<HintPath>..\packages\Ninject.3.2.0.0\lib\net45-full\Ninject.dll</HintPath> <HintPath>..\packages\Ninject.3.2.0.0\lib\net45-full\Ninject.dll</HintPath>
</Reference> </Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.3.11\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Quartz, Version=2.3.3.0, Culture=neutral, PublicKeyToken=f6b8c98a402cc8a4, processorArchitecture=MSIL"> <Reference Include="Quartz, Version=2.3.3.0, Culture=neutral, PublicKeyToken=f6b8c98a402cc8a4, processorArchitecture=MSIL">
<HintPath>..\packages\Quartz.2.3.3\lib\net40\Quartz.dll</HintPath> <HintPath>..\packages\Quartz.2.3.3\lib\net40\Quartz.dll</HintPath>
<Private>True</Private> <Private>True</Private>

View file

@ -2,5 +2,6 @@
<packages> <packages>
<package id="Common.Logging" version="3.0.0" targetFramework="net45" /> <package id="Common.Logging" version="3.0.0" targetFramework="net45" />
<package id="Common.Logging.Core" version="3.0.0" targetFramework="net45" /> <package id="Common.Logging.Core" version="3.0.0" targetFramework="net45" />
<package id="NLog" version="4.3.11" targetFramework="net45" />
<package id="Quartz" version="2.3.3" targetFramework="net45" /> <package id="Quartz" version="2.3.3" targetFramework="net45" />
</packages> </packages>

View file

@ -60,52 +60,13 @@ namespace PlexRequests.Core
// Shrink DB // Shrink DB
TableCreation.Vacuum(Db.DbConnection()); TableCreation.Vacuum(Db.DbConnection());
} }
// Add the new 'running' item into the scheduled jobs so we can check if the cachers are running
// The below code is obsolete, we should use PlexRequests.Core.Migrations.MigrationRunner Db.DbConnection().AlterTable("ScheduledJobs", "ADD", "Running", true, "INTEGER");
var version = CheckSchema();
if (version > 0)
{
if (version > 1899 && version <= 1900)
{
MigrateToVersion1900();
}
if(version > 1899 && version <= 1910)
{
MigrateToVersion1910();
}
}
return Db.DbConnection().ConnectionString; return Db.DbConnection().ConnectionString;
} }
public static string ConnectionString => Db.DbConnection().ConnectionString;
private int CheckSchema()
{
var productVersion = AssemblyHelper.GetProductVersion();
var trimStatus = new Regex("[^0-9]", RegexOptions.Compiled).Replace(productVersion, string.Empty).PadRight(4, '0');
var version = int.Parse(trimStatus);
var connection = Db.DbConnection();
var schema = connection.GetSchemaVersion();
if (schema == null)
{
connection.CreateSchema(version); // Set the default.
schema = connection.GetSchemaVersion();
}
if (version > schema.SchemaVersion)
{
Db.DbConnection().UpdateSchemaVersion(version);
schema = connection.GetSchemaVersion();
}
version = schema.SchemaVersion;
return version;
}
private void CreateDefaultSettingsPage(string baseUrl) private void CreateDefaultSettingsPage(string baseUrl)
{ {
var defaultSettings = new PlexRequestSettings var defaultSettings = new PlexRequestSettings
@ -148,7 +109,6 @@ namespace PlexRequests.Core
Task.Run(() => { CacheSonarrQualityProfiles(mc); }); Task.Run(() => { CacheSonarrQualityProfiles(mc); });
Task.Run(() => { CacheCouchPotatoQualityProfiles(mc); }); Task.Run(() => { CacheCouchPotatoQualityProfiles(mc); });
// we don't need to cache sickrage profiles, those are static // we don't need to cache sickrage profiles, those are static
// TODO: cache headphones profiles?
} }
catch (Exception) catch (Exception)
{ {
@ -156,12 +116,12 @@ namespace PlexRequests.Core
} }
} }
private void CacheSonarrQualityProfiles(MemoryCacheProvider cacheProvider) private void CacheSonarrQualityProfiles(ICacheProvider cacheProvider)
{ {
try try
{ {
Log.Info("Executing GetSettings call to Sonarr for quality profiles"); Log.Info("Executing GetSettings call to Sonarr for quality profiles");
var sonarrSettingsService = new SettingsServiceV2<SonarrSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider())); var sonarrSettingsService = new SettingsServiceV2<SonarrSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), cacheProvider));
var sonarrSettings = sonarrSettingsService.GetSettings(); var sonarrSettings = sonarrSettingsService.GetSettings();
if (sonarrSettings.Enabled) if (sonarrSettings.Enabled)
{ {
@ -178,12 +138,12 @@ namespace PlexRequests.Core
} }
} }
private void CacheCouchPotatoQualityProfiles(MemoryCacheProvider cacheProvider) private void CacheCouchPotatoQualityProfiles(ICacheProvider cacheProvider)
{ {
try try
{ {
Log.Info("Executing GetSettings call to CouchPotato for quality profiles"); Log.Info("Executing GetSettings call to CouchPotato for quality profiles");
var cpSettingsService = new SettingsServiceV2<CouchPotatoSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider())); var cpSettingsService = new SettingsServiceV2<CouchPotatoSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), cacheProvider));
var cpSettings = cpSettingsService.GetSettings(); var cpSettings = cpSettingsService.GetSettings();
if (cpSettings.Enabled) if (cpSettings.Enabled)
{ {
@ -199,102 +159,5 @@ namespace PlexRequests.Core
Log.Error(ex, "Failed to cache CouchPotato quality profiles!"); Log.Error(ex, "Failed to cache CouchPotato quality profiles!");
} }
} }
/// <summary>
/// Migrates to version 1.9.
/// Move the Plex auth token to the new field.
/// Reconfigure the log level
/// Set the wizard flag to true if we already have settings
/// </summary>
public void MigrateToVersion1900()
{
// Need to change the Plex Token location
var authSettings = new SettingsServiceV2<AuthenticationSettings>(new SettingsJsonRepository(Db, new MemoryCacheProvider()));
var auth = authSettings.GetSettings();
var plexSettings = new SettingsServiceV2<PlexSettings>(new SettingsJsonRepository(Db, new MemoryCacheProvider()));
if (auth != null)
{
//If we have an authToken we do not need to go through the setup
if (!string.IsNullOrEmpty(auth.OldPlexAuthToken))
{
var prServuce = new SettingsServiceV2<PlexRequestSettings>(new SettingsJsonRepository(Db, new MemoryCacheProvider()));
var settings = prServuce.GetSettings();
settings.Wizard = true;
prServuce.SaveSettings(settings);
}
// Clear out the old token and save it to the new field
var currentSettings = plexSettings.GetSettings();
if (!string.IsNullOrEmpty(auth.OldPlexAuthToken))
{
currentSettings.PlexAuthToken = auth.OldPlexAuthToken;
plexSettings.SaveSettings(currentSettings);
// Clear out the old value
auth.OldPlexAuthToken = string.Empty;
authSettings.SaveSettings(auth);
}
}
// Set the log level
try
{
var settingsService = new SettingsServiceV2<LogSettings>(new SettingsJsonRepository(Db, new MemoryCacheProvider()));
var logSettings = settingsService.GetSettings();
logSettings.Level = LogLevel.Error.Ordinal;
settingsService.SaveSettings(logSettings);
LoggingHelper.ReconfigureLogLevel(LogLevel.FromOrdinal(logSettings.Level));
}
catch (Exception e)
{
Log.Error(e);
}
// Enable analytics;
try
{
var prSettings = new SettingsServiceV2<PlexRequestSettings>(new SettingsJsonRepository(Db, new MemoryCacheProvider()));
var settings = prSettings.GetSettings();
settings.CollectAnalyticData = true;
var updated = prSettings.SaveSettings(settings);
}
catch (Exception e)
{
Log.Error(e);
}
}
/// <summary>
/// Migrates to version1910.
/// </summary>
public void MigrateToVersion1910()
{
try
{
// Get the new machine Identifier
var settings = new SettingsServiceV2<PlexSettings>(new SettingsJsonRepository(Db, new MemoryCacheProvider()));
var plex = settings.GetSettings();
if (!string.IsNullOrEmpty(plex.PlexAuthToken))
{
var api = new PlexApi(new ApiRequest());
var server = api.GetServer(plex.PlexAuthToken); // Get the server info
plex.MachineIdentifier = server.Server.FirstOrDefault(x => x.AccessToken == plex.PlexAuthToken)?.MachineIdentifier;
settings.SaveSettings(plex); // Save the new settings
}
}
catch (Exception e)
{
Log.Error(e);
}
}
} }
} }

View file

@ -65,6 +65,7 @@ namespace PlexRequests.Services.Jobs
var settings = CpSettings.GetSettings(); var settings = CpSettings.GetSettings();
if (settings.Enabled) if (settings.Enabled)
{ {
Job.SetRunning(true, JobNames.CpCacher);
Log.Trace("Getting all movies from CouchPotato"); Log.Trace("Getting all movies from CouchPotato");
try try
{ {
@ -109,7 +110,6 @@ namespace PlexRequests.Services.Jobs
public void Execute(IJobExecutionContext context) public void Execute(IJobExecutionContext context)
{ {
Job.SetRunning(true, JobNames.CpCacher);
Queued(); Queued();
} }
} }

View file

@ -87,7 +87,6 @@ namespace PlexRequests.Services.Jobs
Log.Debug("Validation of the plex settings failed."); Log.Debug("Validation of the plex settings failed.");
return; return;
} }
var libraries = CachedLibraries(plexSettings, true); //force setting the cache (10 min intervals via scheduler) var libraries = CachedLibraries(plexSettings, true); //force setting the cache (10 min intervals via scheduler)
if (libraries == null || !libraries.Any()) if (libraries == null || !libraries.Any())
@ -156,9 +155,6 @@ namespace PlexRequests.Services.Jobs
NotificationEngine.NotifyUsers(modifiedModel, plexSettings.PlexAuthToken, NotificationType.RequestAvailable); NotificationEngine.NotifyUsers(modifiedModel, plexSettings.PlexAuthToken, NotificationType.RequestAvailable);
RequestService.BatchUpdate(modifiedModel); RequestService.BatchUpdate(modifiedModel);
} }
Job.Record(JobNames.PlexChecker);
Job.SetRunning(false, JobNames.CpCacher);
} }
public List<PlexMovie> GetPlexMovies() public List<PlexMovie> GetPlexMovies()
@ -504,7 +500,7 @@ namespace PlexRequests.Services.Jobs
public void Execute(IJobExecutionContext context) public void Execute(IJobExecutionContext context)
{ {
Job.SetRunning(true, JobNames.CpCacher); Job.SetRunning(true, JobNames.PlexChecker);
try try
{ {
CheckAndUpdateAll(); CheckAndUpdateAll();
@ -513,6 +509,11 @@ namespace PlexRequests.Services.Jobs
{ {
Log.Error(e); Log.Error(e);
} }
finally
{
Job.Record(JobNames.PlexChecker);
Job.SetRunning(false, JobNames.PlexChecker);
}
} }
} }
} }

View file

@ -145,7 +145,7 @@ namespace PlexRequests.Services.Jobs
public void Execute(IJobExecutionContext context) public void Execute(IJobExecutionContext context)
{ {
Job.SetRunning(true, JobNames.CpCacher);
try try
{ {
var s = Plex.GetSettings(); var s = Plex.GetSettings();
@ -163,6 +163,7 @@ namespace PlexRequests.Services.Jobs
return; return;
} }
} }
Job.SetRunning(true, JobNames.EpisodeCacher);
CacheEpisodes(s); CacheEpisodes(s);
} }
catch (Exception e) catch (Exception e)
@ -172,7 +173,7 @@ namespace PlexRequests.Services.Jobs
finally finally
{ {
Job.Record(JobNames.EpisodeCacher); Job.Record(JobNames.EpisodeCacher);
Job.SetRunning(false, JobNames.CpCacher); Job.SetRunning(false, JobNames.EpisodeCacher);
} }
} }
} }

View file

@ -78,8 +78,6 @@ namespace PlexRequests.Services.Jobs
public void Execute(IJobExecutionContext context) public void Execute(IJobExecutionContext context)
{ {
JobRecord.SetRunning(true, JobNames.CpCacher);
try try
{ {
var settings = NewsletterSettings.GetSettings(); var settings = NewsletterSettings.GetSettings();
@ -87,7 +85,7 @@ namespace PlexRequests.Services.Jobs
{ {
return; return;
} }
JobRecord.SetRunning(true, JobNames.RecentlyAddedEmail);
Start(settings); Start(settings);
} }
catch (Exception e) catch (Exception e)
@ -97,7 +95,7 @@ namespace PlexRequests.Services.Jobs
finally finally
{ {
JobRecord.Record(JobNames.RecentlyAddedEmail); JobRecord.Record(JobNames.RecentlyAddedEmail);
JobRecord.SetRunning(false, JobNames.CpCacher); JobRecord.SetRunning(false, JobNames.RecentlyAddedEmail);
} }
} }
@ -264,7 +262,8 @@ namespace PlexRequests.Services.Jobs
{ {
foreach (var user in users.User) foreach (var user in users.User)
{ {
message.Bcc.Add(new MailboxAddress(user.Username, user.Email)); if (user.Email != null)
message.Bcc.Add(new MailboxAddress(user.Username, user.Email));
} }
} }
} }

View file

@ -58,12 +58,14 @@ namespace PlexRequests.Services.Jobs
public void Queued() public void Queued()
{ {
Job.SetRunning(true, JobNames.CpCacher);
Log.Trace("Getting the settings"); Log.Trace("Getting the settings");
var settings = SrSettings.GetSettings(); var settings = SrSettings.GetSettings();
if (settings.Enabled) if (settings.Enabled)
{ {
Job.SetRunning(true, JobNames.SrCacher);
Log.Trace("Getting all shows from SickRage"); Log.Trace("Getting all shows from SickRage");
try try
{ {
@ -80,7 +82,7 @@ namespace PlexRequests.Services.Jobs
finally finally
{ {
Job.Record(JobNames.SrCacher); Job.Record(JobNames.SrCacher);
Job.SetRunning(false, JobNames.CpCacher); Job.SetRunning(false, JobNames.SrCacher);
} }
} }
} }

View file

@ -62,10 +62,10 @@ namespace PlexRequests.Services.Jobs
public void Queued() public void Queued()
{ {
Job.SetRunning(true, JobNames.CpCacher);
var settings = SonarrSettings.GetSettings(); var settings = SonarrSettings.GetSettings();
if (settings.Enabled) if (settings.Enabled)
{ {
Job.SetRunning(true, JobNames.SonarrCacher);
try try
{ {
var series = SonarrApi.GetSeries(settings.ApiKey, settings.FullUri); var series = SonarrApi.GetSeries(settings.ApiKey, settings.FullUri);
@ -81,7 +81,7 @@ namespace PlexRequests.Services.Jobs
finally finally
{ {
Job.Record(JobNames.SonarrCacher); Job.Record(JobNames.SonarrCacher);
Job.SetRunning(false, JobNames.CpCacher); Job.SetRunning(false, JobNames.SonarrCacher);
} }
} }
} }

View file

@ -182,3 +182,9 @@ button.list-group-item:focus {
#sidebar-wrapper { #sidebar-wrapper {
background: #252424; } background: #252424; }
#cacherRunning {
background-color: #333333;
text-align: center;
font-size: 15px;
padding: 3px 0; }

View file

@ -1 +1 @@
.form-control-custom{background-color:#333 !important;}.form-control-custom-disabled{background-color:#252424 !important;}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{background:#df691a;}.scroll-top-wrapper{background-color:#333;}.scroll-top-wrapper:hover{background-color:#df691a;}body{font-family:Open Sans Regular,Helvetica Neue,Helvetica,Arial,sans-serif;color:#eee;background-color:#1f1f1f;}.table-striped>tbody>tr:nth-of-type(odd){background-color:#333;}.table-hover>tbody>tr:hover{background-color:#282828;}fieldset{padding:15px;}legend{border-bottom:1px solid #333;}.form-control{color:#fefefe;background-color:#333;}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{margin-left:-0;}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:-15px;}.dropdown-menu{background-color:#282828;}.dropdown-menu .divider{background-color:#333;}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-color:#333;}.input-group-addon{background-color:#333;}.nav>li>a:hover,.nav>li>a:focus{background-color:#df691a;}.nav-tabs>li>a:hover{border-color:#df691a #df691a transparent;}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{background-color:#df691a;border:1px solid #df691a;}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #df691a;}.navbar-default{background-color:#0a0a0a;}.navbar-default .navbar-brand{color:#df691a;}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#f0ad4e;background-color:#282828;}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{background-color:#282828;}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#df691a;color:#fff;}.pagination>li>a,.pagination>li>span{background-color:#282828;}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{background-color:#333;}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#fefefe;background-color:#333;}.list-group-item{background-color:#282828;}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{background-color:#333;}.input-addon,.input-group-addon{color:#df691a;}.modal-header,.modal-footer{background-color:#282828;}.modal-content{position:relative;background-color:#282828;border:1px solid transparent;border-radius:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;outline:0;}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:300;color:#ebebeb;line-height:1;vertical-align:middle;white-space:nowrap;text-align:center;background-color:#333;border-radius:10px;}.bootstrap-datetimepicker-widget.dropdown-menu{background-color:#333;}.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after{border-bottom:6px solid #333 !important;}#sidebar-wrapper{background:#252424;} .form-control-custom{background-color:#333 !important;}.form-control-custom-disabled{background-color:#252424 !important;}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{background:#df691a;}.scroll-top-wrapper{background-color:#333;}.scroll-top-wrapper:hover{background-color:#df691a;}body{font-family:Open Sans Regular,Helvetica Neue,Helvetica,Arial,sans-serif;color:#eee;background-color:#1f1f1f;}.table-striped>tbody>tr:nth-of-type(odd){background-color:#333;}.table-hover>tbody>tr:hover{background-color:#282828;}fieldset{padding:15px;}legend{border-bottom:1px solid #333;}.form-control{color:#fefefe;background-color:#333;}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{margin-left:-0;}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:-15px;}.dropdown-menu{background-color:#282828;}.dropdown-menu .divider{background-color:#333;}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-color:#333;}.input-group-addon{background-color:#333;}.nav>li>a:hover,.nav>li>a:focus{background-color:#df691a;}.nav-tabs>li>a:hover{border-color:#df691a #df691a transparent;}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{background-color:#df691a;border:1px solid #df691a;}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #df691a;}.navbar-default{background-color:#0a0a0a;}.navbar-default .navbar-brand{color:#df691a;}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#f0ad4e;background-color:#282828;}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{background-color:#282828;}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#df691a;color:#fff;}.pagination>li>a,.pagination>li>span{background-color:#282828;}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{background-color:#333;}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#fefefe;background-color:#333;}.list-group-item{background-color:#282828;}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{background-color:#333;}.input-addon,.input-group-addon{color:#df691a;}.modal-header,.modal-footer{background-color:#282828;}.modal-content{position:relative;background-color:#282828;border:1px solid transparent;border-radius:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;outline:0;}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:300;color:#ebebeb;line-height:1;vertical-align:middle;white-space:nowrap;text-align:center;background-color:#333;border-radius:10px;}.bootstrap-datetimepicker-widget.dropdown-menu{background-color:#333;}.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after{border-bottom:6px solid #333 !important;}#sidebar-wrapper{background:#252424;}#cacherRunning{background-color:#333;text-align:center;font-size:15px;padding:3px 0;}

View file

@ -228,3 +228,10 @@ button.list-group-item:focus {
#sidebar-wrapper { #sidebar-wrapper {
background: $bg-colour-disabled; background: $bg-colour-disabled;
} }
#cacherRunning {
background-color: $bg-colour;
text-align: center;
font-size: 15px;
padding: 3px 0;
}

View file

@ -257,6 +257,12 @@ label {
font-size: 15px; font-size: 15px;
padding: 3px 0; } padding: 3px 0; }
#cacherRunning {
background-color: #4e5d6c;
text-align: center;
font-size: 15px;
padding: 3px 0; }
.checkbox label { .checkbox label {
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;

File diff suppressed because one or more lines are too long

View file

@ -327,6 +327,13 @@ $border-radius: 10px;
padding: 3px 0; padding: 3px 0;
} }
#cacherRunning {
background-color: $form-color;
text-align: center;
font-size: 15px;
padding: 3px 0;
}
.checkbox label { .checkbox label {
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;

View file

@ -25,6 +25,8 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using Nancy;
using Nancy.Linker;
using Nancy.Security; using Nancy.Security;
using Nancy.ViewEngines.Razor; using Nancy.ViewEngines.Razor;
using Ninject; using Ninject;
@ -41,22 +43,37 @@ namespace PlexRequests.UI.Helpers
get get
{ {
var userRepo = ServiceLocator.Instance.Resolve<IUserRepository>(); var userRepo = ServiceLocator.Instance.Resolve<IUserRepository>();
return _security ?? (_security = new SecurityExtensions(userRepo, null)); var linker = ServiceLocator.Instance.Resolve<IResourceLinker>();
return _security ?? (_security = new SecurityExtensions(userRepo, null, linker));
} }
} }
private static SecurityExtensions _security; private static SecurityExtensions _security;
public static bool HasAnyPermission(this HtmlHelpers helper, int permission) public static bool HasAnyPermission(this HtmlHelpers helper, int permission, bool authenticated = true)
{ {
return helper.CurrentUser.IsAuthenticated() if (authenticated)
&& Security.HasPermissions(helper.CurrentUser, (Permissions) permission); {
return helper.CurrentUser.IsAuthenticated()
&& Security.HasPermissions(helper.CurrentUser, (Permissions) permission);
}
return Security.HasPermissions(helper.CurrentUser, (Permissions)permission);
} }
public static bool DoesNotHavePermission(this HtmlHelpers helper, int permission) public static bool DoesNotHavePermission(this HtmlHelpers helper, int permission)
{ {
return Security.DoesNotHavePermissions(permission, helper.CurrentUser); return Security.DoesNotHavePermissions(permission, helper.CurrentUser);
} }
public static bool IsAdmin(this HtmlHelpers helper, bool isAuthenticated = true)
{
return HasAnyPermission(helper, (int) Permissions.Administrator, isAuthenticated);
}
public static bool IsLoggedIn(this HtmlHelpers helper, NancyContext context)
{
return Security.IsLoggedIn(context);
}
} }
} }

View file

@ -30,6 +30,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Nancy; using Nancy;
using Nancy.Extensions; using Nancy.Extensions;
using Nancy.Linker;
using Nancy.Responses;
using Nancy.Security; using Nancy.Security;
using Ninject; using Ninject;
using PlexRequests.Helpers.Permissions; using PlexRequests.Helpers.Permissions;
@ -40,14 +42,16 @@ namespace PlexRequests.UI.Helpers
{ {
public class SecurityExtensions public class SecurityExtensions
{ {
public SecurityExtensions(IUserRepository userRepository, NancyModule context) public SecurityExtensions(IUserRepository userRepository, NancyModule context, IResourceLinker linker)
{ {
UserRepository = userRepository; UserRepository = userRepository;
Module = context; Module = context;
Linker = linker;
} }
private IUserRepository UserRepository { get; } private IUserRepository UserRepository { get; }
private NancyModule Module { get; } private NancyModule Module { get; }
private IResourceLinker Linker { get; }
public bool IsLoggedIn(NancyContext context) public bool IsLoggedIn(NancyContext context)
{ {
@ -117,7 +121,7 @@ namespace PlexRequests.UI.Helpers
if (dbUser == null) return false; if (dbUser == null) return false;
var permissions = (Permissions)dbUser.Permissions; var permissions = (Permissions)dbUser.Permissions;
var result = permissions.HasFlag((Permissions)perm); var result = permissions.HasFlag(perm);
return !result; return !result;
} }
@ -134,10 +138,11 @@ namespace PlexRequests.UI.Helpers
return result; return result;
} }
public void HasPermissionsResponse(Permissions perm) public Response HasPermissionsRedirect(Permissions perm, NancyContext context, string routeName, HttpStatusCode code)
{ {
Module.AddBeforeHookOrExecute( var url = Linker.BuildRelativeUri(context, routeName);
ForbiddenIfNot(ctx =>
var response = ForbiddenIfNot(ctx =>
{ {
if (ctx.CurrentUser == null) return false; if (ctx.CurrentUser == null) return false;
@ -145,13 +150,24 @@ namespace PlexRequests.UI.Helpers
if (dbUser == null) return false; if (dbUser == null) return false;
var permissions = (Permissions)dbUser.Permissions; var permissions = (Permissions) dbUser.Permissions;
var result = permissions.HasFlag(perm); var result = permissions.HasFlag(perm);
return result; return result;
}), "Requires Claims"); });
var r = response(context);
return r.StatusCode == code
? new RedirectResponse(url.ToString())
: null;
} }
public Response AdminLoginRedirect(Permissions perm, NancyContext context)
{
// This will redirect us to the Login Page if we don't have the correct permission passed in (e.g. Admin with Http 403 status code).
return HasPermissionsRedirect(perm, context, "LocalLogin", HttpStatusCode.Forbidden);
}
// BELOW IS A COPY FROM THE SecurityHooks CLASS! // BELOW IS A COPY FROM THE SecurityHooks CLASS!
/// <summary> /// <summary>

View file

@ -66,6 +66,7 @@ using PlexRequests.UI.Helpers;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
using Quartz; using Quartz;
using Action = PlexRequests.Helpers.Analytics.Action; using Action = PlexRequests.Helpers.Analytics.Action;
using HttpStatusCode = Nancy.HttpStatusCode;
namespace PlexRequests.UI.Modules namespace PlexRequests.UI.Modules
{ {
@ -154,8 +155,8 @@ namespace PlexRequests.UI.Modules
NotifySettings = notifyService; NotifySettings = notifyService;
RecentlyAdded = recentlyAdded; RecentlyAdded = recentlyAdded;
Security.HasPermissionsResponse(Permissions.Administrator); Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
Get["/"] = _ => Admin(); Get["/"] = _ => Admin();
Get["/authentication", true] = async (x, ct) => await Authentication(); Get["/authentication", true] = async (x, ct) => await Authentication();
@ -826,13 +827,11 @@ namespace PlexRequests.UI.Modules
return Response.AsJson(result return Response.AsJson(result
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Newsletter!" } ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Newsletter!" }
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
} }
private Response CreateApiKey() private Response CreateApiKey()
{ {
Security.HasPermissionsResponse(Permissions.Administrator);
Analytics.TrackEventAsync(Category.Admin, Action.Create, "Created API Key", Username, CookieHelper.GetAnalyticClientId(Cookies)); Analytics.TrackEventAsync(Category.Admin, Action.Create, "Created API Key", Username, CookieHelper.GetAnalyticClientId(Cookies));
var apiKey = Guid.NewGuid().ToString("N"); var apiKey = Guid.NewGuid().ToString("N");
var settings = PrService.GetSettings(); var settings = PrService.GetSettings();
@ -978,11 +977,11 @@ namespace PlexRequests.UI.Modules
if (!isValid) if (!isValid)
{ {
return Response.AsJson(new JsonResponseModel return Response.AsJson(new JsonResponseModel
{ {
Result = false, Result = false,
Message = Message =
$"CRON {settings.RecentlyAddedCron} is not valid. Please ensure you are using a valid CRON." $"CRON {settings.RecentlyAddedCron} is not valid. Please ensure you are using a valid CRON."
}); });
} }
} }
var result = await ScheduledJobSettings.SaveSettingsAsync(settings); var result = await ScheduledJobSettings.SaveSettingsAsync(settings);

View file

@ -46,7 +46,7 @@ namespace PlexRequests.UI.Modules.Admin
Cache = cache; Cache = cache;
RequestQueue = requestQueue; RequestQueue = requestQueue;
Security.HasPermissionsResponse(Permissions.Administrator); Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
Get["Index", "/faultqueue"] = x => Index(); Get["Index", "/faultqueue"] = x => Index();
} }

View file

@ -49,8 +49,8 @@ namespace PlexRequests.UI.Modules.Admin
Cache = cache; Cache = cache;
SystemSettings = ss; SystemSettings = ss;
Security.HasPermissionsResponse(Permissions.Administrator); Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
Get["/status", true] = async (x, ct) => await Status(); Get["/status", true] = async (x, ct) => await Status();
Post["/save", true] = async (x, ct) => await Save(); Post["/save", true] = async (x, ct) => await Save();

View file

@ -30,6 +30,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using Nancy; using Nancy;
using Nancy.Linker;
using Nancy.Security; using Nancy.Security;
using Ninject; using Ninject;
using PlexRequests.Core; using PlexRequests.Core;
@ -151,14 +152,14 @@ namespace PlexRequests.UI.Modules
get get
{ {
var userRepo = ServiceLocator.Instance.Resolve<IUserRepository>(); var userRepo = ServiceLocator.Instance.Resolve<IUserRepository>();
return _security ?? (_security = new SecurityExtensions(userRepo, this)); var linker = ServiceLocator.Instance.Resolve<IResourceLinker>();
return _security ?? (_security = new SecurityExtensions(userRepo, this, linker));
} }
} }
private SecurityExtensions _security; private SecurityExtensions _security;
protected bool LoggedIn => Context?.CurrentUser != null; protected bool LoggedIn => Context?.CurrentUser != null;
protected string Culture { get; set; } protected string Culture { get; set; }

View file

@ -1,7 +1,7 @@
#region Copyright #region Copyright
// /************************************************************************ // /************************************************************************
// Copyright (c) 2016 Jamie Rees // Copyright (c) 2016 Jamie Rees
// File: UpdateCheckerModule.cs // File: LayoutModule.cs
// Created By: Jamie Rees // Created By: Jamie Rees
// //
// Permission is hereby granted, free of charge, to any person obtaining // Permission is hereby granted, free of charge, to any person obtaining
@ -25,6 +25,7 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Nancy; using Nancy;
@ -35,24 +36,29 @@ using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Core.StatusChecker; using PlexRequests.Core.StatusChecker;
using PlexRequests.Helpers; using PlexRequests.Helpers;
using PlexRequests.Services.Interfaces;
using PlexRequests.Services.Jobs;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
namespace PlexRequests.UI.Modules namespace PlexRequests.UI.Modules
{ {
public class UpdateCheckerModule : BaseAuthModule public class LayoutModule : BaseAuthModule
{ {
public UpdateCheckerModule(ICacheProvider provider, ISettingsService<PlexRequestSettings> pr, ISettingsService<SystemSettings> settings) : base("updatechecker", pr) public LayoutModule(ICacheProvider provider, ISettingsService<PlexRequestSettings> pr, ISettingsService<SystemSettings> settings, IJobRecord rec) : base("layout", pr)
{ {
Cache = provider; Cache = provider;
SystemSettings = settings; SystemSettings = settings;
Job = rec;
Get["/", true] = async (x,ct) => await CheckLatestVersion(); Get["/", true] = async (x,ct) => await CheckLatestVersion();
Get["/cacher", true] = async (x,ct) => await CacherRunning();
} }
private ICacheProvider Cache { get; } private ICacheProvider Cache { get; }
private static Logger Log = LogManager.GetCurrentClassLogger(); private static Logger Log = LogManager.GetCurrentClassLogger();
private ISettingsService<SystemSettings> SystemSettings { get; } private ISettingsService<SystemSettings> SystemSettings { get; }
private IJobRecord Job { get; }
private async Task<Response> CheckLatestVersion() private async Task<Response> CheckLatestVersion()
{ {
@ -79,5 +85,35 @@ namespace PlexRequests.UI.Modules
return Response.AsJson(new JsonUpdateAvailableModel { UpdateAvailable = false }); return Response.AsJson(new JsonUpdateAvailableModel { UpdateAvailable = false });
} }
} }
private async Task<Response> CacherRunning()
{
try
{
var jobs = await Job.GetJobsAsync();
// Check to see if any are running
var runningJobs = jobs.Where(x => x.Running);
// We only want the cachers
var cacherJobs = runningJobs.Where(x =>
x.Name.Equals(JobNames.CpCacher)
|| x.Name.Equals(JobNames.EpisodeCacher)
|| x.Name.Equals(JobNames.PlexChecker)
|| x.Name.Equals(JobNames.SonarrCacher)
|| x.Name.Equals(JobNames.SrCacher));
return Response.AsJson(cacherJobs.Any()
? new { CurrentlyRunning = true, IsAdmin}
: new { CurrentlyRunning = false, IsAdmin });
}
catch (Exception e)
{
Log.Warn("Exception Thrown when attempting to check the status");
Log.Warn(e);
return Response.AsJson(new { CurrentlyRunning = false, IsAdmin });
}
}
} }
} }

View file

@ -52,7 +52,7 @@ namespace PlexRequests.UI.Modules
: base(pr) : base(pr)
{ {
UserMapper = m; UserMapper = m;
Get["/login"] = _ => Get["LocalLogin","/login"] = _ =>
{ {
if (LoggedIn) if (LoggedIn)
{ {

View file

@ -99,7 +99,7 @@ namespace PlexRequests.UI.Modules
{ {
Session["TempMessage"] = Resources.UI.UserLogin_IncorrectUserPass; Session["TempMessage"] = Resources.UI.UserLogin_IncorrectUserPass;
var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex"); var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex");
return Response.AsRedirect(uri.ToString()); // TODO Check this return Response.AsRedirect(uri.ToString());
} }
var authenticated = false; var authenticated = false;
@ -112,7 +112,7 @@ namespace PlexRequests.UI.Modules
Log.Debug("User is in denied list, not allowing them to authenticate"); Log.Debug("User is in denied list, not allowing them to authenticate");
Session["TempMessage"] = Resources.UI.UserLogin_IncorrectUserPass; Session["TempMessage"] = Resources.UI.UserLogin_IncorrectUserPass;
var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex"); var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex");
return Response.AsRedirect(uri.ToString()); // TODO Check this return Response.AsRedirect(uri.ToString());
} }
var password = string.Empty; var password = string.Empty;
@ -178,7 +178,7 @@ namespace PlexRequests.UI.Modules
{ {
var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex"); var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex");
Session["TempMessage"] = Resources.UI.UserLogin_IncorrectUserPass; Session["TempMessage"] = Resources.UI.UserLogin_IncorrectUserPass;
return Response.AsRedirect(uri.ToString()); // TODO Check this return Response.AsRedirect(uri.ToString());
} }
var landingSettings = await LandingPageSettings.GetSettingsAsync(); var landingSettings = await LandingPageSettings.GetSettingsAsync();
@ -192,7 +192,7 @@ namespace PlexRequests.UI.Modules
} }
} }
var retVal = Linker.BuildRelativeUri(Context, "SearchIndex"); var retVal = Linker.BuildRelativeUri(Context, "SearchIndex");
return Response.AsRedirect(retVal.ToString()); // TODO Check this return Response.AsRedirect(retVal.ToString());
} }

View file

@ -258,7 +258,7 @@
<Compile Include="Modules\IssuesModule.cs" /> <Compile Include="Modules\IssuesModule.cs" />
<Compile Include="Modules\LandingPageModule.cs" /> <Compile Include="Modules\LandingPageModule.cs" />
<Compile Include="Modules\RequestsBetaModule.cs" /> <Compile Include="Modules\RequestsBetaModule.cs" />
<Compile Include="Modules\UpdateCheckerModule.cs" /> <Compile Include="Modules\LayoutModule.cs" />
<Compile Include="Modules\UserWizardModule.cs" /> <Compile Include="Modules\UserWizardModule.cs" />
<Compile Include="NinjectModules\ApiModule.cs" /> <Compile Include="NinjectModules\ApiModule.cs" />
<Compile Include="NinjectModules\ConfigurationModule.cs" /> <Compile Include="NinjectModules\ConfigurationModule.cs" />
@ -747,6 +747,7 @@
<EmbeddedResource Include="Resources\UI.resx"> <EmbeddedResource Include="Resources\UI.resx">
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>UI1.Designer.cs</LastGenOutput> <LastGenOutput>UI1.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Resources\UI.sv.resx" /> <EmbeddedResource Include="Resources\UI.sv.resx" />
</ItemGroup> </ItemGroup>

View file

@ -459,4 +459,34 @@
<data name="Requests_ReleaseDate_Unavailable" xml:space="preserve"> <data name="Requests_ReleaseDate_Unavailable" xml:space="preserve">
<value>Der er ingen oplysninger om udgivelsesdatoen</value> <value>Der er ingen oplysninger om udgivelsesdatoen</value>
</data> </data>
<data name="Search_ViewInPlex" xml:space="preserve">
<value>Udsigt I Plex</value>
</data>
<data name="Custom_Donation_Default" xml:space="preserve">
<value>Doner til Bibliotek vedligeholder</value>
</data>
<data name="Search_Available_on_plex" xml:space="preserve">
<value>Tilgængelig på</value>
</data>
<data name="Search_Movie_Status" xml:space="preserve">
<value>Movie status</value>
</data>
<data name="Search_Not_Requested_Yet" xml:space="preserve">
<value>Ikke anmodet endnu</value>
</data>
<data name="Search_Pending_approval" xml:space="preserve">
<value>Afventer godkendelse</value>
</data>
<data name="Search_Processing_Request" xml:space="preserve">
<value>Behandler forespørgsel</value>
</data>
<data name="Search_Request_denied" xml:space="preserve">
<value>Anmodning afvist!</value>
</data>
<data name="Search_TV_Show_Status" xml:space="preserve">
<value>Tv-show status!</value>
</data>
<data name="Layout_CacherRunning" xml:space="preserve">
<value>En baggrund proces kører i øjeblikket, så der kan være nogle uventede problemer. Dette bør ikke tage for lang tid.</value>
</data>
</root> </root>

View file

@ -459,4 +459,34 @@
<data name="Requests_ReleaseDate_Unavailable" xml:space="preserve"> <data name="Requests_ReleaseDate_Unavailable" xml:space="preserve">
<value>Es gibt noch keine Informationen für diesen Release-Termin</value> <value>Es gibt noch keine Informationen für diesen Release-Termin</value>
</data> </data>
</root> <data name="Search_ViewInPlex" xml:space="preserve">
<value>Ansicht In Plex</value>
</data>
<data name="Custom_Donation_Default" xml:space="preserve">
<value>Spenden zur Bibliothek Maintainer</value>
</data>
<data name="Search_Available_on_plex" xml:space="preserve">
<value>Verfügbar auf Plex</value>
</data>
<data name="Search_Movie_Status" xml:space="preserve">
<value>Film-Status!</value>
</data>
<data name="Search_Not_Requested_Yet" xml:space="preserve">
<value>Noch nicht heraus!</value>
</data>
<data name="Search_Pending_approval" xml:space="preserve">
<value>Genehmigung ausstehend</value>
</data>
<data name="Search_Processing_Request" xml:space="preserve">
<value>Die Verarbeitung Anfrage</value>
</data>
<data name="Search_Request_denied" xml:space="preserve">
<value>Anfrage verweigert.</value>
</data>
<data name="Search_TV_Show_Status" xml:space="preserve">
<value>TV-Show-Status</value>
</data>
<data name="Layout_CacherRunning" xml:space="preserve">
<value>Ein Hintergrundprozess gerade läuft, so könnte es einige unerwartete Verhalten sein. Dies sollte nicht zu lange dauern.</value>
</data>
</root>

View file

@ -459,4 +459,31 @@
<data name="Requests_ReleaseDate_Unavailable" xml:space="preserve"> <data name="Requests_ReleaseDate_Unavailable" xml:space="preserve">
<value>No hay información disponible para la fecha de lanzamiento</value> <value>No hay información disponible para la fecha de lanzamiento</value>
</data> </data>
<data name="Custom_Donation_Default" xml:space="preserve">
<value>Donar a la biblioteca Mantenedor</value>
</data>
<data name="Search_Available_on_plex" xml:space="preserve">
<value>Disponible en Plex</value>
</data>
<data name="Search_Movie_Status" xml:space="preserve">
<value>estado de película</value>
</data>
<data name="Search_Not_Requested_Yet" xml:space="preserve">
<value>No solicitado</value>
</data>
<data name="Search_Pending_approval" xml:space="preserve">
<value>PENDIENTES DE APROBACIÓN</value>
</data>
<data name="Search_Processing_Request" xml:space="preserve">
<value>solicitud de procesamiento</value>
</data>
<data name="Search_Request_denied" xml:space="preserve">
<value>Solicitud denegada</value>
</data>
<data name="Search_TV_Show_Status" xml:space="preserve">
<value>estado de programa de televisión</value>
</data>
<data name="Layout_CacherRunning" xml:space="preserve">
<value>Un proceso en segundo plano se está ejecutando actualmente, por lo que podría ser un comportamiento inesperado. Esto no debería tomar mucho tiempo.</value>
</data>
</root> </root>

View file

@ -459,4 +459,34 @@
<data name="Requests_ReleaseDate_Unavailable" xml:space="preserve"> <data name="Requests_ReleaseDate_Unavailable" xml:space="preserve">
<value>Il n'y a pas d'information disponible pour la date de sortie</value> <value>Il n'y a pas d'information disponible pour la date de sortie</value>
</data> </data>
<data name="Search_ViewInPlex" xml:space="preserve">
<value>Voir Dans Plex</value>
</data>
<data name="Custom_Donation_Default" xml:space="preserve">
<value>Faire un don à la bibliothèque mainteneur!</value>
</data>
<data name="Search_Available_on_plex" xml:space="preserve">
<value>Disponible sur Plex</value>
</data>
<data name="Search_Movie_Status" xml:space="preserve">
<value>le statut de film</value>
</data>
<data name="Search_Not_Requested_Yet" xml:space="preserve">
<value>Pas encore demandé</value>
</data>
<data name="Search_Pending_approval" xml:space="preserve">
<value>En attente de validation</value>
</data>
<data name="Search_Processing_Request" xml:space="preserve">
<value>Traitement de la demande</value>
</data>
<data name="Search_Request_denied" xml:space="preserve">
<value>Requête refusée</value>
</data>
<data name="Search_TV_Show_Status" xml:space="preserve">
<value>TV show status</value>
</data>
<data name="Layout_CacherRunning" xml:space="preserve">
<value>Un processus d'arrière-plan est en cours d'exécution, de sorte qu'il pourrait y avoir des comportements inattendus. Cela ne devrait pas prendre trop de temps.</value>
</data>
</root> </root>

View file

@ -459,4 +459,34 @@
<data name="Requests_ReleaseDate_Unavailable" xml:space="preserve"> <data name="Requests_ReleaseDate_Unavailable" xml:space="preserve">
<value>Non ci sono informazioni disponibili per la data di uscita</value> <value>Non ci sono informazioni disponibili per la data di uscita</value>
</data> </data>
<data name="Search_ViewInPlex" xml:space="preserve">
<value>Guarda In Plex</value>
</data>
<data name="Custom_Donation_Default" xml:space="preserve">
<value>Donare alla libreria Maintainer</value>
</data>
<data name="Search_Available_on_plex" xml:space="preserve">
<value>Disponibile su Plex</value>
</data>
<data name="Search_Movie_Status" xml:space="preserve">
<value>lo stato di film</value>
</data>
<data name="Search_Not_Requested_Yet" xml:space="preserve">
<value>Non ancora richiesto</value>
</data>
<data name="Search_Pending_approval" xml:space="preserve">
<value>Approvazione in sospeso</value>
</data>
<data name="Search_Processing_Request" xml:space="preserve">
<value>Elaborazione richiesta</value>
</data>
<data name="Search_Request_denied" xml:space="preserve">
<value>Richiesta negata</value>
</data>
<data name="Search_TV_Show_Status" xml:space="preserve">
<value>Show televisivo di stato</value>
</data>
<data name="Layout_CacherRunning" xml:space="preserve">
<value>Un processo in background è in esecuzione, quindi ci potrebbero essere alcuni comportamenti imprevisti. Questo non dovrebbe richiedere troppo tempo.</value>
</data>
</root> </root>

View file

@ -459,4 +459,34 @@
<data name="Requests_ReleaseDate_Unavailable" xml:space="preserve"> <data name="Requests_ReleaseDate_Unavailable" xml:space="preserve">
<value>Er is geen informatie beschikbaar voor de release datum</value> <value>Er is geen informatie beschikbaar voor de release datum</value>
</data> </data>
<data name="Search_ViewInPlex" xml:space="preserve">
<value>Bekijk In Plex</value>
</data>
<data name="Custom_Donation_Default" xml:space="preserve">
<value>Doneer aan Library Maintainer</value>
</data>
<data name="Search_Available_on_plex" xml:space="preserve">
<value>Beschikbaar vanaf</value>
</data>
<data name="Search_Movie_Status" xml:space="preserve">
<value>Movie-status!</value>
</data>
<data name="Search_Not_Requested_Yet" xml:space="preserve">
<value>Nog niet gevraagd</value>
</data>
<data name="Search_Pending_approval" xml:space="preserve">
<value>Wacht op goedkeuring</value>
</data>
<data name="Search_Processing_Request" xml:space="preserve">
<value>Verwerking verzoek...</value>
</data>
<data name="Search_Request_denied" xml:space="preserve">
<value>Aanvraag afgewezen</value>
</data>
<data name="Search_TV_Show_Status" xml:space="preserve">
<value>TV-show-status</value>
</data>
<data name="Layout_CacherRunning" xml:space="preserve">
<value>Een achtergrond taak is momenteel actief, dus er misschien een onverwachte gedrag. Dit moet niet te lang duren.</value>
</data>
</root> </root>

View file

@ -459,4 +459,31 @@
<data name="Requests_ReleaseDate_Unavailable" xml:space="preserve"> <data name="Requests_ReleaseDate_Unavailable" xml:space="preserve">
<value>Não há informações disponíveis para a data de lançamento</value> <value>Não há informações disponíveis para a data de lançamento</value>
</data> </data>
<data name="Search_ViewInPlex" xml:space="preserve">
<value>Ver Em Plex</value>
</data>
<data name="Custom_Donation_Default" xml:space="preserve">
<value>Doações para Biblioteca Mantenedor</value>
</data>
<data name="Search_Available_on_plex" xml:space="preserve">
<value>Disponível em Plex</value>
</data>
<data name="Search_Movie_Status" xml:space="preserve">
<value>status de filme</value>
</data>
<data name="Search_Not_Requested_Yet" xml:space="preserve">
<value>Não solicitada ainda</value>
</data>
<data name="Search_Pending_approval" xml:space="preserve">
<value>B P/ Aprovação</value>
</data>
<data name="Search_Processing_Request" xml:space="preserve">
<value>Processando solicitação...</value>
</data>
<data name="Search_Request_denied" xml:space="preserve">
<value>Solicitação negada.</value>
</data>
<data name="Search_TV_Show_Status" xml:space="preserve">
<value>Programa de TV status</value>
</data>
</root> </root>

View file

@ -467,4 +467,7 @@
<data name="Search_TV_Show_Status" xml:space="preserve"> <data name="Search_TV_Show_Status" xml:space="preserve">
<value>TV show status</value> <value>TV show status</value>
</data> </data>
<data name="Layout_CacherRunning" xml:space="preserve">
<value>A background process is currently running, so there might be some unexpected behavior. This shouldn't take too long.</value>
</data>
</root> </root>

View file

@ -459,4 +459,34 @@
<data name="Requests_ReleaseDate_Unavailable" xml:space="preserve"> <data name="Requests_ReleaseDate_Unavailable" xml:space="preserve">
<value>Det finns ingen information tillgänglig för release datum</value> <value>Det finns ingen information tillgänglig för release datum</value>
</data> </data>
<data name="Search_ViewInPlex" xml:space="preserve">
<value>Vy I Plex</value>
</data>
<data name="Custom_Donation_Default" xml:space="preserve">
<value>Donera till bibliotek Ansvarig</value>
</data>
<data name="Search_Available_on_plex" xml:space="preserve">
<value>Tillgänglig den</value>
</data>
<data name="Search_Movie_Status" xml:space="preserve">
<value>Film status</value>
</data>
<data name="Search_Not_Requested_Yet" xml:space="preserve">
<value>Inte Begärd ännu</value>
</data>
<data name="Search_Pending_approval" xml:space="preserve">
<value>Väntar på godkännande</value>
</data>
<data name="Search_Processing_Request" xml:space="preserve">
<value>Bearbetning förfrågan</value>
</data>
<data name="Search_Request_denied" xml:space="preserve">
<value>Förfrågan nekad</value>
</data>
<data name="Search_TV_Show_Status" xml:space="preserve">
<value>Visa status</value>
</data>
<data name="Layout_CacherRunning" xml:space="preserve">
<value>En bakgrundsprocess är igång, så det kan finnas några oväntade beteende. Detta bör inte ta alltför lång tid.</value>
</data>
</root> </root>

View file

@ -222,6 +222,15 @@ namespace PlexRequests.UI.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to A background process is currently running, so there might be some unexpected behavior. This shouldn&apos;t take too long..
/// </summary>
public static string Layout_CacherRunning {
get {
return ResourceManager.GetString("Layout_CacherRunning", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Change Password. /// Looks up a localized string similar to Change Password.
/// </summary> /// </summary>

View file

@ -44,7 +44,7 @@ namespace PlexRequests.UI.Validators
RuleFor(x => x.BaseUrl).NotEqual("login", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'login' as this is reserved by the application."); RuleFor(x => x.BaseUrl).NotEqual("login", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'login' as this is reserved by the application.");
RuleFor(x => x.BaseUrl).NotEqual("test", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'test' as this is reserved by the application."); RuleFor(x => x.BaseUrl).NotEqual("test", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'test' as this is reserved by the application.");
RuleFor(x => x.BaseUrl).NotEqual("approval", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'approval' as this is reserved by the application."); RuleFor(x => x.BaseUrl).NotEqual("approval", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'approval' as this is reserved by the application.");
RuleFor(x => x.BaseUrl).NotEqual("updatechecker", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'updatechecker' as this is reserved by the application."); RuleFor(x => x.BaseUrl).NotEqual("layout", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'layout' as this is reserved by the application.");
RuleFor(x => x.BaseUrl).NotEqual("usermanagement", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'usermanagement' as this is reserved by the application."); RuleFor(x => x.BaseUrl).NotEqual("usermanagement", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'usermanagement' as this is reserved by the application.");
RuleFor(x => x.BaseUrl).NotEqual("api", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'api' as this is reserved by the application."); RuleFor(x => x.BaseUrl).NotEqual("api", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'api' as this is reserved by the application.");
RuleFor(x => x.BaseUrl).NotEqual("landing", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'landing' as this is reserved by the application."); RuleFor(x => x.BaseUrl).NotEqual("landing", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'landing' as this is reserved by the application.");

View file

@ -10,7 +10,7 @@
$(function () { $(function () {
// Check for update // Check for update
var url = createBaseUrl(urlBase, '/updatechecker'); var url = createBaseUrl(urlBase, '/layout');
$.ajax({ $.ajax({
type: "GET", type: "GET",
url: url, url: url,
@ -20,7 +20,7 @@
var status = createBaseUrl(urlBase, '/admin/status'); var status = createBaseUrl(urlBase, '/admin/status');
$('#updateAvailable').html("<i class='fa fa-cloud-download' aria-hidden='true'></i> @UI.Layout_UpdateAvailablePart1 <a style='color: white' href='" + status + "'>@UI.Layout_UpdateAvailablePart2</a>"); $('#updateAvailable').html("<i class='fa fa-cloud-download' aria-hidden='true'></i> @UI.Layout_UpdateAvailablePart1 <a style='color: white' href='" + status + "'>@UI.Layout_UpdateAvailablePart2</a>");
$('#updateAvailable').removeAttr("hidden"); $('#updateAvailable').removeAttr("hidden");
$('body').addClass('update-available'); //$('body').addClass('update-available');
} }
}, },
error: function (e) { error: function (e) {
@ -29,6 +29,7 @@
}); });
// End Check for update // End Check for update
checkCacheInProgress();
// Scroller // Scroller
$(document).on('scroll', function () { $(document).on('scroll', function () {
@ -43,9 +44,6 @@
$('.scroll-top-wrapper').on('click', scrollToTop); $('.scroll-top-wrapper').on('click', scrollToTop);
// End Scroller // End Scroller
// Get Issue count // Get Issue count
var issueUrl = createBaseUrl(urlBase, '/issues/issuecount'); var issueUrl = createBaseUrl(urlBase, '/issues/issuecount');
$.ajax({ $.ajax({
@ -66,6 +64,8 @@
// End issue count // End issue count
$('#donate').click(function () { $('#donate').click(function () {
ga('send', 'event', 'Navbar', 'Donate', 'Donate Clicked'); ga('send', 'event', 'Navbar', 'Donate', 'Donate Clicked');
}); });
@ -80,4 +80,23 @@
offsetTop = offset.top; offsetTop = offset.top;
$('html, body').animate({ scrollTop: offsetTop }, 500, 'linear'); $('html, body').animate({ scrollTop: offsetTop }, 500, 'linear');
} }
function checkCacheInProgress() {
var url = createBaseUrl(urlBase, '/layout/cacher');
$.ajax({
type: "GET",
url: url,
dataType: "json",
success: function (response) {
if (response.currentlyRunning) {
$('#cacherRunning').html("@UI.Layout_CacherRunning");
$('#cacherRunning').removeAttr("hidden");
}
},
error: function (e) {
console.log(e);
}
});
}
</script> </script>

View file

@ -35,21 +35,21 @@
@Html.GetNavbarUrl(Context, "/search", UI.Layout_Search, "search") @Html.GetNavbarUrl(Context, "/search", UI.Layout_Search, "search")
@Html.GetNavbarUrl(Context, "/requests", UI.Layout_Requests, "plus-circle") @Html.GetNavbarUrl(Context, "/requests", UI.Layout_Requests, "plus-circle")
@Html.GetNavbarUrl(Context, "/issues", UI.Layout_Issues, "exclamation", "<span id=\"issueCount\"></span>") @Html.GetNavbarUrl(Context, "/issues", UI.Layout_Issues, "exclamation", "<span id=\"issueCount\"></span>")
@if (Context.CurrentUser.IsAuthenticated()) // TODO replace with IsAdmin @*@if (Context.CurrentUser.IsAuthenticated()) // TODO replace with IsAdmin*@
@if (Html.IsAdmin())
{ {
<li><a id="donate" href="https://www.paypal.me/PlexRequestsNet" target="_blank"><i class="fa fa-heart" style="color: red"></i> @UI.Layout_Donate</a></li> <li><a id="donate" href="https://www.paypal.me/PlexRequestsNet" target="_blank"><i class="fa fa-heart" style="color: red"></i> @UI.Layout_Donate</a></li>
} }
<li id="customDonate" style="display: none"><a id="customDonateHref" href="https://www.paypal.me/PlexRequestsNet" target="_blank"><i class="fa fa-heart" style="color: yellow;"></i> <span id="donationText">@UI.Custom_Donation_Default</span></a></li> <li id="customDonate" style="display: none"><a id="customDonateHref" href="https://www.paypal.me/PlexRequestsNet" target="_blank"><i class="fa fa-heart" style="color: yellow;"></i> <span id="donationText">@UI.Custom_Donation_Default</span></a></li>
</ul> </ul>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
@*@if (!Context.CurrentUser.IsAuthenticated() && Context.Request.Session[SessionKeys.UsernameKey] == null) // TODO replace with IsAdmin*@
@if (!Html.IsLoggedIn(Context))
@if (!Context.CurrentUser.IsAuthenticated() && Context.Request.Session[SessionKeys.UsernameKey] == null) // TODO replace with IsAdmin
{ {
<li><a href="@url/login?redirect=@Context.Request.Path"><i class="fa fa-user"></i> @UI.Layout_Admin</a></li> <li><a href="@url/login?redirect=@Context.Request.Path"><i class="fa fa-user"></i> @UI.Layout_Admin</a></li>
} }
@if (Context.CurrentUser.IsAuthenticated()) // TODO replace with IsAdmin @*@if (Context.CurrentUser.IsAuthenticated()) // TODO replace with IsAdmin*@
@if (Html.IsAdmin())
{ {
<li><a>@UI.Layout_Welcome @Context.Request.Session[SessionKeys.UsernameKey]</a></li> <li><a>@UI.Layout_Welcome @Context.Request.Session[SessionKeys.UsernameKey]</a></li>
<li class="dropdown"> <li class="dropdown">
@ -63,7 +63,8 @@
</ul> </ul>
</li> </li>
} }
@if (Context.Request.Session[SessionKeys.UsernameKey] != null && !Context.CurrentUser.IsAuthenticated()) @*@if (Context.Request.Session[SessionKeys.UsernameKey] != null && !Context.CurrentUser.IsAuthenticated())*@
else if (Html.IsLoggedIn(Context)) // Logged in but not admin
{ {
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-user"></i> @UI.Layout_Welcome @Context.Request.Session[SessionKeys.UsernameKey] <span class="caret"></span></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-user"></i> @UI.Layout_Welcome @Context.Request.Session[SessionKeys.UsernameKey] <span class="caret"></span></a>
@ -77,19 +78,19 @@
} }
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-language" aria-hidden="true"><span class="caret"></span></i></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-language" aria-hidden="true"><span class="caret"></span></i></a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
<li><a href="@url/culture?l=en&u=@Context.Request.Path">@UI.Layout_English</a></li> <li><a href="@url/culture?l=en&u=@Context.Request.Path">@UI.Layout_English</a></li>
<li><a href="@url/culture?l=fr&u=@Context.Request.Path">@UI.Layout_French</a></li> <li><a href="@url/culture?l=fr&u=@Context.Request.Path">@UI.Layout_French</a></li>
<li><a href="@url/culture?l=nl&u=@Context.Request.Path">@UI.Layout_Dutch</a></li> <li><a href="@url/culture?l=nl&u=@Context.Request.Path">@UI.Layout_Dutch</a></li>
<li><a href="@url/culture?l=es&u=@Context.Request.Path">@UI.Layout_Spanish</a></li> <li><a href="@url/culture?l=es&u=@Context.Request.Path">@UI.Layout_Spanish</a></li>
<li><a href="@url/culture?l=de&u=@Context.Request.Path">@UI.Layout_German</a></li> <li><a href="@url/culture?l=de&u=@Context.Request.Path">@UI.Layout_German</a></li>
<li><a href="@url/culture?l=da&u=@Context.Request.Path">@UI.Layout_Danish</a></li> <li><a href="@url/culture?l=da&u=@Context.Request.Path">@UI.Layout_Danish</a></li>
<li><a href="@url/culture?l=pt&u=@Context.Request.Path">@UI.Layout_Portuguese</a></li> <li><a href="@url/culture?l=pt&u=@Context.Request.Path">@UI.Layout_Portuguese</a></li>
<li><a href="@url/culture?l=sv&u=@Context.Request.Path">@UI.Layout_Swedish</a></li> <li><a href="@url/culture?l=sv&u=@Context.Request.Path">@UI.Layout_Swedish</a></li>
<li><a href="@url/culture?l=it&u=@Context.Request.Path">@UI.Layout_Italian</a></li> <li><a href="@url/culture?l=it&u=@Context.Request.Path">@UI.Layout_Italian</a></li>
</ul> </ul>
<li/> <li />
</ul> </ul>
</div> </div>
</div> </div>
@ -104,16 +105,17 @@
var donateLink = $("#customDonateHref"); var donateLink = $("#customDonateHref");
var donationText = $("#donationText"); var donationText = $("#donationText");
donateLink.attr("href", result.url); donateLink.attr("href", result.url);
if(result.message) { if (result.message) {
donationText.text(result.message); donationText.text(result.message);
} }
} }
}, },
error: function(xhr, status, error) { error: function (xhr, status, error) {
console.log("error " + error); console.log("error " + error);
$("#customDonate").hide(); $("#customDonate").hide();
} }
}); });
</script> </script>
<div id="updateAvailable" hidden="hidden"></div> <div id="updateAvailable" hidden="hidden"></div>
<div id="cacherRunning" hidden="hidden"></div>
</nav> </nav>

View file

@ -16,8 +16,6 @@
<i class="fa fa-2x fa-arrow-circle-up"></i> <i class="fa fa-2x fa-arrow-circle-up"></i>
</span> </span>
</div> </div>
@*@MiniProfiler.RenderIncludes()*@
</body> </body>
@Html.GetInformationalVersion() @Html.GetInformationalVersion()