The official Guidance EnScript course uses "Progressive study" examples to show how to build an EnScript that does a specific action. Rather than just showing you a finished EnScript and the code, the idea is to start with the simple "skeleton" or "shell", then build on that piece by piece until it does what you want. In this post, I will follow that same idea and explain an EnScript request I received and then progressively write an EnScript to fit the request.
If you have read any of the previous tutorials I have posted, then you already know the basic principles and syntax, so I will skip those formalities. If you have not read them, then I suggest you click on the tutorial links at the top of the page to learn the basic syntax.
The EnScript request I received requested the following:
Create an EnScript that exports selected files to a export folder with sequential numeric prefix. The EnScript should take all selected files, regardless of where they are in the original image and put them all in one simple folder. The sequential numeric prefix is simply to avoid two files with the same name from overwriting each other. Lastly, create a CSV log that records the original path, MAC dates, extension, logical size and if it is deleted.
Here is the basic skeleton:
class MainClass {
void Main(CaseClass c) {
}
}
We obviously need to recurse or process through each entry in the case, so we will use a simple recursion function:
class MainClass {
void Main(CaseClass c) {
forall(EntryClass entry in c.EntryRoot()){
}
}
}
Next, we will need to check to see which objects the user has selected (blue checked):
class MainClass {
void Main(CaseClass c) {
forall(EntryClass entry in c.EntryRoot()){
if (entry.IsSelected()){
}
}
}
}
Now we have a basic skeleton to start processing each file that the user has selected. Next, we will need to do some file I/O, so that means we will need to deal with the FileClass objects. We need to create at least three different variables of the FileClass type. One for the entry object we need to open and read, then the second to create a file ont he local file system to write the file the user wants exported, and the third is another local file that will contain our log.
class MainClass {
void Main(CaseClass c) {
EntryFileClass file();
LocalFileClass local(), log();
forall(EntryClass entry in c.EntryRoot()){
if (entry.IsSelected()){
}
}
}
}
Now we need to create a folder inside the default case folder to put the exported files into. This requires another variable of the ConnecitonClass type.
class MainClass {
void Main(CaseClass c) {
EntryFileClass file();
LocalFileClass local(), log();
ConnectionClass conn = LocalMachine;
conn.CreateFolder(c.ExportFolder() + "\\Exported Files");
forall(EntryClass entry in c.EntryRoot()){
if (entry.IsSelected()){
}
}
}
}
This allows us to create a folder inside the default export folder that we will use to put the exported files into. Next we will open the files that the user has selected for reading and then export the file and contents to the local file system, into the folder we just created:
class MainClass {
void Main(CaseClass c) {
EntryFileClass file();
LocalFileClass local(), log();
ConnectionClass conn = LocalMachine;
conn.CreateFolder(c.ExportFolder() + "\\Exported Files");
forall(EntryClass entry in c.EntryRoot()){
if (entry.IsSelected()){
file.Open(entry);
local.Open(c.ExportFolder() + "\\Exported Files\\" + entry.Name(), FileClass::WRITE);
local.WriteBuffer(file);
}
}
}
}
Now we have added three lines that first opens the file that the user selected for reading, then opens a file ont he local file system, in the export folder, then writes the contents of the selected file into the file we created on the local file system. The only problem with this approach is in the case when two files exist with the same name, but in different paths in the original image. When they are exported into the same export folder they will overwrite each other. Therefore, we need to prepend a numeric counter as a prefix to each file that is exported.
class MainClass {
void Main(CaseClass c) {
EntryFileClass file();
LocalFileClass local(), log();
ConnectionClass conn = LocalMachine;
conn.CreateFolder(c.ExportFolder() + "\\Exported Files");
uint mastercounter;
forall(EntryClass entry in c.EntryRoot()){
if (entry.IsSelected()){
file.Open(entry);
mastercounter++;
local.Open(c.ExportFolder() + "\\Exported Files\\" + mastercounter + " - " + entry.Name(),
FileClass::WRITE);
local.WriteBuffer(file);
}
}
}
}
Take note that the "local.Open" statement has now been lengthened to where it wraps to a new line in this blog posting. It would normally all be on the same line, terminated with a semicolon, but this blog automatically wraps long lines.
Now we have an EnScript that exports selected files to our default export folder and prepends a numeric prefix to each file. We are almost done. We just need to create a log with the associated metadata. To do this, we need to create another file in the local export folder.
class MainClass {
void Main(CaseClass c) {
EntryFileClass file();
LocalFileClass local(), log();
ConnectionClass conn = LocalMachine;
conn.CreateFolder(c.ExportFolder() + "\\Exported Files");
uint mastercounter;
log.Open(c.ExportFolder() + "\\Exported Files\\log.csv", FileClass::WRITE);
log.WriteLine("Full_Path,Export_Name,Extension,Created_Date,Last_Written,Last_Accessed," + "Logical_Size,Deleted");
forall(EntryClass entry in c.EntryRoot()){
if (entry.IsSelected()){
file.Open(entry);
mastercounter++;
local.Open(c.ExportFolder() + "\\Exported Files\\" + mastercounter + " - " + entry.Name(), FileClass::WRITE);
local.WriteBuffer(file);
}
}
}
}
Again, take note that the local.Open and log.WriteLine statements above have wrapped on this blog entry. They will work if you copied and pasted them in their wrapped form, but it does not make for very readable code.
Finally, we just need to write the metadata for each file we export into the log file:
class MainClass {
void Main(CaseClass c) {
EntryFileClass file();
LocalFileClass local(), log();
ConnectionClass conn = LocalMachine;
conn.CreateFolder(c.ExportFolder() + "\\Exported Files");
uint mastercounter;
log.Open(c.ExportFolder() + "\\Exported Files\\log.csv", FileClass::WRITE);
log.WriteLine("Full_Path,Export_Name,Extension,Created_Date,Last_Written,Last_Accessed,Logical_Size,Deleted");
forall(EntryClass entry in c.EntryRoot()){
if (entry.IsSelected()){
file.Open(entry);
mastercounter++;
local.Open(c.ExportFolder() + "\\Exported Files\\" + mastercounter + " - " + entry.Name(), FileClass::WRITE);
local.WriteBuffer(file);
log.WriteLine(entry.FullPath() + "," + mastercounter + " - " + entry.Name() + "," + entry.Extension() +
"," + entry.Created().GetString() + "," + entry.Written().GetString() + "," +
entry.Accessed().GetString() + "," + entry.LogicalSize() + "," + entry.IsDeleted() + ",");
}
}
}
}
Notice that I have also recorded the new name of the file in the export folder, including the sequential counter for each file we export, that way if two files were named the same, but in different original paths, the reviewer would have the ability to correlate exactly which file is which and which metadata in the log belongs to which file in the export folder.
A completed and functioning version of this EnScript can be
downloaded from here. This version has some added error checking that is not discussed above, but it very easy to understand.