diff --git a/BasicNodes/File/WriteText.cs b/BasicNodes/File/WriteText.cs
new file mode 100644
index 00000000..4e0f4b65
--- /dev/null
+++ b/BasicNodes/File/WriteText.cs
@@ -0,0 +1,102 @@
+using System.ComponentModel.DataAnnotations;
+using FileFlows.BasicNodes;
+using FileFlows.Plugin;
+using FileFlows.Plugin.Attributes;
+
+namespace BasicNodes.File;
+
+///
+/// A flow element that writes text to a file
+///
+public class WriteText : Node
+{
+ ///
+ public override int Inputs => 1;
+
+ ///
+ public override int Outputs => 1;
+
+ ///
+ public override FlowElementType Type => FlowElementType.Process;
+
+ ///
+ public override string Icon => "fas fa-file-signature";
+
+ ///
+ public override string Group => "File";
+
+ ///
+ public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/write-text";
+
+ ///
+ /// Gets or sets the output text location
+ ///
+ [TextVariable(1)]
+ [Required]
+ public string File { get; set; }
+
+ ///
+ /// Gets or sets the text to write, if blank writes the current file
+ ///
+ [TextVariable(2)]
+ public string Text { get; set; }
+
+ ///
+ public override int Execute(NodeParameters args)
+ {
+ string file = args.ReplaceVariables(File, stripMissing: true);
+ if (string.IsNullOrWhiteSpace(file))
+ {
+ args.FailureReason = "No file set";
+ args.Logger?.ELog(args.FailureReason);
+ return -1;
+ }
+
+ string text = GetText(args, Text, file);
+
+ args.Logger?.ILog($"Text: {text}");
+ args.Logger?.ILog($"File: {file}");
+
+ var result = args.FileService.FileAppendAllText(file, text);
+ if (result.Failed(out var error))
+ {
+ args.FailureReason = "File writing file: " + error;
+ args.Logger.ELog(args.FailureReason);
+ return -1;
+ }
+
+ args.Logger?.ILog($"Text written to file");
+ return 1;
+ }
+
+ ///
+ /// Gets the text to write
+ ///
+ /// the node parameters
+ /// the original text
+ /// the file to write to
+ /// the text to write
+ public static string GetText(NodeParameters args, string text, string outputFile)
+ {
+ if (outputFile?.ToLowerInvariant().EndsWith(".csv") == true)
+ {
+ var parts = text?.Split([";", ","], StringSplitOptions.RemoveEmptyEntries) ?? [];
+ if (parts.Length == 0)
+ {
+ return CsvEncode(args.WorkingFile);
+ }
+
+ return string.Join(",", parts.Select(x => CsvEncode(args.ReplaceVariables(x, stripMissing: true))));
+ }
+
+ return args.ReplaceVariables(text, stripMissing: true)?.EmptyAsNull() ?? args.WorkingFile;
+ }
+
+ ///
+ /// CSV encodes a string
+ ///
+ /// the text to encode
+ /// the CSV encoded string
+ private static string CsvEncode(string text)
+ => $"\"{text.Replace("\"", "\"\"")}\"";
+}
\ No newline at end of file
diff --git a/BasicNodes/Tests/WriteTextTests.cs b/BasicNodes/Tests/WriteTextTests.cs
new file mode 100644
index 00000000..48c97465
--- /dev/null
+++ b/BasicNodes/Tests/WriteTextTests.cs
@@ -0,0 +1,52 @@
+#if(DEBUG)
+
+using BasicNodes.File;
+
+namespace BasicNodes.Tests;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+[TestClass]
+public class WriteTextTests : TestBase
+{
+ [TestMethod]
+ public void WorkingFile_Csv()
+ {
+ var args = new FileFlows.Plugin.NodeParameters(@"/test/file.mkv", new TestLogger(), false, string.Empty, new LocalFileService());
+
+ var output = WriteText.GetText(args, "", "file.csv");
+ Assert.AreEqual("\"/test/file.mkv\"", output);
+ }
+
+ [TestMethod]
+ public void WorkingFile_Text()
+ {
+ var args = new FileFlows.Plugin.NodeParameters(@"/test/file.mkv", new TestLogger(), false, string.Empty, new LocalFileService());
+
+ var output = WriteText.GetText(args, "", "file.txt");
+ Assert.AreEqual("/test/file.mkv", output);
+ }
+
+ [TestMethod]
+ public void CsvArgs()
+ {
+ var args = new FileFlows.Plugin.NodeParameters(@"/test/file.mkv", new TestLogger(), false, string.Empty, new LocalFileService());
+ args.Variables["file.Name"] = "file.mkv";
+ args.Variables["ext"] = "mkv";
+
+ var output = WriteText.GetText(args, "{file.Name};{ext}", "file.csv");
+ Assert.AreEqual("\"file.mkv\",\"mkv\"", output);
+ }
+
+ [TestMethod]
+ public void CsvArg()
+ {
+ var args = new FileFlows.Plugin.NodeParameters(@"/test/file.mkv", new TestLogger(), false, string.Empty, new LocalFileService());
+ args.Variables["file.Name"] = "file.mkv";
+
+ var output = WriteText.GetText(args, "{file.Name}", "file.csv");
+ Assert.AreEqual("\"file.mkv\"", output);
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/BasicNodes/i18n/en.json b/BasicNodes/i18n/en.json
index 35b9ccb7..cfbab138 100644
--- a/BasicNodes/i18n/en.json
+++ b/BasicNodes/i18n/en.json
@@ -501,6 +501,18 @@
"Options": "Options",
"Options-Help": "The string to match against."
}
+ },
+ "WriteText": {
+ "Description": "Writes text to a file",
+ "Outputs": {
+ "1": "Text written to file"
+ },
+ "Fields": {
+ "File": "File",
+ "File-Help": "The file to write the text to.",
+ "Text": "Text",
+ "Text-Help": "The text to write to the file. If left blank the current working file full path will be written."
+ }
}
}
}