diff --git a/AppScaffolding/AppScaffolding.csproj b/AppScaffolding/AppScaffolding.csproj
index 8f7f0fae..444aabf4 100644
--- a/AppScaffolding/AppScaffolding.csproj
+++ b/AppScaffolding/AppScaffolding.csproj
@@ -3,7 +3,7 @@
net5.0
- 6.3.3.1
+ 6.3.4.1
diff --git a/FileManager/FileTemplate.cs b/FileManager/FileTemplate.cs
index 8fab2de0..6848aba0 100644
--- a/FileManager/FileTemplate.cs
+++ b/FileManager/FileTemplate.cs
@@ -45,8 +45,20 @@ namespace FileManager
.Replace(">", "");
private string formatValue(object value)
- => ParameterMaxSize.HasValue && ParameterMaxSize.Value > 0
- ? value?.ToString().Truncate(ParameterMaxSize.Value)
- : value?.ToString();
+ {
+ if (value is null)
+ 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;
+ }
}
}
diff --git a/LibationFileManager/Templates.cs b/LibationFileManager/Templates.cs
index d33ae7fc..934a7d08 100644
--- a/LibationFileManager/Templates.cs
+++ b/LibationFileManager/Templates.cs
@@ -16,8 +16,6 @@ namespace LibationFileManager
public const string WARNING_NO_TAGS = "Should use tags. Eg: ";
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: ";
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: or ";
- // 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: ";
public static Templates Folder { get; } = new FolderTemplate();
public static Templates File { get; } = new FileTemplate();
@@ -26,6 +24,7 @@ namespace LibationFileManager
public abstract string Name { get; }
public abstract string Description { get; }
public abstract string DefaultTemplate { get; }
+ protected abstract bool IsChapterized { get; }
public abstract IEnumerable GetErrors(string template);
public bool IsValid(string template) => !GetErrors(template).Any();
@@ -33,7 +32,17 @@ namespace LibationFileManager
public abstract IEnumerable GetWarnings(string template);
public bool HasWarnings(string template) => GetWarnings(template).Any();
- public abstract int TagCount(string template);
+ public IEnumerable 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 == 1, use:
+ // .Count(t => template.Contains($"<{t.TagName}>"))
+ // .Sum() impl: == 2
+ .Sum(t => template.Split($"<{t.TagName}>").Length - 1);
public static bool ContainsChapterOnlyTags(string template)
=> TemplateTags.GetAll()
@@ -59,7 +68,7 @@ namespace LibationFileManager
return Valid;
}
- protected IEnumerable getWarnings(string template, bool isChapter)
+ protected IEnumerable getWarnings(string template)
{
var warnings = GetErrors(template).ToList();
if (template is null)
@@ -73,28 +82,18 @@ namespace LibationFileManager
if (TagCount(template) == 0)
warnings.Add(WARNING_NO_TAGS);
- var containsChapterOnlyTags = ContainsChapterOnlyTags(template);
- if (isChapter && !containsChapterOnlyTags)
- warnings.Add(WARNING_NO_CHAPTER_TAGS);
- if (!isChapter && containsChapterOnlyTags)
+ if (!IsChapterized && ContainsChapterOnlyTags(template))
warnings.Add(WARNING_HAS_CHAPTER_TAGS);
return warnings;
}
- protected static int tagCount(string template, Func func)
- => TemplateTags.GetAll()
- .Where(func)
- // for == 1, use:
- // .Count(t => template.Contains($"<{t.TagName}>"))
- // .Sum() impl: == 2
- .Sum(t => template.Split($"<{t.TagName}>").Length - 1);
-
private class FolderTemplate : Templates
{
public override string Name => "Folder Template";
public override string Description => Configuration.GetDescription(nameof(Configuration.FolderTemplate));
public override string DefaultTemplate { get; } = " []";
+ protected override bool IsChapterized { get; } = false;
public override IEnumerable GetErrors(string template)
{
@@ -109,9 +108,7 @@ namespace LibationFileManager
return Valid;
}
- public override IEnumerable GetWarnings(string template) => getWarnings(template, false);
-
- public override int TagCount(string template) => tagCount(template, t => !t.IsChapterOnly);
+ public override IEnumerable GetWarnings(string template) => getWarnings(template);
}
private class FileTemplate : Templates
@@ -119,12 +116,11 @@ namespace LibationFileManager
public override string Name => "File Template";
public override string Description => Configuration.GetDescription(nameof(Configuration.FileTemplate));
public override string DefaultTemplate { get; } = " []";
+ protected override bool IsChapterized { get; } = false;
public override IEnumerable GetErrors(string template) => getFileErrors(template);
- public override IEnumerable GetWarnings(string template) => getWarnings(template, false);
-
- public override int TagCount(string template) => tagCount(template, t => !t.IsChapterOnly);
+ public override IEnumerable GetWarnings(string template) => getWarnings(template);
}
private class ChapterFileTemplate : Templates
@@ -132,26 +128,22 @@ namespace LibationFileManager
public override string Name => "Chapter File Template";
public override string Description => Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate));
public override string DefaultTemplate { get; } = " [] - - ";
-
+ protected override bool IsChapterized { get; } = true;
+
public override IEnumerable GetErrors(string template) => getFileErrors(template);
public override IEnumerable GetWarnings(string template)
{
- var warnings = getWarnings(template, true).ToList();
+ var warnings = getWarnings(template).ToList();
if (template is null)
return warnings;
// recommended to incl. or
if (!ContainsTag(template, TemplateTags.ChNumber.TagName) && !ContainsTag(template, TemplateTags.ChNumber0.TagName))
- {
- warnings.Remove(WARNING_NO_CHAPTER_TAGS);
warnings.Add(WARNING_NO_CHAPTER_NUMBER_TAG);
- }
return warnings;
}
-
- public override int TagCount(string template) => tagCount(template, t => true);
}
}
}
diff --git a/LibationWinForms/Dialogs/EditTemplateDialog.Designer.cs b/LibationWinForms/Dialogs/EditTemplateDialog.Designer.cs
index 79f072d9..7bf11cce 100644
--- a/LibationWinForms/Dialogs/EditTemplateDialog.Designer.cs
+++ b/LibationWinForms/Dialogs/EditTemplateDialog.Designer.cs
@@ -34,6 +34,9 @@
this.templateLbl = new System.Windows.Forms.Label();
this.resetToDefaultBtn = new System.Windows.Forms.Button();
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();
//
// saveBtn
@@ -93,12 +96,41 @@
//
// 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.Name = "outputTb";
this.outputTb.ReadOnly = true;
- this.outputTb.Size = new System.Drawing.Size(759, 205);
- this.outputTb.TabIndex = 100;
+ this.outputTb.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+ 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
//
@@ -107,6 +139,7 @@
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelBtn;
this.ClientSize = new System.Drawing.Size(933, 539);
+ this.Controls.Add(this.listView1);
this.Controls.Add(this.outputTb);
this.Controls.Add(this.resetToDefaultBtn);
this.Controls.Add(this.templateLbl);
@@ -131,5 +164,8 @@
private System.Windows.Forms.Label templateLbl;
private System.Windows.Forms.Button resetToDefaultBtn;
private System.Windows.Forms.TextBox outputTb;
+ private System.Windows.Forms.ListView listView1;
+ private System.Windows.Forms.ColumnHeader columnHeader1;
+ private System.Windows.Forms.ColumnHeader columnHeader2;
}
}
\ No newline at end of file
diff --git a/LibationWinForms/Dialogs/EditTemplateDialog.cs b/LibationWinForms/Dialogs/EditTemplateDialog.cs
index aa317cef..03c668a6 100644
--- a/LibationWinForms/Dialogs/EditTemplateDialog.cs
+++ b/LibationWinForms/Dialogs/EditTemplateDialog.cs
@@ -39,16 +39,32 @@ namespace LibationWinForms.Dialogs
this.templateLbl.Text = template.Description;
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 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 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();
- var fileTemplate = template == Templates.Folder ? config.FileTemplate : templateTb.Text;
+ var fileTemplate = template == Templates.Folder ? config.FileTemplate : t;
fileTemplate = fileTemplate.Trim();
var ext = config.DecryptToLossy ? "mp3" : "m4b";
@@ -56,13 +72,23 @@ namespace LibationWinForms.Dialogs
// this logic should be external
path = path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
+ var sing = $"{Path.DirectorySeparatorChar}";
var dbl = $"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}";
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(
new DataLayer.AudibleProductId("123456789"),
- "A Study in Scarlet",
+ "A Study in Scarlet: A Sherlock Holmes Novel",
"Fake description",
1234,
DataLayer.ContentType.Product,
@@ -78,6 +104,9 @@ namespace LibationWinForms.Dialogs
var libraryBook = new DataLayer.LibraryBook(book, DateTime.Now, "my account");
outputTb.Text = @$"
+
+Example:
+
{books}
{folderTemplate}
{fileTemplate}
@@ -89,14 +118,21 @@ namespace LibationWinForms.Dialogs
{book.AuthorNames}
{book.NarratorNames}
series: {"Sherlock Holmes"}
-";
+
+{warnings}
+
+".Trim();
}
private void saveBtn_Click(object sender, EventArgs e)
{
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;
}
diff --git a/_Tests/FileManager.Tests/FileTemplateTests.cs b/_Tests/FileManager.Tests/FileTemplateTests.cs
index 5567795a..c958e9b1 100644
--- a/_Tests/FileManager.Tests/FileTemplateTests.cs
+++ b/_Tests/FileManager.Tests/FileTemplateTests.cs
@@ -104,5 +104,13 @@ namespace FileTemplateTests
return fileTemplate.GetFilePath();
}
+
+ [TestMethod]
+ public void remove_slashes()
+ {
+ var fileTemplate = new FileTemplate(@"\foo\.txt");
+ fileTemplate.AddParameterReplacement("title", @"s\l/a\s/h\e/s");
+ fileTemplate.GetFilePath().Should().Be(@"\foo\slashes.txt");
+ }
}
}