mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-11 03:52:33 +02:00
static resource URLs are now case sensitive.
This commit is contained in:
parent
e89a35522e
commit
478caf15f8
@ -58,8 +58,8 @@ module.exports = function (grunt) {
|
||||
expand: true,
|
||||
src : [
|
||||
'UI/Content/base.less',
|
||||
'UI/Content/Overrides.less',
|
||||
'UI/Series/Series.less',
|
||||
'UI/Content/overrides.less',
|
||||
'UI/Series/series.less',
|
||||
'UI/AddSeries/addSeries.less',
|
||||
'UI/Calendar/calendar.less',
|
||||
'UI/Cells/cells.less',
|
||||
|
@ -16,14 +16,14 @@ public MediaCoverMapper(IAppFolderInfo appFolderInfo)
|
||||
public string Map(string resourceUrl)
|
||||
{
|
||||
var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar);
|
||||
path = path.Trim(Path.DirectorySeparatorChar).ToLower();
|
||||
path = path.Trim(Path.DirectorySeparatorChar);
|
||||
|
||||
return Path.Combine(_appFolderInfo.GetAppDataPath(), path);
|
||||
}
|
||||
|
||||
public bool CanHandle(string resourceUrl)
|
||||
{
|
||||
return resourceUrl.StartsWith("/mediacover");
|
||||
return resourceUrl.StartsWith("/MediaCover");
|
||||
}
|
||||
}
|
||||
}
|
@ -32,10 +32,10 @@ public StaticResourceMapper(IAppFolderInfo appFolderInfo)
|
||||
public string Map(string resourceUrl)
|
||||
{
|
||||
var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar);
|
||||
path = path.Trim(Path.DirectorySeparatorChar).ToLower();
|
||||
path = path.Trim(Path.DirectorySeparatorChar);
|
||||
|
||||
|
||||
return Path.Combine(_appFolderInfo.StartUpFolder, "ui", path);
|
||||
return Path.Combine(_appFolderInfo.StartUpFolder, "UI", path);
|
||||
}
|
||||
|
||||
public bool CanHandle(string resourceUrl)
|
||||
|
@ -5,6 +5,7 @@
|
||||
using Nancy;
|
||||
using Nancy.Responses;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
||||
namespace NzbDrone.Api.Frontend
|
||||
{
|
||||
@ -20,6 +21,8 @@ public class StaticResourceProvider : IProcessStaticResource
|
||||
private readonly IAddCacheHeaders _addCacheHeaders;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private readonly bool _caseSensitive;
|
||||
|
||||
public StaticResourceProvider(IDiskProvider diskProvider,
|
||||
IEnumerable<IMapHttpRequestsToDisk> requestMappers,
|
||||
IAddCacheHeaders addCacheHeaders,
|
||||
@ -29,11 +32,16 @@ public StaticResourceProvider(IDiskProvider diskProvider,
|
||||
_requestMappers = requestMappers;
|
||||
_addCacheHeaders = addCacheHeaders;
|
||||
_logger = logger;
|
||||
|
||||
if (!RuntimeInfo.IsProduction)
|
||||
{
|
||||
_caseSensitive = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Response ProcessStaticResourceRequest(NancyContext context, string workingFolder)
|
||||
{
|
||||
var path = context.Request.Url.Path.ToLower();
|
||||
var path = context.Request.Url.Path;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
@ -46,7 +54,7 @@ public Response ProcessStaticResourceRequest(NancyContext context, string workin
|
||||
{
|
||||
var filePath = mapper.Map(path);
|
||||
|
||||
if (_diskProvider.FileExists(filePath))
|
||||
if (_diskProvider.FileExists(filePath, _caseSensitive))
|
||||
{
|
||||
var response = new StreamResponse(() => File.OpenRead(filePath), MimeTypes.GetMimeType(filePath));
|
||||
_addCacheHeaders.ToResponse(context.Request, response);
|
||||
|
@ -78,12 +78,12 @@ public void should_honor_ttl()
|
||||
{
|
||||
hitCount++;
|
||||
return null;
|
||||
}, TimeSpan.FromMilliseconds(200));
|
||||
}, TimeSpan.FromMilliseconds(300));
|
||||
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
|
||||
hitCount.Should().BeInRange(4, 6);
|
||||
hitCount.Should().BeInRange(3, 6);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@ -69,6 +71,40 @@ public void normalize_path_exception_null()
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void get_actual_casing_for_none_existing_file_should_throw()
|
||||
{
|
||||
WindowsOnly();
|
||||
Assert.Throws<DirectoryNotFoundException>(() => "C:\\InValidFolder\\invalidfile.exe".GetActualCasing());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void get_actual_casing_should_return_actual_casing_for_local_file()
|
||||
{
|
||||
var path = Process.GetCurrentProcess().MainModule.FileName;
|
||||
path.ToUpper().GetActualCasing().Should().Be(path);
|
||||
path.ToLower().GetActualCasing().Should().Be(path);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void get_actual_casing_should_return_actual_casing_for_local_dir()
|
||||
{
|
||||
var path = Directory.GetCurrentDirectory();
|
||||
path.ToUpper().GetActualCasing().Should().Be(path);
|
||||
path.ToLower().GetActualCasing().Should().Be(path);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
[Explicit]
|
||||
public void get_actual_casing_should_return_original_casing_for_shares()
|
||||
{
|
||||
var path = @"\\server\Pool\Apps";
|
||||
path.GetActualCasing().Should().Be(path);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[Test]
|
||||
public void AppDataDirectory_path_test()
|
||||
@ -76,7 +112,6 @@ public void AppDataDirectory_path_test()
|
||||
GetIAppDirectoryInfo().GetAppDataPath().Should().BeEquivalentTo(@"C:\NzbDrone\");
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Config_path_test()
|
||||
{
|
||||
|
@ -14,8 +14,10 @@ public interface IDiskProvider
|
||||
DateTime GetLastFolderWrite(string path);
|
||||
DateTime GetLastFileWrite(string path);
|
||||
void EnsureFolder(string path);
|
||||
bool FolderExists(string path, bool caseSensitive);
|
||||
bool FolderExists(string path);
|
||||
bool FileExists(string path);
|
||||
bool FileExists(string path, bool caseSensitive);
|
||||
string[] GetDirectories(string path);
|
||||
string[] GetFiles(string path, SearchOption searchOption);
|
||||
long GetFolderSize(string path);
|
||||
@ -97,17 +99,36 @@ public virtual void EnsureFolder(string path)
|
||||
public virtual bool FolderExists(string path)
|
||||
{
|
||||
Ensure.That(() => path).IsValidPath();
|
||||
|
||||
return Directory.Exists(path);
|
||||
}
|
||||
|
||||
public virtual bool FolderExists(string path, bool caseSensitive)
|
||||
{
|
||||
if (caseSensitive)
|
||||
{
|
||||
return FolderExists(path) && path == path.GetActualCasing();
|
||||
}
|
||||
|
||||
return FolderExists(path);
|
||||
}
|
||||
|
||||
|
||||
public virtual bool FileExists(string path)
|
||||
{
|
||||
Ensure.That(() => path).IsValidPath();
|
||||
|
||||
return File.Exists(path);
|
||||
}
|
||||
|
||||
public virtual bool FileExists(string path, bool caseSensitive)
|
||||
{
|
||||
if (caseSensitive)
|
||||
{
|
||||
return FileExists(path) && path == path.GetActualCasing();
|
||||
}
|
||||
|
||||
return FileExists(path);
|
||||
}
|
||||
|
||||
public virtual string[] GetDirectories(string path)
|
||||
{
|
||||
Ensure.That(() => path).IsValidPath();
|
||||
|
@ -41,18 +41,33 @@ public static bool ContainsInvalidPathChars(this string text)
|
||||
private static string GetProperCapitalization(DirectoryInfo dirInfo)
|
||||
{
|
||||
var parentDirInfo = dirInfo.Parent;
|
||||
if (null == parentDirInfo)
|
||||
return dirInfo.Name;
|
||||
return Path.Combine(GetProperCapitalization(parentDirInfo),
|
||||
parentDirInfo.GetDirectories(dirInfo.Name)[0].Name);
|
||||
if (parentDirInfo == null)
|
||||
{
|
||||
//Drive letter
|
||||
return dirInfo.Name.ToUpper();
|
||||
}
|
||||
return Path.Combine(GetProperCapitalization(parentDirInfo), parentDirInfo.GetDirectories(dirInfo.Name)[0].Name);
|
||||
}
|
||||
|
||||
public static string GetActualCasing(this string filename)
|
||||
public static string GetActualCasing(this string path)
|
||||
{
|
||||
var fileInfo = new FileInfo(filename);
|
||||
var attributes = File.GetAttributes(path);
|
||||
|
||||
if (OsInfo.IsLinux || path.StartsWith("\\"))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
if ((attributes & FileAttributes.Directory) == FileAttributes.Directory)
|
||||
{
|
||||
return GetProperCapitalization(new DirectoryInfo(path));
|
||||
}
|
||||
|
||||
var fileInfo = new FileInfo(path);
|
||||
|
||||
|
||||
DirectoryInfo dirInfo = fileInfo.Directory;
|
||||
return Path.Combine(GetProperCapitalization(dirInfo),
|
||||
dirInfo.GetFiles(fileInfo.Name)[0].Name);
|
||||
return Path.Combine(GetProperCapitalization(dirInfo), dirInfo.GetFiles(fileInfo.Name)[0].Name);
|
||||
}
|
||||
|
||||
|
||||
|
@ -37,7 +37,7 @@ public void should_convert_trakts_urls_to_local()
|
||||
Subject.ConvertToLocalUrls(12, covers);
|
||||
|
||||
|
||||
covers.Single().Url.Should().Be("/mediacover/12/banner.jpg?lastWrite=1234");
|
||||
covers.Single().Url.Should().Be("/MediaCover/12/banner.jpg?lastWrite=1234");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -52,7 +52,7 @@ public void should_convert_trakts_urls_to_local_without_time_if_file_doesnt_exis
|
||||
Subject.ConvertToLocalUrls(12, covers);
|
||||
|
||||
|
||||
covers.Single().Url.Should().Be("/mediacover/12/banner.jpg");
|
||||
covers.Single().Url.Should().Be("/MediaCover/12/banner.jpg");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ public void ConvertToLocalUrls(int seriesId, IEnumerable<MediaCover> covers)
|
||||
{
|
||||
var filePath = GetCoverPath(seriesId, mediaCover.CoverType);
|
||||
|
||||
mediaCover.Url = @"/mediacover/" + seriesId + "/" + mediaCover.CoverType.ToString().ToLower() + ".jpg";
|
||||
mediaCover.Url = @"/MediaCover/" + seriesId + "/" + mediaCover.CoverType.ToString().ToLower() + ".jpg";
|
||||
|
||||
if (_diskProvider.FileExists(filePath))
|
||||
{
|
||||
|
@ -3,7 +3,7 @@ define(
|
||||
[
|
||||
'backbone',
|
||||
'AddSeries/RootFolders/Model',
|
||||
'mixins/backbone.signalr.mixin'
|
||||
'Mixins/backbone.signalr.mixin'
|
||||
], function (Backbone, RootFolderModel) {
|
||||
|
||||
var RootFolderCollection = Backbone.Collection.extend({
|
||||
|
@ -2,70 +2,34 @@
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url('/content/fonts/opensans-light.eot');
|
||||
src: url('/Content/fonts/OpenSans-Light.eot');
|
||||
src: local('Open Sans Light'),
|
||||
local('OpenSans-Light'),
|
||||
url('/content/fonts/opensans-light.eot?#iefix') format('embedded-opentype'),
|
||||
url('/content/fonts/opensans-light.woff') format('woff'),
|
||||
url('/content/fonts/opensans-light.ttf') format('truetype');
|
||||
url('/Content/fonts/OpenSans-Light.eot?#iefix') format('embedded-opentype'),
|
||||
url('/Content/fonts/OpenSans-Light.woff') format('woff'),
|
||||
url('/Content/fonts/OpenSans-Light.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('/content/fonts/opensans-regular.eot');
|
||||
src: url('/Content/fonts/OpenSans-Regular.eot');
|
||||
src: local('Open Sans'),
|
||||
local('OpenSans'),
|
||||
url('/content/fonts/opensans-regular.eot?#iefix') format('embedded-opentype'),
|
||||
url('/content/fonts/opensans-regular.woff') format('woff'),
|
||||
url('/content/fonts/opensans-regular.ttf') format('truetype')
|
||||
url('/Content/fonts/OpenSans-Regular.eot?#iefix') format('embedded-opentype'),
|
||||
url('/Content/fonts/OpenSans-Regular.woff') format('woff'),
|
||||
url('/Content/fonts/OpenSans-Regular.ttf') format('truetype')
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: url('/content/fonts/opensans-semibold.eot');
|
||||
src: local('Open Sans Semibold'),
|
||||
local('OpenSans-Semibold'),
|
||||
url('/content/fonts/opensans-semibold.eot?#iefix') format('embedded-opentype'),
|
||||
url('/content/fonts/opensans-semibold.woff') format('woff'),
|
||||
url('/content/fonts/opensans-semibold.ttf') format('truetype')
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: url('/content/fonts/opensans-lightitalic.eot');
|
||||
src: local('Open Sans Light Italic'),
|
||||
local('OpenSansLight-Italic'),
|
||||
url('/content/fonts/opensans-lightitalic.eot?#iefix') format('embedded-opentype'),
|
||||
url('/content/fonts/opensans-lightitalic.woff') format('woff'),
|
||||
url('/content/fonts/opensans-lightitalic.ttf') format('truetype')
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url('/content/fonts/opensans-italic.eot');
|
||||
src: local('Open Sans Italic'),
|
||||
local('OpenSans-Italic'),
|
||||
url('/content/fonts/opensans-italic.eot?#iefix') format('embedded-opentype'),
|
||||
url('/content/fonts/opensans-italic.woff') format('woff'),
|
||||
url('/content/fonts/opensans-italic.woff.ttf') format('truetype')
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: url('/content/fonts/opensans-semibolditalic.eot');
|
||||
src: local('Open Sans Semibold Italic'),
|
||||
local('OpenSans-SemiboldItalic'),
|
||||
url('/content/fonts/opensans-semibolditalic.eot?#iefix') format('embedded-opentype'),
|
||||
url('/content/fonts/opensans-semibolditalic.woff') format('woff'),
|
||||
url('/content/fonts/opensans-semibolditalic.ttf') format('truetype')
|
||||
src: url('/Content/fonts/OpenSans-SemiBold.eot');
|
||||
src: local('Open Sans SemiBold'),
|
||||
local('OpenSans-SemiBold'),
|
||||
url('/Content/fonts/OpenSans-SemiBold.eot?#iefix') format('embedded-opentype'),
|
||||
url('/Content/fonts/OpenSans-SemiBold.woff') format('woff'),
|
||||
url('/Content/fonts/OpenSans-SemiBold.ttf') format('truetype')
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
body {
|
||||
background-color : #1c1c1c;
|
||||
background-image : url('../content/images/pattern.png');
|
||||
background-image : url('../Content/Images/pattern.png');
|
||||
margin-bottom : 100px;
|
||||
p {
|
||||
font-size : 0.9em;
|
||||
|
@ -5,6 +5,6 @@ define(
|
||||
'handlebars'
|
||||
], function (Handlebars) {
|
||||
Handlebars.registerHelper('defaultImg', function () {
|
||||
return new Handlebars.SafeString('onerror=this.src=\'/content/images/poster-dark.jpg\';');
|
||||
return new Handlebars.SafeString('onerror=this.src=\'/Content/Images/poster-dark.jpg\';');
|
||||
});
|
||||
});
|
||||
|
@ -3,17 +3,17 @@
|
||||
<head runat="server">
|
||||
<title>NzbDrone</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<link href="/Content/Bootstrap.css" rel='stylesheet' type='text/css'/>
|
||||
<link href="/Content/bootstrap.css" rel='stylesheet' type='text/css'/>
|
||||
<link href="/Content/bootstrap.toggle-switch.css" rel='stylesheet' type='text/css'/>
|
||||
<link href="/Content/Messenger/messenger.css" rel='stylesheet' type='text/css'/>
|
||||
<link href="/Content/Messenger/messenger.future.css" rel='stylesheet' type='text/css'/>
|
||||
<link href="/Content/fullcalendar.css" rel='stylesheet' type='text/css'>
|
||||
<link href="/Content/base.css" rel='stylesheet' type='text/css'/>
|
||||
<link href="/Cells/Cells.css" rel='stylesheet' type='text/css'>
|
||||
<link href="/Series/Series.css" rel='stylesheet' type='text/css'/>
|
||||
<link href="/Cells/cells.css" rel='stylesheet' type='text/css'>
|
||||
<link href="/Series/series.css" rel='stylesheet' type='text/css'/>
|
||||
<link href="/Logs/logs.css" rel='stylesheet' type='text/css'/>
|
||||
<link href="/Settings/settings.css" rel='stylesheet' type='text/css'/>
|
||||
<link href="/AddSeries/Addseries.css" rel='stylesheet' type='text/css'/>
|
||||
<link href="/AddSeries/addSeries.css" rel='stylesheet' type='text/css'/>
|
||||
<link href="/Calendar/calendar.css" rel='stylesheet' type='text/css'/>
|
||||
<link href="/Content/overrides.css" rel='stylesheet' type='text/css'/>
|
||||
|
||||
|
@ -73,7 +73,7 @@ require.config({
|
||||
backbone: {
|
||||
deps :
|
||||
[
|
||||
'mixins/backbone.ajax',
|
||||
'Mixins/backbone.ajax',
|
||||
'underscore',
|
||||
'$'
|
||||
],
|
||||
@ -87,7 +87,7 @@ require.config({
|
||||
'backbone.deepmodel': {
|
||||
deps:
|
||||
[
|
||||
'mixins/underscore.mixin.deepExtend'
|
||||
'Mixins/underscore.mixin.deepExtend'
|
||||
]
|
||||
},
|
||||
|
||||
@ -96,7 +96,7 @@ require.config({
|
||||
[
|
||||
'backbone',
|
||||
'Handlebars/backbone.marionette.templates',
|
||||
'mixins/AsNamedView'
|
||||
'Mixins/AsNamedView'
|
||||
],
|
||||
|
||||
exports: 'Marionette',
|
||||
|
Loading…
Reference in New Issue
Block a user