Bug fix: slashes in template values (eg: title) breaks file management #145
This commit is contained in:
parent
cae8ca7ef3
commit
46b120ee41
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<Version>6.3.3.1</Version>
|
<Version>6.3.4.1</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -45,8 +45,20 @@ namespace FileManager
|
|||||||
.Replace(">", "");
|
.Replace(">", "");
|
||||||
|
|
||||||
private string formatValue(object value)
|
private string formatValue(object value)
|
||||||
=> ParameterMaxSize.HasValue && ParameterMaxSize.Value > 0
|
{
|
||||||
? value?.ToString().Truncate(ParameterMaxSize.Value)
|
if (value is null)
|
||||||
: value?.ToString();
|
return "";
|
||||||
|
|
||||||
|
// Other illegal characters will be taken care of later. Must take care of slashes now so params can't introduce new folders.
|
||||||
|
// Esp important for file templates.
|
||||||
|
var val = value
|
||||||
|
.ToString()
|
||||||
|
.Replace($"{System.IO.Path.DirectorySeparatorChar}", IllegalCharacterReplacements)
|
||||||
|
.Replace($"{System.IO.Path.AltDirectorySeparatorChar}", IllegalCharacterReplacements);
|
||||||
|
return
|
||||||
|
ParameterMaxSize.HasValue && ParameterMaxSize.Value > 0
|
||||||
|
? val.Truncate(ParameterMaxSize.Value)
|
||||||
|
: val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,8 +16,6 @@ namespace LibationFileManager
|
|||||||
public const string WARNING_NO_TAGS = "Should use tags. Eg: <title>";
|
public const string WARNING_NO_TAGS = "Should use tags. Eg: <title>";
|
||||||
public const string WARNING_HAS_CHAPTER_TAGS = "Chapter tags should only be used in the template used for naming files which are split by chapter. Eg: <ch title>";
|
public const string WARNING_HAS_CHAPTER_TAGS = "Chapter tags should only be used in the template used for naming files which are split by chapter. Eg: <ch title>";
|
||||||
public const string WARNING_NO_CHAPTER_NUMBER_TAG = "Should include chapter number tag in template used for naming files which are split by chapter. Ie: <ch#> or <ch# 0>";
|
public const string WARNING_NO_CHAPTER_NUMBER_TAG = "Should include chapter number tag in template used for naming files which are split by chapter. Ie: <ch#> or <ch# 0>";
|
||||||
// actual possible to se?
|
|
||||||
public const string WARNING_NO_CHAPTER_TAGS = "Should include chapter tags in template used for naming files which are split by chapter. Eg: <ch title>";
|
|
||||||
|
|
||||||
public static Templates Folder { get; } = new FolderTemplate();
|
public static Templates Folder { get; } = new FolderTemplate();
|
||||||
public static Templates File { get; } = new FileTemplate();
|
public static Templates File { get; } = new FileTemplate();
|
||||||
@ -26,6 +24,7 @@ namespace LibationFileManager
|
|||||||
public abstract string Name { get; }
|
public abstract string Name { get; }
|
||||||
public abstract string Description { get; }
|
public abstract string Description { get; }
|
||||||
public abstract string DefaultTemplate { get; }
|
public abstract string DefaultTemplate { get; }
|
||||||
|
protected abstract bool IsChapterized { get; }
|
||||||
|
|
||||||
public abstract IEnumerable<string> GetErrors(string template);
|
public abstract IEnumerable<string> GetErrors(string template);
|
||||||
public bool IsValid(string template) => !GetErrors(template).Any();
|
public bool IsValid(string template) => !GetErrors(template).Any();
|
||||||
@ -33,7 +32,17 @@ namespace LibationFileManager
|
|||||||
public abstract IEnumerable<string> GetWarnings(string template);
|
public abstract IEnumerable<string> GetWarnings(string template);
|
||||||
public bool HasWarnings(string template) => GetWarnings(template).Any();
|
public bool HasWarnings(string template) => GetWarnings(template).Any();
|
||||||
|
|
||||||
public abstract int TagCount(string template);
|
public IEnumerable<TemplateTags> GetTemplateTags()
|
||||||
|
=> TemplateTags.GetAll()
|
||||||
|
// yeah, this line is a little funky but it works when you think through it. also: trust the unit tests
|
||||||
|
.Where(t => IsChapterized || !t.IsChapterOnly);
|
||||||
|
|
||||||
|
public int TagCount(string template)
|
||||||
|
=> GetTemplateTags()
|
||||||
|
// for <id><id> == 1, use:
|
||||||
|
// .Count(t => template.Contains($"<{t.TagName}>"))
|
||||||
|
// .Sum() impl: <id><id> == 2
|
||||||
|
.Sum(t => template.Split($"<{t.TagName}>").Length - 1);
|
||||||
|
|
||||||
public static bool ContainsChapterOnlyTags(string template)
|
public static bool ContainsChapterOnlyTags(string template)
|
||||||
=> TemplateTags.GetAll()
|
=> TemplateTags.GetAll()
|
||||||
@ -59,7 +68,7 @@ namespace LibationFileManager
|
|||||||
return Valid;
|
return Valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IEnumerable<string> getWarnings(string template, bool isChapter)
|
protected IEnumerable<string> getWarnings(string template)
|
||||||
{
|
{
|
||||||
var warnings = GetErrors(template).ToList();
|
var warnings = GetErrors(template).ToList();
|
||||||
if (template is null)
|
if (template is null)
|
||||||
@ -73,28 +82,18 @@ namespace LibationFileManager
|
|||||||
if (TagCount(template) == 0)
|
if (TagCount(template) == 0)
|
||||||
warnings.Add(WARNING_NO_TAGS);
|
warnings.Add(WARNING_NO_TAGS);
|
||||||
|
|
||||||
var containsChapterOnlyTags = ContainsChapterOnlyTags(template);
|
if (!IsChapterized && ContainsChapterOnlyTags(template))
|
||||||
if (isChapter && !containsChapterOnlyTags)
|
|
||||||
warnings.Add(WARNING_NO_CHAPTER_TAGS);
|
|
||||||
if (!isChapter && containsChapterOnlyTags)
|
|
||||||
warnings.Add(WARNING_HAS_CHAPTER_TAGS);
|
warnings.Add(WARNING_HAS_CHAPTER_TAGS);
|
||||||
|
|
||||||
return warnings;
|
return warnings;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static int tagCount(string template, Func<TemplateTags, bool> func)
|
|
||||||
=> TemplateTags.GetAll()
|
|
||||||
.Where(func)
|
|
||||||
// for <id><id> == 1, use:
|
|
||||||
// .Count(t => template.Contains($"<{t.TagName}>"))
|
|
||||||
// .Sum() impl: <id><id> == 2
|
|
||||||
.Sum(t => template.Split($"<{t.TagName}>").Length - 1);
|
|
||||||
|
|
||||||
private class FolderTemplate : Templates
|
private class FolderTemplate : Templates
|
||||||
{
|
{
|
||||||
public override string Name => "Folder Template";
|
public override string Name => "Folder Template";
|
||||||
public override string Description => Configuration.GetDescription(nameof(Configuration.FolderTemplate));
|
public override string Description => Configuration.GetDescription(nameof(Configuration.FolderTemplate));
|
||||||
public override string DefaultTemplate { get; } = "<title short> [<id>]";
|
public override string DefaultTemplate { get; } = "<title short> [<id>]";
|
||||||
|
protected override bool IsChapterized { get; } = false;
|
||||||
|
|
||||||
public override IEnumerable<string> GetErrors(string template)
|
public override IEnumerable<string> GetErrors(string template)
|
||||||
{
|
{
|
||||||
@ -109,9 +108,7 @@ namespace LibationFileManager
|
|||||||
return Valid;
|
return Valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> GetWarnings(string template) => getWarnings(template, false);
|
public override IEnumerable<string> GetWarnings(string template) => getWarnings(template);
|
||||||
|
|
||||||
public override int TagCount(string template) => tagCount(template, t => !t.IsChapterOnly);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FileTemplate : Templates
|
private class FileTemplate : Templates
|
||||||
@ -119,12 +116,11 @@ namespace LibationFileManager
|
|||||||
public override string Name => "File Template";
|
public override string Name => "File Template";
|
||||||
public override string Description => Configuration.GetDescription(nameof(Configuration.FileTemplate));
|
public override string Description => Configuration.GetDescription(nameof(Configuration.FileTemplate));
|
||||||
public override string DefaultTemplate { get; } = "<title> [<id>]";
|
public override string DefaultTemplate { get; } = "<title> [<id>]";
|
||||||
|
protected override bool IsChapterized { get; } = false;
|
||||||
|
|
||||||
public override IEnumerable<string> GetErrors(string template) => getFileErrors(template);
|
public override IEnumerable<string> GetErrors(string template) => getFileErrors(template);
|
||||||
|
|
||||||
public override IEnumerable<string> GetWarnings(string template) => getWarnings(template, false);
|
public override IEnumerable<string> GetWarnings(string template) => getWarnings(template);
|
||||||
|
|
||||||
public override int TagCount(string template) => tagCount(template, t => !t.IsChapterOnly);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ChapterFileTemplate : Templates
|
private class ChapterFileTemplate : Templates
|
||||||
@ -132,26 +128,22 @@ namespace LibationFileManager
|
|||||||
public override string Name => "Chapter File Template";
|
public override string Name => "Chapter File Template";
|
||||||
public override string Description => Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate));
|
public override string Description => Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate));
|
||||||
public override string DefaultTemplate { get; } = "<title> [<id>] - <ch# 0> - <ch title>";
|
public override string DefaultTemplate { get; } = "<title> [<id>] - <ch# 0> - <ch title>";
|
||||||
|
protected override bool IsChapterized { get; } = true;
|
||||||
|
|
||||||
public override IEnumerable<string> GetErrors(string template) => getFileErrors(template);
|
public override IEnumerable<string> GetErrors(string template) => getFileErrors(template);
|
||||||
|
|
||||||
public override IEnumerable<string> GetWarnings(string template)
|
public override IEnumerable<string> GetWarnings(string template)
|
||||||
{
|
{
|
||||||
var warnings = getWarnings(template, true).ToList();
|
var warnings = getWarnings(template).ToList();
|
||||||
if (template is null)
|
if (template is null)
|
||||||
return warnings;
|
return warnings;
|
||||||
|
|
||||||
// recommended to incl. <ch#> or <ch# 0>
|
// recommended to incl. <ch#> or <ch# 0>
|
||||||
if (!ContainsTag(template, TemplateTags.ChNumber.TagName) && !ContainsTag(template, TemplateTags.ChNumber0.TagName))
|
if (!ContainsTag(template, TemplateTags.ChNumber.TagName) && !ContainsTag(template, TemplateTags.ChNumber0.TagName))
|
||||||
{
|
|
||||||
warnings.Remove(WARNING_NO_CHAPTER_TAGS);
|
|
||||||
warnings.Add(WARNING_NO_CHAPTER_NUMBER_TAG);
|
warnings.Add(WARNING_NO_CHAPTER_NUMBER_TAG);
|
||||||
}
|
|
||||||
|
|
||||||
return warnings;
|
return warnings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int TagCount(string template) => tagCount(template, t => true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,9 @@
|
|||||||
this.templateLbl = new System.Windows.Forms.Label();
|
this.templateLbl = new System.Windows.Forms.Label();
|
||||||
this.resetToDefaultBtn = new System.Windows.Forms.Button();
|
this.resetToDefaultBtn = new System.Windows.Forms.Button();
|
||||||
this.outputTb = new System.Windows.Forms.TextBox();
|
this.outputTb = new System.Windows.Forms.TextBox();
|
||||||
|
this.listView1 = new System.Windows.Forms.ListView();
|
||||||
|
this.columnHeader1 = new System.Windows.Forms.ColumnHeader();
|
||||||
|
this.columnHeader2 = new System.Windows.Forms.ColumnHeader();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// saveBtn
|
// saveBtn
|
||||||
@ -93,12 +96,41 @@
|
|||||||
//
|
//
|
||||||
// outputTb
|
// outputTb
|
||||||
//
|
//
|
||||||
this.outputTb.Location = new System.Drawing.Point(12, 153);
|
this.outputTb.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.outputTb.Location = new System.Drawing.Point(346, 56);
|
||||||
this.outputTb.Multiline = true;
|
this.outputTb.Multiline = true;
|
||||||
this.outputTb.Name = "outputTb";
|
this.outputTb.Name = "outputTb";
|
||||||
this.outputTb.ReadOnly = true;
|
this.outputTb.ReadOnly = true;
|
||||||
this.outputTb.Size = new System.Drawing.Size(759, 205);
|
this.outputTb.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||||
this.outputTb.TabIndex = 100;
|
this.outputTb.Size = new System.Drawing.Size(574, 434);
|
||||||
|
this.outputTb.TabIndex = 4;
|
||||||
|
//
|
||||||
|
// listView1
|
||||||
|
//
|
||||||
|
this.listView1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
|
||||||
|
this.columnHeader1,
|
||||||
|
this.columnHeader2});
|
||||||
|
this.listView1.HideSelection = false;
|
||||||
|
this.listView1.Location = new System.Drawing.Point(12, 56);
|
||||||
|
this.listView1.Name = "listView1";
|
||||||
|
this.listView1.Size = new System.Drawing.Size(328, 434);
|
||||||
|
this.listView1.TabIndex = 100;
|
||||||
|
this.listView1.UseCompatibleStateImageBehavior = false;
|
||||||
|
this.listView1.View = System.Windows.Forms.View.Details;
|
||||||
|
//
|
||||||
|
// columnHeader1
|
||||||
|
//
|
||||||
|
this.columnHeader1.Text = "Tag";
|
||||||
|
this.columnHeader1.Width = 90;
|
||||||
|
//
|
||||||
|
// columnHeader2
|
||||||
|
//
|
||||||
|
this.columnHeader2.Text = "Description";
|
||||||
|
this.columnHeader2.Width = 230;
|
||||||
//
|
//
|
||||||
// EditTemplateDialog
|
// EditTemplateDialog
|
||||||
//
|
//
|
||||||
@ -107,6 +139,7 @@
|
|||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.CancelButton = this.cancelBtn;
|
this.CancelButton = this.cancelBtn;
|
||||||
this.ClientSize = new System.Drawing.Size(933, 539);
|
this.ClientSize = new System.Drawing.Size(933, 539);
|
||||||
|
this.Controls.Add(this.listView1);
|
||||||
this.Controls.Add(this.outputTb);
|
this.Controls.Add(this.outputTb);
|
||||||
this.Controls.Add(this.resetToDefaultBtn);
|
this.Controls.Add(this.resetToDefaultBtn);
|
||||||
this.Controls.Add(this.templateLbl);
|
this.Controls.Add(this.templateLbl);
|
||||||
@ -131,5 +164,8 @@
|
|||||||
private System.Windows.Forms.Label templateLbl;
|
private System.Windows.Forms.Label templateLbl;
|
||||||
private System.Windows.Forms.Button resetToDefaultBtn;
|
private System.Windows.Forms.Button resetToDefaultBtn;
|
||||||
private System.Windows.Forms.TextBox outputTb;
|
private System.Windows.Forms.TextBox outputTb;
|
||||||
|
private System.Windows.Forms.ListView listView1;
|
||||||
|
private System.Windows.Forms.ColumnHeader columnHeader1;
|
||||||
|
private System.Windows.Forms.ColumnHeader columnHeader2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,16 +39,32 @@ namespace LibationWinForms.Dialogs
|
|||||||
|
|
||||||
this.templateLbl.Text = template.Description;
|
this.templateLbl.Text = template.Description;
|
||||||
this.templateTb.Text = inputTemplateText;
|
this.templateTb.Text = inputTemplateText;
|
||||||
|
|
||||||
|
// populate list view
|
||||||
|
foreach (var tag in template.GetTemplateTags())
|
||||||
|
listView1.Items.Add(new ListViewItem(new[] { $"<{tag.TagName}>", tag.Description }));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetToDefaultBtn_Click(object sender, EventArgs e) => templateTb.Text = template.DefaultTemplate;
|
private void resetToDefaultBtn_Click(object sender, EventArgs e) => templateTb.Text = template.DefaultTemplate;
|
||||||
|
|
||||||
private void templateTb_TextChanged(object sender, EventArgs e)
|
private void templateTb_TextChanged(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
var t = templateTb.Text;
|
||||||
|
|
||||||
|
var warnings
|
||||||
|
= !template.HasWarnings(t)
|
||||||
|
? ""
|
||||||
|
: "Warnings:\r\n" +
|
||||||
|
template
|
||||||
|
.GetWarnings(t)
|
||||||
|
.Select(err => $"- {err}")
|
||||||
|
.Aggregate((a, b) => $"{a}\r\n{b}");
|
||||||
|
|
||||||
|
|
||||||
var books = config.Books;
|
var books = config.Books;
|
||||||
var folderTemplate = template == Templates.Folder ? templateTb.Text : config.FolderTemplate;
|
var folderTemplate = template == Templates.Folder ? t : config.FolderTemplate;
|
||||||
folderTemplate = folderTemplate.Trim().Trim(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }).Trim();
|
folderTemplate = folderTemplate.Trim().Trim(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }).Trim();
|
||||||
var fileTemplate = template == Templates.Folder ? config.FileTemplate : templateTb.Text;
|
var fileTemplate = template == Templates.Folder ? config.FileTemplate : t;
|
||||||
fileTemplate = fileTemplate.Trim();
|
fileTemplate = fileTemplate.Trim();
|
||||||
var ext = config.DecryptToLossy ? "mp3" : "m4b";
|
var ext = config.DecryptToLossy ? "mp3" : "m4b";
|
||||||
|
|
||||||
@ -56,13 +72,23 @@ namespace LibationWinForms.Dialogs
|
|||||||
|
|
||||||
// this logic should be external
|
// this logic should be external
|
||||||
path = path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
path = path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||||
|
var sing = $"{Path.DirectorySeparatorChar}";
|
||||||
var dbl = $"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}";
|
var dbl = $"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}";
|
||||||
while (path.Contains(dbl))
|
while (path.Contains(dbl))
|
||||||
path = path.Replace(dbl, $"{Path.DirectorySeparatorChar}");
|
path = path.Replace(dbl, sing);
|
||||||
|
|
||||||
|
// once path is finalized
|
||||||
|
const char ZERO_WIDTH_SPACE = '\u200B';
|
||||||
|
path = path.Replace(sing, $"{ZERO_WIDTH_SPACE}{sing}");
|
||||||
|
// result: can wrap long paths. eg:
|
||||||
|
// |-- LINE WRAP BOUNDARIES --|
|
||||||
|
// \books\author with a very <= normal line break on space between words
|
||||||
|
// long name\narrator narrator
|
||||||
|
// \title <= line break on the zero-with space we added before slashes
|
||||||
|
|
||||||
var book = new DataLayer.Book(
|
var book = new DataLayer.Book(
|
||||||
new DataLayer.AudibleProductId("123456789"),
|
new DataLayer.AudibleProductId("123456789"),
|
||||||
"A Study in Scarlet",
|
"A Study in Scarlet: A Sherlock Holmes Novel",
|
||||||
"Fake description",
|
"Fake description",
|
||||||
1234,
|
1234,
|
||||||
DataLayer.ContentType.Product,
|
DataLayer.ContentType.Product,
|
||||||
@ -78,6 +104,9 @@ namespace LibationWinForms.Dialogs
|
|||||||
var libraryBook = new DataLayer.LibraryBook(book, DateTime.Now, "my account");
|
var libraryBook = new DataLayer.LibraryBook(book, DateTime.Now, "my account");
|
||||||
|
|
||||||
outputTb.Text = @$"
|
outputTb.Text = @$"
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
{books}
|
{books}
|
||||||
{folderTemplate}
|
{folderTemplate}
|
||||||
{fileTemplate}
|
{fileTemplate}
|
||||||
@ -89,14 +118,21 @@ namespace LibationWinForms.Dialogs
|
|||||||
{book.AuthorNames}
|
{book.AuthorNames}
|
||||||
{book.NarratorNames}
|
{book.NarratorNames}
|
||||||
series: {"Sherlock Holmes"}
|
series: {"Sherlock Holmes"}
|
||||||
";
|
|
||||||
|
{warnings}
|
||||||
|
|
||||||
|
".Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveBtn_Click(object sender, EventArgs e)
|
private void saveBtn_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (!template.IsValid(templateTb.Text))
|
if (!template.IsValid(templateTb.Text))
|
||||||
{
|
{
|
||||||
MessageBox.Show("This template text is not valid.", "Invalid", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
var errors = template
|
||||||
|
.GetErrors(templateTb.Text)
|
||||||
|
.Select(err => $"- {err}")
|
||||||
|
.Aggregate((a, b) => $"{a}\r\n{b}");
|
||||||
|
MessageBox.Show($"This template text is not valid. Errors:\r\n{errors}", "Invalid", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -104,5 +104,13 @@ namespace FileTemplateTests
|
|||||||
|
|
||||||
return fileTemplate.GetFilePath();
|
return fileTemplate.GetFilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void remove_slashes()
|
||||||
|
{
|
||||||
|
var fileTemplate = new FileTemplate(@"\foo\<title>.txt");
|
||||||
|
fileTemplate.AddParameterReplacement("title", @"s\l/a\s/h\e/s");
|
||||||
|
fileTemplate.GetFilePath().Should().Be(@"\foo\slashes.txt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user