Expanded and improved File and Folder sections in Utility Block

This commit is contained in:
ruri
2020-03-24 02:29:26 +01:00
4 changed files with 193 additions and 4 deletions
@@ -183,12 +183,24 @@
<Label Content="Path:" DockPanel.Dock="Left"/>
<TextBox Text="{Binding FilePath, UpdateSourceTrigger=PropertyChanged}" />
</DockPanel>
<Label Foreground="{DynamicResource ForegroundCustom}" Content="Path can be relative or absolute" />
<DockPanel>
<Label Content="Input String:" DockPanel.Dock="Left" />
<TextBox Text="{Binding InputString, UpdateSourceTrigger=PropertyChanged}" />
</DockPanel>
<Label Foreground="{DynamicResource ForegroundCustom}" Content="Newline escape sequences will be evaluated. &quot;Lines&quot; accept LIST[*]." />
<Label Foreground="{DynamicResource ForegroundCustom}" Content="On Copy and Move, Input String needs to be the destination file path." />
</StackPanel>
</TabItem>
<TabItem x:Name="folderTab">
<StackPanel>
<DockPanel>
<Label Content="Action:" DockPanel.Dock="Left" />
<ComboBox x:Name="folderActionCombobox" SelectionChanged="folderActionCombobox_SelectionChanged" />
</DockPanel>
<DockPanel>
<Label Content="Path:" DockPanel.Dock="Left"/>
<TextBox Text="{Binding FolderPath, UpdateSourceTrigger=PropertyChanged}" />
</DockPanel>
</StackPanel>
</TabItem>
</TabControl>
@@ -49,6 +49,11 @@ namespace OpenBullet.Views.StackerBlocks
fileActionCombobox.SelectedIndex = (int)block.FileAction;
foreach (var a in Enum.GetNames(typeof(FolderAction)))
folderActionCombobox.Items.Add(a);
folderActionCombobox.SelectedIndex = (int)block.FolderAction;
foreach (var c in Enum.GetNames(typeof(Comparer)))
removeComparerCombobox.Items.Add(c);
@@ -80,6 +85,10 @@ namespace OpenBullet.Views.StackerBlocks
case UtilityGroup.File:
groupTabControl.SelectedIndex = 4;
break;
case UtilityGroup.Folder:
groupTabControl.SelectedIndex = 5;
break;
}
}
@@ -139,6 +148,11 @@ namespace OpenBullet.Views.StackerBlocks
block.FileAction = (FileAction)((ComboBox)e.OriginalSource).SelectedIndex;
}
private void folderActionCombobox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
block.FolderAction = (FolderAction)((ComboBox)e.OriginalSource).SelectedIndex;
}
private void removeComparerCombobox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
block.ListElementComparer = (Comparer)((ComboBox)e.OriginalSource).SelectedIndex;
+106 -3
View File
@@ -28,7 +28,10 @@ namespace RuriLib
Conversion,
/// <summary>The group of actions that interact with files.</summary>
File
File,
/// <summary>The group of actions that interact with folders.</summary>
Folder
}
/// <summary>
@@ -90,6 +93,9 @@ namespace RuriLib
/// </summary>
public enum FileAction
{
/// <summary>Checks if a file exists.</summary>
Exists,
/// <summary>Reads a file to a single variable.</summary>
Read,
@@ -106,7 +112,25 @@ namespace RuriLib
Append,
/// <summary>Appends a list variable to a file.</summary>
AppendLines
AppendLines,
/// <summary>Copies a file to a new file.</summary>
Copy,
/// <summary>Moves a file to a different location.</summary>
Move
}
/// <summary>
/// Actions that can be performed on folders.
/// </summary>
public enum FolderAction
{
/// <summary>Checks if a folder exists.</summary>
Exists,
/// <summary>Creates a folder.</summary>
Create
}
/// <summary>
@@ -200,13 +224,22 @@ namespace RuriLib
// Files
private string filePath = "test.txt";
/// <summary>The path to the file to read/write.</summary>
/// <summary>The path to the file to interact with.</summary>
public string FilePath { get { return filePath; } set { filePath = value; OnPropertyChanged(); } }
private FileAction fileAction = FileAction.Read;
/// <summary>The action to be performed on the file.</summary>
public FileAction FileAction { get { return fileAction; } set { fileAction = value; OnPropertyChanged(); } }
// Folders
private string folderPath = "TestFolder";
/// <summary>The path to the folder to interact with.</summary>
public string FolderPath { get { return folderPath; } set { folderPath = value; OnPropertyChanged(); } }
private FolderAction folderAction = FolderAction.Create;
/// <summary>The action to be performed on the folder.</summary>
public FolderAction FolderAction { get { return folderAction; } set { folderAction = value; OnPropertyChanged(); } }
/// <summary>
/// Creates a Utility block.
/// </summary>
@@ -297,10 +330,17 @@ namespace RuriLib
case FileAction.WriteLines:
case FileAction.Append:
case FileAction.AppendLines:
case FileAction.Copy:
case FileAction.Move:
InputString = LineParser.ParseLiteral(ref input, "Input String");
break;
}
break;
case UtilityGroup.Folder:
FolderPath = LineParser.ParseLiteral(ref input, "Folder Name");
FolderAction = (FolderAction)LineParser.ParseEnum(ref input, "Folder Action", typeof(FolderAction));
break;
}
// Try to parse the arrow, otherwise just return the block as is with default var name and var / cap choice
@@ -410,11 +450,19 @@ namespace RuriLib
case FileAction.WriteLines:
case FileAction.Append:
case FileAction.AppendLines:
case FileAction.Copy:
case FileAction.Move:
writer
.Literal(InputString);
break;
}
break;
case UtilityGroup.Folder:
writer
.Literal(FolderPath)
.Token(FolderAction);
break;
}
if (!writer.CheckDefault(VariableName, "VariableName"))
@@ -556,8 +604,14 @@ namespace RuriLib
case UtilityGroup.File:
var file = ReplaceValues(filePath, data);
ThrowIfNotInCWD(file);
switch (fileAction)
{
case FileAction.Exists:
data.Variables.Set(new CVar(variableName, File.Exists(file).ToString(), isCapture));
break;
case FileAction.Read:
data.Variables.Set(new CVar(variableName, File.ReadAllText(file), isCapture));
break;
@@ -567,10 +621,12 @@ namespace RuriLib
break;
case FileAction.Write:
CreatePath(file);
File.WriteAllText(file, replacedInput.Unescape());
break;
case FileAction.WriteLines:
CreatePath(file);
File.WriteAllLines(file, ReplaceValuesRecursive(inputString, data).Select(i => i.Unescape()));
break;
@@ -581,15 +637,62 @@ namespace RuriLib
case FileAction.AppendLines:
File.AppendAllLines(file, ReplaceValuesRecursive(inputString, data).Select(i => i.Unescape()));
break;
case FileAction.Copy:
var fileCopyLocation = ReplaceValues(inputString, data);
ThrowIfNotInCWD(fileCopyLocation);
CreatePath(fileCopyLocation);
File.Copy(file, fileCopyLocation);
break;
case FileAction.Move:
var fileMoveLocation = ReplaceValues(inputString, data);
ThrowIfNotInCWD(fileMoveLocation);
CreatePath(fileMoveLocation);
File.Move(file, fileMoveLocation);
break;
}
data.Log(new LogEntry($"Executed action {fileAction} on file {file}", isCapture ? Colors.Tomato : Colors.Yellow));
break;
case UtilityGroup.Folder:
var folder = ReplaceValues(folderPath, data);
ThrowIfNotInCWD(folder);
switch (folderAction)
{
case FolderAction.Exists:
data.Variables.Set(new CVar(variableName, Directory.Exists(folder).ToString(), isCapture));
break;
case FolderAction.Create:
data.Variables.Set(new CVar(variableName, Directory.CreateDirectory(folder).ToString(), isCapture));
break;
}
data.Log(new LogEntry($"Executed action {folderAction} on folder {folder}", isCapture ? Colors.Tomato : Colors.Yellow));
break;
default:
break;
}
}
catch(Exception ex) { data.Log(new LogEntry(ex.Message, Colors.Tomato)); }
}
private void ThrowIfNotInCWD(string path)
{
if (!path.IsSubPathOf(Directory.GetCurrentDirectory()))
{
throw new UnauthorizedAccessException("For security reasons, you cannot interact with paths outside of the current working directory");
}
}
private void CreatePath(string file)
{
if (!Directory.Exists(Path.GetDirectoryName(file)))
{
Directory.CreateDirectory(Path.GetDirectoryName(file));
}
}
}
}
+60
View File
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
@@ -56,5 +57,64 @@ namespace RuriLib
.Replace(@"\\t", @"\t")
.ToString();
}
/// <summary>
/// Returns true if <paramref name="path"/> starts with the path <paramref name="baseDirPath"/>.
/// The comparison is case-insensitive, handles / and \ slashes as folder separators and
/// only matches if the base dir folder name is matched exactly ("c:\foobar\file.txt" is not a sub path of "c:\foo").
/// </summary>
public static bool IsSubPathOf(this string path, string baseDirPath)
{
string normalizedPath = Path.GetFullPath(path.Replace('/', '\\')
.WithEnding("\\"));
string normalizedBaseDirPath = Path.GetFullPath(baseDirPath.Replace('/', '\\')
.WithEnding("\\"));
return normalizedPath.StartsWith(normalizedBaseDirPath, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Returns <paramref name="str"/> with the minimal concatenation of <paramref name="ending"/> (starting from end) that
/// results in satisfying .EndsWith(ending).
/// </summary>
/// <example>"hel".WithEnding("llo") returns "hello", which is the result of "hel" + "lo".</example>
public static string WithEnding(this string str, string ending)
{
if (str == null)
return ending;
string result = str;
// Right() is 1-indexed, so include these cases
// * Append no characters
// * Append up to N characters, where N is ending length
for (int i = 0; i <= ending.Length; i++)
{
string tmp = result + ending.Right(i);
if (tmp.EndsWith(ending))
return tmp;
}
return result;
}
/// <summary>Gets the rightmost <paramref name="length" /> characters from a string.</summary>
/// <param name="value">The string to retrieve the substring from. Must not be null.</param>
/// <param name="length">The number of characters to retrieve.</param>
/// <returns>The substring.</returns>
public static string Right(this string value, int length)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
if (length < 0)
{
throw new ArgumentOutOfRangeException("length", length, "Length is less than zero");
}
return (length < value.Length) ? value.Substring(value.Length - length) : value;
}
}
}