Minor changes
Empty file ISO Add date back for just .kanbn Removed HardcodedFileSearchAndSort Sync with 01-23 JsonToTsv System.Text.Json White-List Ready to move to Move Helper Remove Whitelist Force Start At Check for .git directory before ls Optional Allow root for unc path nuget bump PreVerify EnforceCodeStyleInBuild dotnet_analyzer_diagnostic HelperGit searchDelegate Host File AlertIfNewDeviceIsConnected AOT SetFrontMatterAndH1 Match Error Unknown with better logging Undo 04-05 WriteAppendToHostConfFile MonA IsKanbanIndex Dotnet Format Pre-commit NPM CreateWindowsShortcut Working directory Split description Copy tests Ready to test Delete after a couple of days GitConfigCleanUp knb Files
This commit is contained in:
42
Helpers/HelperDirectory.cs
Normal file
42
Helpers/HelperDirectory.cs
Normal file
@ -0,0 +1,42 @@
|
||||
namespace File_Folder_Helper.Helpers;
|
||||
|
||||
internal static class HelperDirectory
|
||||
{
|
||||
|
||||
internal static List<string> GetDirectoryNames(string directory)
|
||||
{
|
||||
List<string> results = [];
|
||||
string? fileName;
|
||||
string? checkDirectory = directory;
|
||||
string? pathRoot = Path.GetPathRoot(directory);
|
||||
string extension = Path.GetExtension(directory);
|
||||
if (string.IsNullOrEmpty(pathRoot))
|
||||
throw new NullReferenceException(nameof(pathRoot));
|
||||
if (Directory.Exists(directory))
|
||||
{
|
||||
fileName = Path.GetFileName(directory);
|
||||
if (!string.IsNullOrEmpty(fileName))
|
||||
results.Add(fileName);
|
||||
}
|
||||
else if ((string.IsNullOrEmpty(extension) || extension.Length > 3) && !File.Exists(directory))
|
||||
{
|
||||
fileName = Path.GetFileName(directory);
|
||||
if (!string.IsNullOrEmpty(fileName))
|
||||
results.Add(fileName);
|
||||
}
|
||||
for (int i = 0; i < int.MaxValue; i++)
|
||||
{
|
||||
checkDirectory = Path.GetDirectoryName(checkDirectory);
|
||||
if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot)
|
||||
break;
|
||||
fileName = Path.GetFileName(checkDirectory);
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
continue;
|
||||
results.Add(fileName);
|
||||
}
|
||||
results.Add(pathRoot);
|
||||
results.Reverse();
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
66
Helpers/HelperGit.cs
Normal file
66
Helpers/HelperGit.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace File_Folder_Helper.Helpers;
|
||||
|
||||
internal static class HelperGit
|
||||
{
|
||||
|
||||
private record ProcessResult(string Errors,
|
||||
int ExitCode,
|
||||
string Output);
|
||||
|
||||
private static async Task<ProcessResult> RunProcessAsync(string application, string arguments, string workingDirectory, CancellationToken cancellationToken)
|
||||
{
|
||||
using Process process = new();
|
||||
StringBuilder outputBuilder = new();
|
||||
StringBuilder errorsBuilder = new();
|
||||
process.StartInfo = new ProcessStartInfo
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true,
|
||||
FileName = application,
|
||||
Arguments = arguments,
|
||||
WorkingDirectory = workingDirectory,
|
||||
};
|
||||
process.OutputDataReceived += (_, args) => outputBuilder.AppendLine(args.Data);
|
||||
process.ErrorDataReceived += (_, args) => errorsBuilder.AppendLine(args.Data);
|
||||
_ = process.Start();
|
||||
process.BeginErrorReadLine();
|
||||
process.BeginOutputReadLine();
|
||||
await process.WaitForExitAsync(cancellationToken);
|
||||
return new(errorsBuilder.ToString().Trim(), process.ExitCode, outputBuilder.ToString().Trim());
|
||||
}
|
||||
|
||||
private static async Task<string> RunAsync(string arguments, string workingDirectory, CancellationToken cancellationToken)
|
||||
{
|
||||
ProcessResult result = await RunProcessAsync("git", arguments, workingDirectory, cancellationToken);
|
||||
if (result.ExitCode != 0)
|
||||
throw new Exception($"{result.ExitCode} {result.Errors}");
|
||||
return result.Output;
|
||||
}
|
||||
|
||||
internal static ReadOnlyCollection<string> GetOthersModifiedAndDeletedExcludingStandardFiles(string repositoryDirectory, bool usePathCombine, CancellationToken cancellationToken)
|
||||
{
|
||||
List<string> results = [];
|
||||
string checkDirectory = Path.Combine(repositoryDirectory, ".git");
|
||||
if (Directory.Exists(checkDirectory))
|
||||
{
|
||||
Task<string> task = RunAsync($"ls-files --others --modified --deleted --exclude-standard", repositoryDirectory, cancellationToken);
|
||||
task.Wait(cancellationToken);
|
||||
string[] files = task.Result.Split("\r\n");
|
||||
foreach (string file in files)
|
||||
{
|
||||
if (!usePathCombine)
|
||||
results.Add(file);
|
||||
else
|
||||
results.Add(Path.GetFullPath(Path.Combine(repositoryDirectory, file)));
|
||||
}
|
||||
}
|
||||
return new(results);
|
||||
}
|
||||
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Globalization;
|
||||
|
||||
namespace File_Folder_Helper.Helpers;
|
||||
|
||||
internal static class HelperHardcodedFileSearchAndSort
|
||||
{
|
||||
|
||||
internal static void HardcodedFileSearchAndSort(ILogger log, string sourceDirectory, SearchOption searchOption = SearchOption.TopDirectoryOnly)
|
||||
{
|
||||
bool check;
|
||||
string lines;
|
||||
string checkFile;
|
||||
string? directory;
|
||||
FileInfo fileInfo;
|
||||
string weekOfYear;
|
||||
string checkDirectory;
|
||||
CultureInfo cultureInfo = new("en-US");
|
||||
Calendar calendar = cultureInfo.Calendar;
|
||||
string[] hardcodedValues =
|
||||
[
|
||||
"BIORAD2",
|
||||
"BIORAD3",
|
||||
"BIORAD4",
|
||||
"BIORAD5",
|
||||
"CDE2",
|
||||
"CDE3",
|
||||
"CDE4",
|
||||
"CDE5",
|
||||
"CDE6",
|
||||
"HGCV1",
|
||||
"HGCV2",
|
||||
"HGCV3",
|
||||
"TENCOR1",
|
||||
"TENCOR2",
|
||||
"TENCOR3",
|
||||
"SP101",
|
||||
"SPV01",
|
||||
"SRP",
|
||||
"WC6Inch",
|
||||
"WC8Inch",
|
||||
"Bio-Rad"
|
||||
];
|
||||
string[] files = Directory.GetFiles(sourceDirectory, "*", searchOption);
|
||||
foreach (string file in files)
|
||||
{
|
||||
directory = Path.GetDirectoryName(file);
|
||||
if (string.IsNullOrEmpty(directory))
|
||||
continue;
|
||||
check = false;
|
||||
fileInfo = new(file);
|
||||
weekOfYear = calendar.GetWeekOfYear(fileInfo.LastWriteTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
|
||||
for (int i = 1; i < 3; i++)
|
||||
{
|
||||
if (check)
|
||||
break;
|
||||
lines = i switch
|
||||
{
|
||||
1 => fileInfo.Name,
|
||||
2 => File.ReadAllText(file),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
foreach (string hardcodedValue in hardcodedValues)
|
||||
{
|
||||
if (!lines.Contains(hardcodedValue))
|
||||
continue;
|
||||
checkDirectory = Path.Combine(directory, $"{fileInfo.LastWriteTime:yyyy}_Week_{weekOfYear}", fileInfo.LastWriteTime.ToString("yyyy-MM-dd"), hardcodedValue);
|
||||
if (!Directory.Exists(checkDirectory))
|
||||
_ = Directory.CreateDirectory(checkDirectory);
|
||||
checkFile = Path.Combine(checkDirectory, Path.GetFileName(file));
|
||||
if (File.Exists(checkFile) || !File.Exists(file))
|
||||
continue;
|
||||
try
|
||||
{ File.Move(file, checkFile); }
|
||||
catch (Exception) { }
|
||||
check = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
log.LogInformation("{sourceDirectory}", sourceDirectory);
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using File_Folder_Helper.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
@ -14,6 +15,11 @@ internal static partial class HelperKanbanMetadata
|
||||
[GeneratedRegex("[\\s!?.,@:;|\\\\/\"'`£$%\\^&*{}[\\]()<>~#+\\-=_¬]+")]
|
||||
private static partial Regex InvalidCharacter();
|
||||
|
||||
private record Record(FileInfo FileInfo,
|
||||
string Group,
|
||||
int GroupCount,
|
||||
int ItemLineNumber);
|
||||
|
||||
private static string GetParamCase(string value)
|
||||
{
|
||||
string result;
|
||||
@ -64,15 +70,15 @@ internal static partial class HelperKanbanMetadata
|
||||
throw new Exception("я надеюсь, что это сработает");
|
||||
}
|
||||
|
||||
private static List<(int, int, string, FileInfo)> GetCollectionFromIndex(string sourceDirectory, string[] lines)
|
||||
private static List<Record> GetCollectionFromIndex(string sourceDirectory, ReadOnlyCollection<string> lines)
|
||||
{
|
||||
List<(int, int, string, FileInfo)> results = [];
|
||||
List<Record> results = [];
|
||||
string line;
|
||||
FileInfo fileInfo;
|
||||
string[] segments;
|
||||
int groupCount = 0;
|
||||
string? group = null;
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
for (int i = 0; i < lines.Count; i++)
|
||||
{
|
||||
line = lines[i];
|
||||
if (line.Length < 4)
|
||||
@ -91,12 +97,30 @@ internal static partial class HelperKanbanMetadata
|
||||
fileInfo = new(Path.Combine(sourceDirectory, segments[1][..^1]));
|
||||
if (!fileInfo.Exists)
|
||||
continue;
|
||||
results.Add((groupCount, i, group, fileInfo));
|
||||
results.Add(new(fileInfo, group, groupCount, i));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
internal static void SetMetadata(ILogger log, AppSettings appSettings, string sourceDirectory, bool addTicks)
|
||||
private static void WriteKanbanBoardFile(string fullPath, List<Record> records, string h1)
|
||||
{
|
||||
string? last = null;
|
||||
List<string> lines = [h1, string.Empty];
|
||||
foreach (Record record in records)
|
||||
{
|
||||
if (last is null || record.Group != last)
|
||||
{
|
||||
lines.Add(string.Empty);
|
||||
lines.Add($"## {record.Group}");
|
||||
lines.Add(string.Empty);
|
||||
}
|
||||
lines.Add($"- [ ] {Path.GetFileNameWithoutExtension(record.FileInfo.Name)}");
|
||||
last = record.Group;
|
||||
}
|
||||
File.WriteAllLines(Path.Combine(fullPath, "index.knb.md"), lines);
|
||||
}
|
||||
|
||||
internal static void SetMetadata(ILogger log, AppSettings appSettings, string sourceDirectory)
|
||||
{
|
||||
bool? match;
|
||||
string? paramCase;
|
||||
@ -112,20 +136,22 @@ internal static partial class HelperKanbanMetadata
|
||||
string indexFile = Path.Combine(fullPath, "index.md");
|
||||
if (File.Exists(indexFile))
|
||||
{
|
||||
string[] indexFileLines = File.ReadAllLines(indexFile);
|
||||
List<(int, int, string, FileInfo)> collectionFromIndex = GetCollectionFromIndex(sourceDirectory, indexFileLines);
|
||||
foreach ((int groupCount, int itemLineNumber, string group, FileInfo fileInfo) in collectionFromIndex)
|
||||
FileInfo fileInfo = new(indexFile);
|
||||
(lines, lineNumber) = HelperMarkdown.GetStatusAndFrontMatterYamlEndLineNumbers(fileInfo);
|
||||
ReadOnlyCollection<string> indexFileLines = new(lines);
|
||||
List<Record> records = GetCollectionFromIndex(sourceDirectory, indexFileLines);
|
||||
if (lineNumber.H1 is not null)
|
||||
WriteKanbanBoardFile(fullPath, records, indexFileLines[lineNumber.H1.Value]);
|
||||
foreach (Record record in records)
|
||||
{
|
||||
if (itemLineNumber == 0)
|
||||
if (record.ItemLineNumber == 0)
|
||||
throw new NotSupportedException();
|
||||
(lines, lineNumber) = HelperMarkdown.GetStatusAndFrontMatterYamlEndLineNumbers(fileInfo);
|
||||
(lines, lineNumber) = HelperMarkdown.GetStatusAndFrontMatterYamlEndLineNumbers(record.FileInfo);
|
||||
if (lines.Count == 0)
|
||||
continue;
|
||||
statusLine = $"status: \"{groupCount}-{group}\"";
|
||||
statusLine = $"status: \"{record.GroupCount}-{record.Group}\"";
|
||||
paramCase = lineNumber.H1 is null ? null : GetParamCase(lines[lineNumber.H1.Value]);
|
||||
if (addTicks)
|
||||
indexFileLines[itemLineNumber] = $"{fileInfo.LastWriteTime.Ticks}~~~{indexFileLines[itemLineNumber]}";
|
||||
match = lineNumber.H1 is null || paramCase is null ? null : Path.GetFileNameWithoutExtension(fileInfo.Name) == paramCase;
|
||||
match = lineNumber.H1 is null || paramCase is null ? null : Path.GetFileNameWithoutExtension(record.FileInfo.Name) == paramCase;
|
||||
if (lineNumber.FrontMatterYamlEnd is null)
|
||||
throw new NotSupportedException($"{nameof(SetMetadata)} must be executed first!");
|
||||
if (lineNumber.H1 is not null && paramCase is not null && match is not null && !match.Value)
|
||||
@ -138,10 +164,8 @@ internal static partial class HelperKanbanMetadata
|
||||
continue;
|
||||
lines[lineNumber.Status.Value] = statusLine;
|
||||
}
|
||||
File.WriteAllLines(fileInfo.FullName, lines);
|
||||
File.WriteAllLines(record.FileInfo.FullName, lines);
|
||||
}
|
||||
if (addTicks)
|
||||
File.WriteAllLines(indexFile, indexFileLines);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
using File_Folder_Helper.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
@ -11,6 +12,44 @@ namespace File_Folder_Helper.Helpers;
|
||||
internal static partial class HelperMarkdown
|
||||
{
|
||||
|
||||
private record Input(string? Destination,
|
||||
string Source,
|
||||
string? StartAt);
|
||||
|
||||
private record Record(string Directory,
|
||||
string File,
|
||||
string[] Lines);
|
||||
|
||||
private record MarkdownFile(DateTime CreationDateTime,
|
||||
string Directory,
|
||||
string Extension,
|
||||
string File,
|
||||
string FileName,
|
||||
string FileNameWithoutExtension,
|
||||
string H1,
|
||||
bool IsKanbanIndex,
|
||||
DateTime LastWriteDateTime,
|
||||
LineNumber LineNumber,
|
||||
string Type);
|
||||
|
||||
private record MarkdownFileAndLines(MarkdownFile MarkdownFile,
|
||||
string[] Lines);
|
||||
|
||||
private record MarkdownExtra(ReadOnlyCollection<string>? Assignees,
|
||||
string? Effort,
|
||||
ReadOnlyCollection<H2HexColor>? H2HexColorCollection,
|
||||
ReadOnlyCollection<H2NoCheckboxes>? H2NoCheckboxesCollection,
|
||||
ReadOnlyCollection<H2WithCheckboxes>? H2WithCheckboxesCollection,
|
||||
string? RequestedDateTime);
|
||||
|
||||
private record MarkdownFileH1AndRelativePath(MarkdownFile? MarkdownFile, string[]? Lines, string? H1, string? RelativePath);
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[JsonSerializable(typeof(Dictionary<string, JsonElement>))]
|
||||
internal partial class DictionaryStringAndJsonElementSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
||||
private static void SetRecursiveLines(AppSettings appSettings, ILogger<Worker> logger, ReadOnlyDictionary<string, List<MarkdownFileAndLines>> keyValuePairs, string linkTitle, MarkdownFile markdownFile, string[] lines, List<char> indentations, List<string> recursiveLines)
|
||||
{
|
||||
if (recursiveLines is null)
|
||||
@ -553,6 +592,7 @@ internal static partial class HelperMarkdown
|
||||
string key;
|
||||
string type;
|
||||
FileInfo fileInfo;
|
||||
bool isKanbanIndex;
|
||||
List<string> lines;
|
||||
LineNumber lineNumber;
|
||||
MarkdownFile markdownFile;
|
||||
@ -574,6 +614,7 @@ internal static partial class HelperMarkdown
|
||||
File.WriteAllLines(file, ["---", $"type: \"{type}\"", "---", string.Empty, $"# {h1}"]);
|
||||
lines = File.ReadAllLines(file).ToList();
|
||||
}
|
||||
isKanbanIndex = fileNameWithoutExtension == "index" && type == "Kanban";
|
||||
markdownFile = new(fileInfo.CreationTime,
|
||||
fileInfo.DirectoryName,
|
||||
fileInfo.Extension,
|
||||
@ -581,6 +622,7 @@ internal static partial class HelperMarkdown
|
||||
fileInfo.Name,
|
||||
fileNameWithoutExtension,
|
||||
h1,
|
||||
isKanbanIndex,
|
||||
fileInfo.LastWriteTime,
|
||||
lineNumber,
|
||||
type);
|
||||
@ -613,7 +655,7 @@ internal static partial class HelperMarkdown
|
||||
continue;
|
||||
lines = relativeTo.Value.Lines;
|
||||
markdownFile = relativeTo.Value.MarkdownFile;
|
||||
if (markdownFile.FileNameWithoutExtension != "index" || markdownFile.Type != "Kanban")
|
||||
if (markdownFile.IsKanbanIndex)
|
||||
continue;
|
||||
if (!File.Exists(markdownFile.File))
|
||||
continue;
|
||||
@ -642,7 +684,7 @@ internal static partial class HelperMarkdown
|
||||
continue;
|
||||
key = Path.GetRelativePath(input.Source, Path.Combine(markdownFile.Directory, segmentsA[1][..^1]));
|
||||
if (!allKeys.Remove(key))
|
||||
throw new NotSupportedException();
|
||||
continue;
|
||||
if (!relativeToCollection.TryGetValue(key, out markdownFileAndLines))
|
||||
continue;
|
||||
markdownExtra = GetMarkdownExtra(markdownFileAndLines);
|
||||
@ -765,6 +807,7 @@ internal static partial class HelperMarkdown
|
||||
Input result;
|
||||
string? startAt = null;
|
||||
string? destination = null;
|
||||
string source = Path.GetFullPath(args[0]);
|
||||
for (int i = 1; i < args.Count; i++)
|
||||
{
|
||||
if (args[i].Length == 2 && i + 1 < args.Count)
|
||||
@ -778,6 +821,8 @@ internal static partial class HelperMarkdown
|
||||
}
|
||||
if (startAt is not null && !Directory.Exists(startAt))
|
||||
throw new Exception($"Start at directory <{startAt}> doesn't exist!");
|
||||
if (startAt is not null && startAt.Length < source.Length)
|
||||
throw new Exception($"Start at directory <{startAt}> must be a subdirectory!");
|
||||
if (destination is not null)
|
||||
{
|
||||
string? root = Path.GetPathRoot(destination);
|
||||
@ -786,7 +831,7 @@ internal static partial class HelperMarkdown
|
||||
if (!Directory.Exists(destination))
|
||||
_ = Directory.CreateDirectory(destination);
|
||||
}
|
||||
result = new(Path.GetFullPath(args[0]), startAt, destination);
|
||||
result = new(destination, source, startAt);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1019,7 +1064,7 @@ internal static partial class HelperMarkdown
|
||||
continue;
|
||||
lines = relativeTo.Value.Lines;
|
||||
markdownFile = relativeTo.Value.MarkdownFile;
|
||||
if (markdownFile.FileNameWithoutExtension == "index" && markdownFile.Directory.EndsWith(".kanbn"))
|
||||
if (markdownFile.IsKanbanIndex)
|
||||
continue;
|
||||
if (!File.Exists(markdownFile.File))
|
||||
continue;
|
||||
@ -1064,7 +1109,7 @@ internal static partial class HelperMarkdown
|
||||
continue;
|
||||
File.Move(file, checkName);
|
||||
}
|
||||
else if (fileName != fileName.ToLower())
|
||||
else if (!fileName.Equals(fileName, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
if (file != checkName)
|
||||
{
|
||||
@ -1138,134 +1183,21 @@ internal static partial class HelperMarkdown
|
||||
if (string.IsNullOrEmpty(input.StartAt) || string.IsNullOrEmpty(input.Destination))
|
||||
throw new NotSupportedException();
|
||||
ReadOnlyDictionary<string, List<Card>> columnsToCards;
|
||||
string jsonFile = Path.Combine(input.Destination, $"{nameof(columnsToCards)}.json");
|
||||
if (File.Exists(jsonFile))
|
||||
File.Delete(jsonFile);
|
||||
columnsToCards = GetColumnsToCards(input, relativeToCollection);
|
||||
if (columnsToCards.Count == 0)
|
||||
File.WriteAllText(jsonFile, "{}");
|
||||
else
|
||||
int kanbanIndexFiles = (from l in relativeToCollection where l.Value.MarkdownFile.IsKanbanIndex select 1).Sum();
|
||||
if (kanbanIndexFiles == 1)
|
||||
{
|
||||
string json = JsonSerializer.Serialize(columnsToCards, ColumnsAndCardsSourceGenerationContext.Default.ReadOnlyDictionaryStringListCard);
|
||||
File.WriteAllText(jsonFile, json);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Record> GetWithLinksForHugo(AppSettings appSettings, Input input)
|
||||
{
|
||||
List<Record> results = [];
|
||||
string file;
|
||||
string line;
|
||||
string[] lines;
|
||||
string fileName;
|
||||
string? directory;
|
||||
string[] segmentsA;
|
||||
string[] segmentsB;
|
||||
string relativeFile;
|
||||
string segmentsALast;
|
||||
string segmentsBFirst;
|
||||
MarkdownFile markdownFile;
|
||||
int sourceDirectoryLength = input.Source.Length;
|
||||
ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection = GetRelativeToCollection(appSettings, input);
|
||||
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
|
||||
{
|
||||
if (relativeTo.Value.Lines.Length == 0)
|
||||
continue;
|
||||
lines = relativeTo.Value.Lines;
|
||||
markdownFile = relativeTo.Value.MarkdownFile;
|
||||
if (input.Destination is null)
|
||||
continue;
|
||||
if (markdownFile.File.Length < sourceDirectoryLength)
|
||||
continue;
|
||||
if (!File.Exists(markdownFile.File))
|
||||
continue;
|
||||
fileName = $"{input.Destination}{markdownFile.File[sourceDirectoryLength..]}";
|
||||
directory = Path.GetDirectoryName(fileName);
|
||||
if (string.IsNullOrEmpty(directory))
|
||||
continue;
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
string jsonFile = Path.Combine(input.Destination, $"{nameof(columnsToCards)}.json");
|
||||
if (File.Exists(jsonFile))
|
||||
File.Delete(jsonFile);
|
||||
columnsToCards = GetColumnsToCards(input, relativeToCollection);
|
||||
if (columnsToCards.Count == 0)
|
||||
File.WriteAllText(jsonFile, "{}");
|
||||
else
|
||||
{
|
||||
segmentsA = lines[i].Split("](");
|
||||
if (segmentsA.Length != 2)
|
||||
continue;
|
||||
segmentsALast = segmentsA[^1];
|
||||
if (appSettings.ExcludeSchemes.Any(l => segmentsALast.StartsWith(l)))
|
||||
continue;
|
||||
segmentsB = segmentsALast.Split(")");
|
||||
if (segmentsB.Length != 2)
|
||||
continue;
|
||||
segmentsBFirst = segmentsB[0];
|
||||
if (!segmentsBFirst.EndsWith(".md"))
|
||||
file = Path.GetFullPath(Path.Combine(markdownFile.Directory, segmentsBFirst));
|
||||
else
|
||||
file = Path.GetFullPath(Path.Combine(markdownFile.Directory, segmentsBFirst[..^3]));
|
||||
relativeFile = Path.GetRelativePath(input.Source, file).Replace('\\', '/');
|
||||
line = $"{segmentsA[0]}]({relativeFile}){segmentsB[^1]}";
|
||||
if (lines[i] == line)
|
||||
throw new NotSupportedException($"Line {i} shouldn't match with {line}");
|
||||
lines[i] = line;
|
||||
string json = JsonSerializer.Serialize(columnsToCards, ColumnsAndCardsSourceGenerationContext.Default.ReadOnlyDictionaryStringListCard);
|
||||
File.WriteAllText(jsonFile, json);
|
||||
}
|
||||
results.Add(new(directory, fileName, lines));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static List<string> GetDistinct(List<Record> collection)
|
||||
{
|
||||
List<string> results = [];
|
||||
foreach (Record record in collection)
|
||||
{
|
||||
if (results.Contains(record.Directory))
|
||||
continue;
|
||||
results.Add(record.Directory);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static void CreateMissingDirectories(List<string> directories)
|
||||
{
|
||||
foreach (string directory in directories)
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
_ = Directory.CreateDirectory(directory);
|
||||
}
|
||||
}
|
||||
|
||||
private record Input(string Source,
|
||||
string? StartAt,
|
||||
string? Destination);
|
||||
|
||||
private record Record(string Directory,
|
||||
string File,
|
||||
string[] Lines);
|
||||
|
||||
private record MarkdownFile(DateTime CreationDateTime,
|
||||
string Directory,
|
||||
string Extension,
|
||||
string File,
|
||||
string FileName,
|
||||
string FileNameWithoutExtension,
|
||||
string H1,
|
||||
DateTime LastWriteDateTime,
|
||||
LineNumber LineNumber,
|
||||
string Type);
|
||||
|
||||
private record MarkdownFileAndLines(MarkdownFile MarkdownFile,
|
||||
string[] Lines);
|
||||
|
||||
private record MarkdownExtra(ReadOnlyCollection<string>? Assignees,
|
||||
string? Effort,
|
||||
ReadOnlyCollection<H2HexColor>? H2HexColorCollection,
|
||||
ReadOnlyCollection<H2NoCheckboxes>? H2NoCheckboxesCollection,
|
||||
ReadOnlyCollection<H2WithCheckboxes>? H2WithCheckboxesCollection,
|
||||
string? RequestedDateTime);
|
||||
|
||||
private record MarkdownFileH1AndRelativePath(MarkdownFile? MarkdownFile, string[]? Lines, string? H1, string? RelativePath);
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[JsonSerializable(typeof(Dictionary<string, JsonElement>))]
|
||||
internal partial class DictionaryStringAndJsonElementSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
||||
private static (string type, string h1) GetTypeAndH1(AppSettings appSettings, string h1, List<string> lines, LineNumber lineNumber)
|
||||
@ -1275,14 +1207,22 @@ internal static partial class HelperMarkdown
|
||||
return (type, h1FromFile);
|
||||
}
|
||||
|
||||
private static int SetFrontMatterAndH1(AppSettings appSettings, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection)
|
||||
private static int SetFrontMatterAndH1(AppSettings appSettings, Input input, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles)
|
||||
{
|
||||
int result = 0;
|
||||
List<string> results = [];
|
||||
bool gitCheck;
|
||||
string h1Line;
|
||||
string[] lines;
|
||||
string typeLine;
|
||||
TimeSpan timeSpan;
|
||||
string createdLine;
|
||||
string updatedLine;
|
||||
string lineDateTime;
|
||||
DateTime checkDateTime;
|
||||
DateTime creationDateTime;
|
||||
MarkdownFile markdownFile;
|
||||
string lineCreationFormat = "yyyy-MM-ddTHH:mm:ss.fffZ";
|
||||
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
|
||||
{
|
||||
if (relativeTo.Value.Lines.Length == 0)
|
||||
@ -1293,6 +1233,10 @@ internal static partial class HelperMarkdown
|
||||
results.AddRange(lines);
|
||||
typeLine = $"type: \"{appSettings.DefaultNoteType}\"";
|
||||
h1Line = $"# {markdownFile.FileNameWithoutExtension}";
|
||||
creationDateTime = markdownFile.CreationDateTime > markdownFile.LastWriteDateTime ? markdownFile.LastWriteDateTime : markdownFile.CreationDateTime;
|
||||
gitCheck = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(markdownFile.File);
|
||||
createdLine = $"created: \"{creationDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}\"";
|
||||
updatedLine = $"updated: \"{markdownFile.LastWriteDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}\"";
|
||||
if (markdownFile.LineNumber.FrontMatterYamlEnd is null)
|
||||
{
|
||||
if (markdownFile.LineNumber.H1 is null)
|
||||
@ -1302,6 +1246,11 @@ internal static partial class HelperMarkdown
|
||||
results.Insert(0, string.Empty);
|
||||
}
|
||||
results.Insert(0, "---");
|
||||
if (gitCheck)
|
||||
{
|
||||
results.Insert(0, updatedLine);
|
||||
results.Insert(0, createdLine);
|
||||
}
|
||||
results.Insert(0, typeLine);
|
||||
results.Insert(0, "---");
|
||||
}
|
||||
@ -1315,9 +1264,42 @@ internal static partial class HelperMarkdown
|
||||
}
|
||||
if (markdownFile.LineNumber.Type is null)
|
||||
results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, typeLine);
|
||||
if (markdownFile.LineNumber.H1 is not null && markdownFile.LineNumber.Type is not null)
|
||||
continue;
|
||||
if (gitCheck)
|
||||
{
|
||||
if (markdownFile.LineNumber.Updated is null)
|
||||
results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, updatedLine);
|
||||
else
|
||||
{
|
||||
lineDateTime = results[markdownFile.LineNumber.Updated.Value].Split(": ")[1].Trim('"');
|
||||
if (!DateTime.TryParseExact(lineDateTime, lineCreationFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime))
|
||||
results[markdownFile.LineNumber.Updated.Value] = updatedLine;
|
||||
else
|
||||
{
|
||||
timeSpan = new(checkDateTime.Ticks - markdownFile.LastWriteDateTime.Ticks);
|
||||
if (timeSpan.TotalDays is > 1 or < -1)
|
||||
results[markdownFile.LineNumber.Updated.Value] = updatedLine;
|
||||
}
|
||||
}
|
||||
if (markdownFile.LineNumber.Created is null)
|
||||
results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, createdLine);
|
||||
else
|
||||
{
|
||||
lineDateTime = results[markdownFile.LineNumber.Created.Value].Split(": ")[1].Trim('"');
|
||||
if (!DateTime.TryParseExact(lineDateTime, lineCreationFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime))
|
||||
results[markdownFile.LineNumber.Created.Value] = createdLine;
|
||||
else
|
||||
{
|
||||
timeSpan = new(checkDateTime.Ticks - creationDateTime.Ticks);
|
||||
if (timeSpan.TotalDays > 1)
|
||||
results[markdownFile.LineNumber.Created.Value] = createdLine;
|
||||
if (timeSpan.TotalDays < -1)
|
||||
File.SetCreationTime(markdownFile.File, checkDateTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (results.Count == lines.Length && string.Join('\r', lines) == string.Join('\r', results))
|
||||
continue;
|
||||
File.WriteAllLines(markdownFile.File, results);
|
||||
File.SetLastWriteTime(markdownFile.File, markdownFile.LastWriteDateTime);
|
||||
result += 1;
|
||||
@ -1331,13 +1313,14 @@ internal static partial class HelperMarkdown
|
||||
return new(result?.MarkdownFile, result?.Lines, result?.MarkdownFile.H1, result is null ? null : Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(result.MarkdownFile.File)));
|
||||
}
|
||||
|
||||
internal static void MarkdownWikiLinkVerification(AppSettings appSettings, ILogger<Worker> logger, List<string> args)
|
||||
internal static void MarkdownWikiLinkVerification(AppSettings appSettings, ILogger<Worker> logger, List<string> args, CancellationToken cancellationToken)
|
||||
{
|
||||
int updated;
|
||||
bool usePathCombine = false;
|
||||
Input input = GetInput(args);
|
||||
ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection;
|
||||
relativeToCollection = GetRelativeToCollection(appSettings, input);
|
||||
updated = SetFrontMatterAndH1(appSettings, relativeToCollection);
|
||||
ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection = GetRelativeToCollection(appSettings, input);
|
||||
ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles = HelperGit.GetOthersModifiedAndDeletedExcludingStandardFiles(input.Source, usePathCombine, cancellationToken);
|
||||
updated = SetFrontMatterAndH1(appSettings, input, relativeToCollection, gitOthersModifiedAndDeletedExcludingStandardFiles);
|
||||
if (updated != 0)
|
||||
{
|
||||
relativeToCollection = GetRelativeToCollection(appSettings, input);
|
||||
@ -1383,18 +1366,4 @@ internal static partial class HelperMarkdown
|
||||
SaveColumnToCards(input, relativeToCollection);
|
||||
}
|
||||
|
||||
internal static void MarkdownConvertLinksForHugo(AppSettings appSettings, ILogger<Worker> logger, List<string> args)
|
||||
{
|
||||
Input input = GetInput(args);
|
||||
if (string.IsNullOrEmpty(input.Destination))
|
||||
throw new NotSupportedException("This method requires frontMatterYamlLines -d path!");
|
||||
List<Record> collection = GetWithLinksForHugo(appSettings, input);
|
||||
if (collection.Count == 0)
|
||||
logger.LogInformation("No files?");
|
||||
List<string> distinct = GetDistinct(collection);
|
||||
CreateMissingDirectories(distinct);
|
||||
foreach (Record record in collection)
|
||||
File.WriteAllLines(record.File, record.Lines);
|
||||
}
|
||||
|
||||
}
|
30
Helpers/HelperNPM.cs
Normal file
30
Helpers/HelperNPM.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace File_Folder_Helper.Helpers;
|
||||
|
||||
internal static class HelperNPM
|
||||
{
|
||||
|
||||
internal static string RunCommand(string commandFileName, string commandToRun, string workingDirectory)
|
||||
{
|
||||
string result;
|
||||
if (!string.IsNullOrEmpty(commandFileName))
|
||||
File.WriteAllText(Path.Combine(workingDirectory, commandFileName), commandToRun);
|
||||
if (string.IsNullOrEmpty(workingDirectory))
|
||||
workingDirectory = Directory.GetDirectoryRoot(Directory.GetCurrentDirectory());
|
||||
ProcessStartInfo processStartInfo = new()
|
||||
{
|
||||
FileName = "cmd",
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
WorkingDirectory = workingDirectory
|
||||
};
|
||||
Process? process = Process.Start(processStartInfo) ?? throw new Exception("Process should not be null.");
|
||||
process.StandardInput.WriteLine($"{commandToRun} & exit");
|
||||
process.WaitForExit();
|
||||
result = $"{process.StandardOutput.ReadToEnd()}{Environment.NewLine}{process.StandardError.ReadToEnd()}{Environment.NewLine}{process.ExitCode}";
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -34,6 +34,7 @@ internal static class HelperSaveOrCopyContents
|
||||
log.LogInformation("{empty}", string.Empty);
|
||||
continue;
|
||||
}
|
||||
#pragma warning disable IDE0072
|
||||
string fileName = dfb switch
|
||||
{
|
||||
ConsoleKey.D => "Directories",
|
||||
@ -41,6 +42,7 @@ internal static class HelperSaveOrCopyContents
|
||||
ConsoleKey.B => "Both",
|
||||
_ => throw new NotSupportedException(),
|
||||
};
|
||||
#pragma warning restore IDE0072
|
||||
string filePathAndName = Path.Combine(parentDirectory, $"{fileName}.txt");
|
||||
if (dfb == ConsoleKey.F)
|
||||
collection = Directory.GetFiles(argsZero, "*", searchOption).ToList();
|
||||
@ -79,7 +81,7 @@ internal static class HelperSaveOrCopyContents
|
||||
foreach (string file in files)
|
||||
{
|
||||
fileName = Path.GetFileName(file);
|
||||
if (fileName == fileName.ToLower())
|
||||
if (fileName.Equals(fileName, StringComparison.CurrentCultureIgnoreCase))
|
||||
continue;
|
||||
File.Move(file, file.ToLower());
|
||||
filesRenamed++;
|
||||
|
100
Helpers/RijndaelEncryption.cs
Normal file
100
Helpers/RijndaelEncryption.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace File_Folder_Helper.Helpers;
|
||||
|
||||
public static class RijndaelEncryption
|
||||
{ // cSpell:disable
|
||||
|
||||
/// <summary>
|
||||
/// Change the input key GUID when you use this code in your own program.
|
||||
/// Keep this input key very safe and prevent someone from decoding it some way!!
|
||||
/// Generated 2021-08-10
|
||||
/// </summary>
|
||||
internal const string _InputKey = "970CCEF6-4307-4F6A-9AC8-377DADB889BD";
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt the given text and give the byte array back as a BASE64 string
|
||||
/// </summary>
|
||||
/// <param name="text">The text to encrypt</param>
|
||||
/// <param name="salt">The password salt</param>
|
||||
/// <returns>The encrypted text</returns>
|
||||
public static string Encrypt(string text, string salt)
|
||||
{
|
||||
string result;
|
||||
if (string.IsNullOrEmpty(text))
|
||||
throw new ArgumentNullException(nameof(text));
|
||||
#pragma warning disable SYSLIB0022
|
||||
RijndaelManaged aesAlg = NewRijndaelManaged(salt);
|
||||
#pragma warning restore
|
||||
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
|
||||
MemoryStream msEncrypt = new();
|
||||
using (CryptoStream csEncrypt = new(msEncrypt, encryptor, CryptoStreamMode.Write))
|
||||
using (StreamWriter swEncrypt = new(csEncrypt))
|
||||
swEncrypt.Write(text);
|
||||
result = Convert.ToBase64String(msEncrypt.ToArray());
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a string is base64 encoded
|
||||
/// </summary>
|
||||
/// <param name="base64String">The base64 encoded string</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsBase64String(string base64String)
|
||||
{
|
||||
bool result;
|
||||
base64String = base64String.Trim();
|
||||
#pragma warning restore
|
||||
result = (base64String.Length % 4 == 0) && Regex.IsMatch(base64String, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);
|
||||
#pragma warning restore
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the given text
|
||||
/// </summary>
|
||||
/// <param name="cipherText">The encrypted BASE64 text</param>
|
||||
/// <param name="salt">The password salt</param>
|
||||
/// <returns>De gedecrypte text</returns>
|
||||
public static string Decrypt(string cipherText, string salt)
|
||||
{
|
||||
if (string.IsNullOrEmpty(cipherText))
|
||||
throw new ArgumentNullException(nameof(cipherText));
|
||||
if (!IsBase64String(cipherText))
|
||||
throw new Exception("The cipherText input parameter is not base64 encoded");
|
||||
string text;
|
||||
#pragma warning disable SYSLIB0022
|
||||
RijndaelManaged aesAlg = NewRijndaelManaged(salt);
|
||||
#pragma warning restore
|
||||
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
|
||||
byte[] cipher = Convert.FromBase64String(cipherText);
|
||||
using (MemoryStream msDecrypt = new(cipher))
|
||||
{
|
||||
using CryptoStream csDecrypt = new(msDecrypt, decryptor, CryptoStreamMode.Read);
|
||||
using StreamReader srDecrypt = new(csDecrypt);
|
||||
text = srDecrypt.ReadToEnd();
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new RijndaelManaged class and initialize it
|
||||
/// </summary>
|
||||
/// <param name="salt">The password salt</param>
|
||||
/// <returns></returns>
|
||||
#pragma warning disable SYSLIB0022, SYSLIB0041, CA5379
|
||||
private static RijndaelManaged NewRijndaelManaged(string salt)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(salt);
|
||||
byte[] saltBytes = Encoding.ASCII.GetBytes(salt);
|
||||
Rfc2898DeriveBytes key = new(_InputKey, saltBytes);
|
||||
RijndaelManaged aesAlg = new();
|
||||
#pragma warning restore
|
||||
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
|
||||
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
|
||||
return aesAlg;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user