mirror of
https://github.com/rmcrackan/Libation.git
synced 2025-12-31 01:48:39 -05:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46b120ee41 | ||
|
|
cae8ca7ef3 | ||
|
|
904665da7f |
@@ -3,7 +3,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Version>6.3.2.1</Version>
|
||||
<Version>6.3.4.1</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -185,6 +185,9 @@ namespace DataLayer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_seriesLink is null)
|
||||
return "";
|
||||
|
||||
// first: alphabetical by name
|
||||
var withNames = _seriesLink
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s.Series.Name))
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace LibationFileManager
|
||||
|
||||
private string getTemplate(string settingName, Templates templ)
|
||||
{
|
||||
var value = persistentDictionary.GetString(settingName).Trim();
|
||||
var value = persistentDictionary.GetString(settingName)?.Trim();
|
||||
return templ.IsValid(value) ? value : templ.DefaultTemplate;
|
||||
}
|
||||
private void setTemplate(string settingName, Templates templ, string newValue)
|
||||
|
||||
@@ -6,15 +6,43 @@ namespace LibationFileManager
|
||||
{
|
||||
public abstract class Templates
|
||||
{
|
||||
protected static string[] Valid => Array.Empty<string>();
|
||||
public const string ERROR_NULL_IS_INVALID = "Null template is invalid.";
|
||||
public const string ERROR_FULL_PATH_IS_INVALID = @"No full paths allowed. Eg: should not start with C:\";
|
||||
public const string ERROR_INVALID_FILE_NAME_CHAR = @"Only file name friendly characters allowed. Eg: no colons or slashes";
|
||||
|
||||
public const string WARNING_EMPTY = "Template is empty.";
|
||||
public const string WARNING_WHITE_SPACE = "Template is white space.";
|
||||
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_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 static Templates Folder { get; } = new FolderTemplate();
|
||||
public static Templates File { get; } = new FileTemplate();
|
||||
public static Templates ChapterFile { get; } = new ChapterFileTemplate();
|
||||
|
||||
public abstract string Name { get; }
|
||||
public abstract string Description { get; }
|
||||
public abstract string DefaultTemplate { get; }
|
||||
protected abstract bool IsChapterized { get; }
|
||||
|
||||
public abstract bool IsValid(string template);
|
||||
public abstract bool IsRecommended(string template);
|
||||
public abstract int TagCount(string template);
|
||||
public abstract IEnumerable<string> GetErrors(string template);
|
||||
public bool IsValid(string template) => !GetErrors(template).Any();
|
||||
|
||||
public abstract IEnumerable<string> GetWarnings(string template);
|
||||
public bool HasWarnings(string template) => GetWarnings(template).Any();
|
||||
|
||||
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)
|
||||
=> TemplateTags.GetAll()
|
||||
@@ -23,66 +51,99 @@ namespace LibationFileManager
|
||||
|
||||
public static bool ContainsTag(string template, string tag) => template.Contains($"<{tag}>");
|
||||
|
||||
protected static bool fileIsValid(string template)
|
||||
protected static string[] getFileErrors(string template)
|
||||
{
|
||||
// File name only; not path. all other path chars are valid enough to pass this check and will be handled on final save.
|
||||
|
||||
// null is invalid. whitespace is valid but not recommended
|
||||
=> template is not null
|
||||
&& !template.Contains(':')
|
||||
&& !template.Contains(System.IO.Path.DirectorySeparatorChar)
|
||||
&& !template.Contains(System.IO.Path.AltDirectorySeparatorChar);
|
||||
if (template is null)
|
||||
return new[] { ERROR_NULL_IS_INVALID };
|
||||
|
||||
protected bool isRecommended(string template, bool isChapter)
|
||||
=> IsValid(template)
|
||||
&& !string.IsNullOrWhiteSpace(template)
|
||||
&& TagCount(template) > 0
|
||||
&& ContainsChapterOnlyTags(template) == isChapter;
|
||||
if (template.Contains(':')
|
||||
|| template.Contains(System.IO.Path.DirectorySeparatorChar)
|
||||
|| template.Contains(System.IO.Path.AltDirectorySeparatorChar)
|
||||
)
|
||||
return new[] { ERROR_INVALID_FILE_NAME_CHAR };
|
||||
|
||||
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);
|
||||
return Valid;
|
||||
}
|
||||
|
||||
protected IEnumerable<string> getWarnings(string template)
|
||||
{
|
||||
var warnings = GetErrors(template).ToList();
|
||||
if (template is null)
|
||||
return warnings;
|
||||
|
||||
if (string.IsNullOrEmpty(template))
|
||||
warnings.Add(WARNING_EMPTY);
|
||||
else if (string.IsNullOrWhiteSpace(template))
|
||||
warnings.Add(WARNING_WHITE_SPACE);
|
||||
|
||||
if (TagCount(template) == 0)
|
||||
warnings.Add(WARNING_NO_TAGS);
|
||||
|
||||
if (!IsChapterized && ContainsChapterOnlyTags(template))
|
||||
warnings.Add(WARNING_HAS_CHAPTER_TAGS);
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
private class FolderTemplate : Templates
|
||||
{
|
||||
public override string Name => "Folder Template";
|
||||
public override string Description => Configuration.GetDescription(nameof(Configuration.FolderTemplate));
|
||||
public override string DefaultTemplate { get; } = "<title short> [<id>]";
|
||||
protected override bool IsChapterized { get; } = false;
|
||||
|
||||
public override bool IsValid(string template)
|
||||
// must be relative. no colons. all other path chars are valid enough to pass this check and will be handled on final save.
|
||||
public override IEnumerable<string> GetErrors(string template)
|
||||
{
|
||||
// null is invalid. whitespace is valid but not recommended
|
||||
=> template is not null
|
||||
&& !template.Contains(':');
|
||||
if (template is null)
|
||||
return new[] { ERROR_NULL_IS_INVALID };
|
||||
|
||||
public override bool IsRecommended(string template) => isRecommended(template, false);
|
||||
// must be relative. no colons. all other path chars are valid enough to pass this check and will be handled on final save.
|
||||
if (template.Contains(':'))
|
||||
return new[] { ERROR_FULL_PATH_IS_INVALID };
|
||||
|
||||
public override int TagCount(string template) => tagCount(template, t => !t.IsChapterOnly);
|
||||
return Valid;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> GetWarnings(string template) => getWarnings(template);
|
||||
}
|
||||
|
||||
private class FileTemplate : Templates
|
||||
{
|
||||
public override string DefaultTemplate { get; } = "<title> [<id>]";
|
||||
public override string Name => "File Template";
|
||||
public override string Description => Configuration.GetDescription(nameof(Configuration.FileTemplate));
|
||||
public override string DefaultTemplate { get; } = "<title> [<id>]";
|
||||
protected override bool IsChapterized { get; } = false;
|
||||
|
||||
public override bool IsValid(string template) => fileIsValid(template);
|
||||
public override IEnumerable<string> GetErrors(string template) => getFileErrors(template);
|
||||
|
||||
public override bool IsRecommended(string template) => isRecommended(template, false);
|
||||
|
||||
public override int TagCount(string template) => tagCount(template, t => !t.IsChapterOnly);
|
||||
public override IEnumerable<string> GetWarnings(string template) => getWarnings(template);
|
||||
}
|
||||
|
||||
private class ChapterFileTemplate : Templates
|
||||
{
|
||||
public override string Name => "Chapter File Template";
|
||||
public override string Description => Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate));
|
||||
public override string DefaultTemplate { get; } = "<title> [<id>] - <ch# 0> - <ch title>";
|
||||
protected override bool IsChapterized { get; } = true;
|
||||
|
||||
public override bool IsValid(string template) => fileIsValid(template);
|
||||
public override IEnumerable<string> GetErrors(string template) => getFileErrors(template);
|
||||
|
||||
public override IEnumerable<string> GetWarnings(string template)
|
||||
{
|
||||
var warnings = getWarnings(template).ToList();
|
||||
if (template is null)
|
||||
return warnings;
|
||||
|
||||
public override bool IsRecommended(string template)
|
||||
=> isRecommended(template, true)
|
||||
// recommended to incl. <ch#> or <ch# 0>
|
||||
&& (ContainsTag(template, TemplateTags.ChNumber.TagName) || ContainsTag(template, TemplateTags.ChNumber0.TagName));
|
||||
if (!ContainsTag(template, TemplateTags.ChNumber.TagName) && !ContainsTag(template, TemplateTags.ChNumber0.TagName))
|
||||
warnings.Add(WARNING_NO_CHAPTER_NUMBER_TAG);
|
||||
|
||||
public override int TagCount(string template) => tagCount(template, t => true);
|
||||
return warnings;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
171
LibationWinForms/Dialogs/EditTemplateDialog.Designer.cs
generated
Normal file
171
LibationWinForms/Dialogs/EditTemplateDialog.Designer.cs
generated
Normal file
@@ -0,0 +1,171 @@
|
||||
namespace LibationWinForms.Dialogs
|
||||
{
|
||||
partial class EditTemplateDialog
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.saveBtn = new System.Windows.Forms.Button();
|
||||
this.cancelBtn = new System.Windows.Forms.Button();
|
||||
this.templateTb = new System.Windows.Forms.TextBox();
|
||||
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
|
||||
//
|
||||
this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.saveBtn.Location = new System.Drawing.Point(714, 496);
|
||||
this.saveBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.saveBtn.Name = "saveBtn";
|
||||
this.saveBtn.Size = new System.Drawing.Size(88, 27);
|
||||
this.saveBtn.TabIndex = 98;
|
||||
this.saveBtn.Text = "Save";
|
||||
this.saveBtn.UseVisualStyleBackColor = true;
|
||||
this.saveBtn.Click += new System.EventHandler(this.saveBtn_Click);
|
||||
//
|
||||
// cancelBtn
|
||||
//
|
||||
this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.cancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.cancelBtn.Location = new System.Drawing.Point(832, 496);
|
||||
this.cancelBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.cancelBtn.Name = "cancelBtn";
|
||||
this.cancelBtn.Size = new System.Drawing.Size(88, 27);
|
||||
this.cancelBtn.TabIndex = 99;
|
||||
this.cancelBtn.Text = "Cancel";
|
||||
this.cancelBtn.UseVisualStyleBackColor = true;
|
||||
this.cancelBtn.Click += new System.EventHandler(this.cancelBtn_Click);
|
||||
//
|
||||
// templateTb
|
||||
//
|
||||
this.templateTb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.templateTb.Location = new System.Drawing.Point(12, 27);
|
||||
this.templateTb.Name = "templateTb";
|
||||
this.templateTb.Size = new System.Drawing.Size(779, 23);
|
||||
this.templateTb.TabIndex = 1;
|
||||
this.templateTb.TextChanged += new System.EventHandler(this.templateTb_TextChanged);
|
||||
//
|
||||
// templateLbl
|
||||
//
|
||||
this.templateLbl.AutoSize = true;
|
||||
this.templateLbl.Location = new System.Drawing.Point(12, 9);
|
||||
this.templateLbl.Name = "templateLbl";
|
||||
this.templateLbl.Size = new System.Drawing.Size(89, 15);
|
||||
this.templateLbl.TabIndex = 0;
|
||||
this.templateLbl.Text = "[template desc]";
|
||||
//
|
||||
// resetToDefaultBtn
|
||||
//
|
||||
this.resetToDefaultBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.resetToDefaultBtn.Location = new System.Drawing.Point(797, 26);
|
||||
this.resetToDefaultBtn.Name = "resetToDefaultBtn";
|
||||
this.resetToDefaultBtn.Size = new System.Drawing.Size(124, 23);
|
||||
this.resetToDefaultBtn.TabIndex = 2;
|
||||
this.resetToDefaultBtn.Text = "Reset to default";
|
||||
this.resetToDefaultBtn.UseVisualStyleBackColor = true;
|
||||
this.resetToDefaultBtn.Click += new System.EventHandler(this.resetToDefaultBtn_Click);
|
||||
//
|
||||
// outputTb
|
||||
//
|
||||
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.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
|
||||
//
|
||||
this.AcceptButton = this.saveBtn;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
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);
|
||||
this.Controls.Add(this.templateTb);
|
||||
this.Controls.Add(this.cancelBtn);
|
||||
this.Controls.Add(this.saveBtn);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
|
||||
this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.Name = "EditTemplateDialog";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Edit Template";
|
||||
this.Load += new System.EventHandler(this.EditTemplateDialog_Load);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
private System.Windows.Forms.Button saveBtn;
|
||||
private System.Windows.Forms.Button cancelBtn;
|
||||
private System.Windows.Forms.TextBox templateTb;
|
||||
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;
|
||||
}
|
||||
}
|
||||
151
LibationWinForms/Dialogs/EditTemplateDialog.cs
Normal file
151
LibationWinForms/Dialogs/EditTemplateDialog.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using Dinah.Core;
|
||||
using LibationFileManager;
|
||||
|
||||
namespace LibationWinForms.Dialogs
|
||||
{
|
||||
public partial class EditTemplateDialog : Form
|
||||
{
|
||||
public string TemplateText { get; private set; }
|
||||
|
||||
private Configuration config { get; } = Configuration.Instance;
|
||||
|
||||
private Templates template { get; }
|
||||
private string inputTemplateText { get; }
|
||||
|
||||
public EditTemplateDialog() => InitializeComponent();
|
||||
public EditTemplateDialog(Templates template, string inputTemplateText) : this()
|
||||
{
|
||||
this.template = ArgumentValidator.EnsureNotNull(template, nameof(template));
|
||||
this.inputTemplateText = inputTemplateText ?? "";
|
||||
}
|
||||
|
||||
private void EditTemplateDialog_Load(object sender, EventArgs e)
|
||||
{
|
||||
if (this.DesignMode)
|
||||
return;
|
||||
|
||||
if (template is null)
|
||||
{
|
||||
MessageBoxAlertAdmin.Show($"Programming error. {nameof(EditTemplateDialog)} was not created correctly", "Edit template error", new NullReferenceException($"{nameof(template)} is null"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.Text = $"Edit {template.Name}";
|
||||
|
||||
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 ? t : config.FolderTemplate;
|
||||
folderTemplate = folderTemplate.Trim().Trim(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }).Trim();
|
||||
var fileTemplate = template == Templates.Folder ? config.FileTemplate : t;
|
||||
fileTemplate = fileTemplate.Trim();
|
||||
var ext = config.DecryptToLossy ? "mp3" : "m4b";
|
||||
|
||||
var path = Path.Combine(books, folderTemplate, $"{fileTemplate}.{ext}");
|
||||
|
||||
// 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, 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 Sherlock Holmes Novel",
|
||||
"Fake description",
|
||||
1234,
|
||||
DataLayer.ContentType.Product,
|
||||
new List<DataLayer.Contributor>
|
||||
{
|
||||
new("Arthur Conan Doyle"),
|
||||
new("Stephen Fry - introductions")
|
||||
},
|
||||
new List<DataLayer.Contributor> { new("Stephen Fry") },
|
||||
new DataLayer.Category(new DataLayer.AudibleCategoryId("cat12345"), "Mystery"),
|
||||
"us"
|
||||
);
|
||||
var libraryBook = new DataLayer.LibraryBook(book, DateTime.Now, "my account");
|
||||
|
||||
outputTb.Text = @$"
|
||||
|
||||
Example:
|
||||
|
||||
{books}
|
||||
{folderTemplate}
|
||||
{fileTemplate}
|
||||
{ext}
|
||||
{path}
|
||||
|
||||
{book.AudibleProductId}
|
||||
{book.Title}
|
||||
{book.AuthorNames}
|
||||
{book.NarratorNames}
|
||||
series: {"Sherlock Holmes"}
|
||||
|
||||
{warnings}
|
||||
|
||||
".Trim();
|
||||
}
|
||||
|
||||
private void saveBtn_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!template.IsValid(templateTb.Text))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
TemplateText = templateTb.Text;
|
||||
|
||||
this.DialogResult = DialogResult.OK;
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void cancelBtn_Click(object sender, EventArgs e)
|
||||
{
|
||||
this.DialogResult = DialogResult.Cancel;
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
60
LibationWinForms/Dialogs/EditTemplateDialog.resx
Normal file
60
LibationWinForms/Dialogs/EditTemplateDialog.resx
Normal file
@@ -0,0 +1,60 @@
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -103,25 +103,22 @@ namespace LibationWinForms.Dialogs
|
||||
|
||||
private void logsBtn_Click(object sender, EventArgs e) => Go.To.Folder(Configuration.Instance.LibationFiles);
|
||||
|
||||
private void folderTemplateBtn_Click(object sender, EventArgs e)
|
||||
private void folderTemplateBtn_Click(object sender, EventArgs e) => editTemplate(Templates.Folder, folderTemplateTb);
|
||||
private void fileTemplateBtn_Click(object sender, EventArgs e) => editTemplate(Templates.File, fileTemplateTb);
|
||||
private void chapterFileTemplateBtn_Click(object sender, EventArgs e) => editTemplate(Templates.ChapterFile, chapterFileTemplateTb);
|
||||
private static void editTemplate(Templates template, TextBox textBox)
|
||||
{
|
||||
#if !DEBUG
|
||||
TEMP_TEMP_TEMP();
|
||||
return;
|
||||
#endif
|
||||
|
||||
var form = new EditTemplateDialog(template, textBox.Text);
|
||||
if (form.ShowDialog() == DialogResult.OK)
|
||||
textBox.Text = form.TemplateText;
|
||||
}
|
||||
|
||||
private void fileTemplateBtn_Click(object sender, EventArgs e)
|
||||
{
|
||||
TEMP_TEMP_TEMP();
|
||||
|
||||
}
|
||||
|
||||
private void chapterFileTemplateBtn_Click(object sender, EventArgs e)
|
||||
{
|
||||
TEMP_TEMP_TEMP();
|
||||
|
||||
}
|
||||
|
||||
private void TEMP_TEMP_TEMP()
|
||||
private static void TEMP_TEMP_TEMP()
|
||||
=> MessageBox.Show("Sorry, not yet. Coming soon :)");
|
||||
|
||||
private void saveBtn_Click(object sender, EventArgs e)
|
||||
@@ -129,31 +126,34 @@ namespace LibationWinForms.Dialogs
|
||||
var newBooks = booksSelectControl.SelectedDirectory;
|
||||
|
||||
#region validation
|
||||
static void validationError(string text, string caption)
|
||||
=> MessageBox.Show(text, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
if (string.IsNullOrWhiteSpace(newBooks))
|
||||
{
|
||||
MessageBox.Show("Cannot set Books Location to blank", "Location is blank", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
validationError("Cannot set Books Location to blank", "Location is blank");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(newBooks) && booksSelectControl.SelectedDirectoryIsCustom)
|
||||
{
|
||||
MessageBox.Show($"Not saving change to Books location. This folder does not exist:\r\n{newBooks}", "Folder does not exist", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
validationError($"Not saving change to Books location. This folder does not exist:\r\n{newBooks}", "Folder does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
// these 3 should do nothing. Configuration will only init these with a valid value. EditTemplateDialog ensures valid before returning
|
||||
if (!Templates.Folder.IsValid(folderTemplateTb.Text))
|
||||
{
|
||||
MessageBox.Show($"Not saving change to folder naming template. Invalid format.", "Invalid folder template", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
validationError($"Not saving change to folder naming template. Invalid format.", "Invalid folder template");
|
||||
return;
|
||||
}
|
||||
if (!Templates.File.IsValid(fileTemplateTb.Text))
|
||||
{
|
||||
MessageBox.Show($"Not saving change to file naming template. Invalid format.", "Invalid file template", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
validationError($"Not saving change to file naming template. Invalid format.", "Invalid file template");
|
||||
return;
|
||||
}
|
||||
if (!Templates.ChapterFile.IsValid(chapterFileTemplateTb.Text))
|
||||
{
|
||||
MessageBox.Show($"Not saving change to chapter file naming template. Invalid format.", "Invalid chapter file template", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
validationError($"Not saving change to chapter file naming template. Invalid format.", "Invalid chapter file template");
|
||||
return;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -104,5 +104,13 @@ namespace FileTemplateTests
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,38 @@ namespace TemplatesTests
|
||||
|
||||
namespace Templates_Folder_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class GetErrors
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_is_invalid() => Tests(null, new[] { Templates.ERROR_NULL_IS_INVALID });
|
||||
|
||||
[TestMethod]
|
||||
public void empty_is_valid() => valid_tests("");
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_is_valid() => valid_tests(" ");
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"foo")]
|
||||
[DataRow(@"\foo")]
|
||||
[DataRow(@"foo\")]
|
||||
[DataRow(@"\foo\")]
|
||||
[DataRow(@"foo\bar")]
|
||||
[DataRow(@"<id>")]
|
||||
[DataRow(@"<id>\<title>")]
|
||||
public void valid_tests(string template) => Tests(template, Array.Empty<string>());
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"C:\", Templates.ERROR_FULL_PATH_IS_INVALID)]
|
||||
public void Tests(string template, params string[] expected)
|
||||
{
|
||||
var result = Templates.Folder.GetErrors(template);
|
||||
result.Count().Should().Be(expected.Length);
|
||||
result.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class IsValid
|
||||
{
|
||||
@@ -58,36 +90,64 @@ namespace Templates_Folder_Tests
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class IsRecommended
|
||||
public class GetWarnings
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_is_not_recommended() => Tests(null, false);
|
||||
public void null_is_invalid() => Tests(null, new[] { Templates.ERROR_NULL_IS_INVALID });
|
||||
|
||||
[TestMethod]
|
||||
public void empty_is_not_recommended() => Tests("", false);
|
||||
public void empty_has_warnings() => Tests("", Templates.WARNING_EMPTY, Templates.WARNING_NO_TAGS);
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_is_not_recommended() => Tests(" ", false);
|
||||
public void whitespace_has_warnings() => Tests(" ", Templates.WARNING_WHITE_SPACE, Templates.WARNING_NO_TAGS);
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"no tags", false)]
|
||||
[DataRow(@"<id>\foo\bar", true)]
|
||||
[DataRow("<ch#> <id>", false)]
|
||||
[DataRow("<ch#> chapter tag", false)]
|
||||
public void Tests(string template, bool expected) => Templates.Folder.IsRecommended(template).Should().Be(expected);
|
||||
[DataRow(@"<id>\foo\bar")]
|
||||
public void valid_tests(string template) => Tests(template, Array.Empty<string>());
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"no tags", Templates.WARNING_NO_TAGS)]
|
||||
[DataRow("<ch#> <id>", Templates.WARNING_HAS_CHAPTER_TAGS)]
|
||||
[DataRow("<ch#> chapter tag", Templates.WARNING_NO_TAGS, Templates.WARNING_HAS_CHAPTER_TAGS)]
|
||||
public void Tests(string template, params string[] expected)
|
||||
{
|
||||
var result = Templates.Folder.GetWarnings(template);
|
||||
result.Count().Should().Be(expected.Length);
|
||||
result.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class HasWarnings
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_has_warnings() => Tests(null, true);
|
||||
|
||||
[TestMethod]
|
||||
public void empty_has_warnings() => Tests("", true);
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_has_warnings() => Tests(" ", true);
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"no tags", true)]
|
||||
[DataRow(@"<id>\foo\bar", false)]
|
||||
[DataRow("<ch#> <id>", true)]
|
||||
[DataRow("<ch#> chapter tag", true)]
|
||||
public void Tests(string template, bool expected) => Templates.Folder.HasWarnings(template).Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class TagCount
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_is_not_recommended() => Assert.ThrowsException<NullReferenceException>(() => Tests(null, -1));
|
||||
public void null_throws() => Assert.ThrowsException<NullReferenceException>(() => Templates.Folder.TagCount(null));
|
||||
|
||||
[TestMethod]
|
||||
public void empty_is_not_recommended() => Tests("", 0);
|
||||
public void empty() => Tests("", 0);
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_is_not_recommended() => Tests(" ", 0);
|
||||
public void whitespace() => Tests(" ", 0);
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("no tags", 0)]
|
||||
@@ -105,6 +165,37 @@ namespace Templates_Folder_Tests
|
||||
|
||||
namespace Templates_File_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class GetErrors
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_is_invalid() => Tests(null, new[] { Templates.ERROR_NULL_IS_INVALID });
|
||||
|
||||
[TestMethod]
|
||||
public void empty_is_valid() => valid_tests("");
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_is_valid() => valid_tests(" ");
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"foo")]
|
||||
[DataRow(@"<id>")]
|
||||
public void valid_tests(string template) => Tests(template, Array.Empty<string>());
|
||||
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"C:\", Templates.ERROR_INVALID_FILE_NAME_CHAR)]
|
||||
[DataRow(@"\foo", Templates.ERROR_INVALID_FILE_NAME_CHAR)]
|
||||
[DataRow(@"/foo", Templates.ERROR_INVALID_FILE_NAME_CHAR)]
|
||||
[DataRow(@"C:\", Templates.ERROR_INVALID_FILE_NAME_CHAR)]
|
||||
public void Tests(string template, params string[] expected)
|
||||
{
|
||||
var result = Templates.File.GetErrors(template);
|
||||
result.Count().Should().Be(expected.Length);
|
||||
result.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class IsValid
|
||||
{
|
||||
@@ -126,9 +217,13 @@ namespace Templates_File_Tests
|
||||
public void Tests(string template, bool expected) => Templates.File.IsValid(template).Should().Be(expected);
|
||||
}
|
||||
|
||||
// same as Templates.Folder.IsRecommended
|
||||
// same as Templates.Folder.GetWarnings
|
||||
//[TestClass]
|
||||
//public class IsRecommended { }
|
||||
//public class GetWarnings { }
|
||||
|
||||
// same as Templates.Folder.HasWarnings
|
||||
//[TestClass]
|
||||
//public class HasWarnings { }
|
||||
|
||||
// same as Templates.Folder.TagCount
|
||||
//[TestClass]
|
||||
@@ -137,29 +232,62 @@ namespace Templates_File_Tests
|
||||
|
||||
namespace Templates_ChapterFile_Tests
|
||||
{
|
||||
// same as Templates.File.GetErrors
|
||||
//[TestClass]
|
||||
//public class GetErrors { }
|
||||
|
||||
// same as Templates.File.IsValid
|
||||
//[TestClass]
|
||||
//public class IsValid { }
|
||||
|
||||
[TestClass]
|
||||
public class IsRecommended
|
||||
public class GetWarnings
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_is_not_recommended() => Tests(null, false);
|
||||
public void null_is_invalid() => Tests(null, new[] { Templates.ERROR_NULL_IS_INVALID });
|
||||
|
||||
[TestMethod]
|
||||
public void empty_is_not_recommended() => Tests("", false);
|
||||
public void empty_has_warnings() => Tests("", Templates.WARNING_EMPTY, Templates.WARNING_NO_TAGS, Templates.WARNING_NO_CHAPTER_NUMBER_TAG);
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_is_not_recommended() => Tests(" ", false);
|
||||
public void whitespace_has_warnings() => Tests(" ", Templates.WARNING_WHITE_SPACE, Templates.WARNING_NO_TAGS, Templates.WARNING_NO_CHAPTER_NUMBER_TAG);
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"no tags", false)]
|
||||
[DataRow(@"<id>\foo\bar", false)]
|
||||
[DataRow("<ch#> <id>", true)]
|
||||
[DataRow("<ch#> -- chapter tag", true)]
|
||||
[DataRow("<chapter count> -- chapter tag but not ch# or ch_#", false)]
|
||||
public void Tests(string template, bool expected) => Templates.ChapterFile.IsRecommended(template).Should().Be(expected);
|
||||
[DataRow("<ch#>")]
|
||||
[DataRow("<ch#> <id>")]
|
||||
public void valid_tests(string template) => Tests(template, Array.Empty<string>());
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"no tags", Templates.WARNING_NO_TAGS, Templates.WARNING_NO_CHAPTER_NUMBER_TAG)]
|
||||
[DataRow(@"<id>\foo\bar", Templates.ERROR_INVALID_FILE_NAME_CHAR, Templates.WARNING_NO_CHAPTER_NUMBER_TAG)]
|
||||
[DataRow("<chapter count> -- chapter tag but not ch# or ch_#", Templates.WARNING_NO_TAGS, Templates.WARNING_NO_CHAPTER_NUMBER_TAG)]
|
||||
public void Tests(string template, params string[] expected)
|
||||
{
|
||||
var result = Templates.ChapterFile.GetWarnings(template);
|
||||
result.Count().Should().Be(expected.Length);
|
||||
result.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class HasWarnings
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_has_warnings() => Tests(null, true);
|
||||
|
||||
[TestMethod]
|
||||
public void empty_has_warnings() => Tests("", true);
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_has_warnings() => Tests(" ", true);
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"no tags", true)]
|
||||
[DataRow(@"<id>\foo\bar", true)]
|
||||
[DataRow("<ch#> <id>", false)]
|
||||
[DataRow("<ch#> -- chapter tag", false)]
|
||||
[DataRow("<chapter count> -- chapter tag but not ch# or ch_#", true)]
|
||||
public void Tests(string template, bool expected) => Templates.ChapterFile.HasWarnings(template).Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
|
||||
Reference in New Issue
Block a user